Подтвердить что ты не робот

Можно ли перечислить компьютерные программы?

Предположим, вам нужно написать программу, которая проверит все программы в поисках того, что завершает конкретную задачу. Например, рассмотрим эту функцию JavaScript:

function find_truth(){
    for(n=0;;++n){
        try {
            var fn = Function(string(n));
            if (fn() == 42)
                return fn;
        } catch() {
            continue;
        }
    }
}

Пока строка (n) возвращает возможную n-ю строку ( "a", "b", "c",... "aa", "ab"...), эта программа в конечном итоге выведет функцию который оценивается как 42. Проблема с этим методом заключается в том, что он перечисляет строки, которые могли или не могли быть действительной программой. Мой вопрос: возможно ли перечислять программы сами? Как?

4b9b3361

Ответ 1

Да, есть способы, которыми это возможно. Несколько месяцев назад я сделал небольшой экспериментальный проект, чтобы сделать что-то вроде этого, который работает достаточно хорошо, учитывая, что он делает. Вы даете ему тип и некоторые тесты для прохождения, и он ищет подходящую функцию, которая проходит тесты. Я никогда не делал работу, чтобы сделать ее зрелой, но вы можете видеть, что она фактически создает функции, которые проходят тесты в случае примеров. Требование типа было важным компонентом для практичности этого поиска - оно резко сократило количество программ, которые необходимо было попробовать.

Важно иметь твердое понимание теории здесь, прежде чем делать утверждения о том, что есть и что не представляется возможным - многие могут перейти к проблеме остановки и сказать вам, что это невозможно, когда правда - это довольно бит более тонкий, чем этот.

  • Вы можете легко сгенерировать все синтаксически действующие программы на определенном языке. Наивно вы можете сгенерировать все строки и отфильтровать те, которые анализируют /typecheck; но есть лучшие способы.
  • Если язык не завершен - например, просто типизированное лямбда-исчисление или даже что-то очень мощное, как Agda, вы можете перечислить все программы, которые генерируют 42, и, учитывая любую программу, вы можете решить, "это порождает 42" или "это не генерирует 42". (Язык, который я использовал в моем экспериментальном проекте, относится к этому классу). Напряжение здесь заключается в том, что на любом таком языке будут выполняться некоторые вычислимые функции, которые вы не можете написать.
  • Даже если язык завершен, вы все равно можете перечислить все программы, которые в итоге генерируют 42 (сделайте это, выполнив их все параллельно и сообщив об успешном завершении).

То, что вы не можете сделать, чтобы закончить полный язык, - это решить, что программа определенно не будет генерировать 42 - она ​​может запускаться навсегда, и вы не сможете сказать, удастся ли ей в конечном итоге добиться успеха, пока это не произойдет. Есть несколько программ, которые вы можете сказать, будет бесконечным циклом, хотя и не все из них. Так что, если у вас есть таблица программ, вы можете ожидать, что ваша программа перечислителя в случае не обязательного полного языка будет выглядеть следующим образом:

Program |  P1  |  P2  |  P3  |  P4  |  P5  |  P6  |  P7  | ...
42?     | No   | No   | No   | Yes  | No   | No   | No   | ...

В то время как на полном языке обучения ваш вывод (в заданное время) будет таким:

Program |  P1  |  P2  |  P3  |  P4  |  P5  |  P6    |  P7  | ...
42?     | No   | No   | Loop | Yes  | No   | Dunno  | No   | ...

И позже, что Dunno может превратиться в Да или Нет, или он может остаться не навсегда - вы просто не знаете.

Все это очень теоретично, и на самом деле генерировать программы на полном языке для поиска тех, кто делает определенную вещь, довольно сложно и занимает много времени. Конечно, вы не хотите делать это один за другим, потому что пространство настолько велико; вы, вероятно, хотите использовать алгоритм генетического поиска или что-то в этом роде.

Еще один тонкий момент в теории: говорить о программах, которые "порождают 42", отсутствует какая-то важная деталь. Обычно, когда мы говорим о создании программ, мы хотим, чтобы он генерировал определенную функцию, а не только выводил что-то конкретное. И именно тогда вещи, которые вы, возможно, захотите сделать, становятся более невозможными. Если у вас есть бесконечное пространство возможных входов - например, ввод одного числа, то

  • Вы не можете вообще сказать, выполняет ли программа заданную функцию. Вы не можете проверить каждый вход вручную, потому что бесконечности слишком много для проверки. Вы не можете найти доказательства того, что ваша функция делает правильную вещь, потому что для любой вычислимой функции f для любой системы аксиом A существуют программы, которые вычисляют f так, что A не имеет доказательств того, что они вычисляют f.
  • Вы не можете определить, будет ли программа давать какой-либо ответ для каждого возможного ввода. Он может работать для первых 400 000 000 из них, но бесконечный цикл на 400 000,001st.
  • Аналогично, вы не можете определить, выполняют ли две программы одну и ту же функцию (т.е. одни и те же входы - одни и те же выходы).

Там у вас это есть, суточная доза феноменологии теории разрешимости.

Если вы заинтересованы в том, чтобы делать это по-настоящему, посмотрите на генетические алгоритмы и поставьте тайм-ауты на функции, которые вы пытаетесь использовать, и/или используете разрешимый (не подлежащий обучению) язык.

Ответ 2

Конечно, можно перечислять все программы на определенном языке, которые являются синтаксически действительными (и на статически типизированном языке, даже те, которые проверяют тип): вы можете просто перечислять все строки, как вы предлагали, анализатор для языка, а затем отклонить те, которые не могут быть проанализированы. Поэтому, если это определение правильной программы, то да, это возможно.

Однако неверно, что ваша программа обязательно выведет функцию, которая в конечном итоге возвращает 42, даже если вы замените string на функцию, которая возвращает только синтаксически действующие программы. Если возвращаемая функция содержит бесконечный цикл, она будет работать вечно, и, следовательно, ваша программа никогда не сможет попробовать другую функцию. Таким образом, вы никогда не сможете достичь функции, которая возвращает 42.

Чтобы избежать этой проблемы, вы можете сказать, что функция string(n) должна генерировать только синтаксически действующий код и не содержать бесконечный цикл (при этом все еще можно вернуть все такие функции). Это, однако, невозможно, потому что это повлечет за собой решение проблемы остановки (что, конечно, неразрешимо).

Ответ 3

Как уже отмечалось, вы можете тривиально превратить программу "сгенерировать все строки" в "сгенерировать все действующие программы на языке X", подключив парсер/компилятор для языка X. Обычно, если вы можете написать программу, которая принимает текст как ввод и возвращает true/false, указывающий, является ли текст действительной программой, затем вы можете использовать его как фильтр в программе "сгенерировать все строки".

Вы также можете разработать язык программирования, в котором каждая строка символов является допустимой программой (perl springs to mind).

Вероятнее всего, более интересно то, что с учетом формальной грамматики для языка вы можете использовать это для генерации программ на языке, а не для их разбора. Вам просто нужно сделать первый шаг по грамматике, чтобы быть уверенным, что каждая программа с конечной длиной будет достигнута за какое-то конечное время (если вы пройдете первый шаг по глубине, вы будете изучать все программы, состоящие исключительно из переменная, чье имя является более длинной последовательностью "A" или что-то в этом роде.

Большинство грамматик, фактически используемых при разборе языков программирования, не будут работать непосредственно для этого; они обычно немного чрезмерно разрешительны. Например, грамматика может определять идентификаторы как все, что соответствует регулярному выражению [_A-Za-z][0-9_A-Za-z]*, что позволяет именам переменных неограниченной длины, но многие языковые реализации фактически задушат программы с именами переменных длиной в гигабайт. Но вы могли бы в принципе узнать обо всех этих типах ошибок и написать перечислимую грамматику, которая точно охватывает все действующие программы на интересующем вас языке.


Таким образом, вы можете перечислить все программы. Это на самом деле не достаточно, чтобы позволить вам запустить вашу программу find_truth и найти программу, которая возвращает 42, хотя, потому что она будет бесконечно оценивать первую программу, которая содержит бесконечный цикл.

Но это все еще возможно сделать! Вам просто нужно выбрать порядок, чтобы исследовать все возможности, чтобы в конечном итоге все было достигнуто за какое-то конечное время. У вас есть два бесконечных "измерения" для поиска; перечисление всех программ и глубину оценки каждой программы. Вы можете убедиться, что вы покрываете все базы, делая что-то вроде следующей стратегии:

  • Запустите все программы длиной до 1 на 1 шаг
  • Запустите все программы длиной до 2 для двух шагов
  • Запустите все программы длиной до 3 для трех шагов
  • ...

И так далее. Это гарантирует, что независимо от длины программы и количества "шагов", вы в конечном итоге ударите их, не выполняя бесконечную работу "сначала" (пока программа с вашим желаемым результатом фактически существует).

Если у вас есть неограниченное хранилище, вы можете избежать повторения работы между этими этапами (вы сохраняете все программы длины N, которые еще не закончили N шагов, вместе с их состоянием, а затем в следующем раунде вы запускаете новые программы до N + 1 шагов и запускать все "ожидающие" программы для еще одного шага каждый). Определение "шаг" не имеет большого значения, если оно не допускает бесконечных циклов. Некоторое конечное число байт-кодов, инструкций процессора или даже секунд; вам, конечно же, потребуется ограниченная реализация языка.

Ответ 4

Предполагая, что я правильно интерпретирую вашу фразу "возможно ли перечислять программы сами?" Затем Да вы можете перечислять программы. Это, по сути, проблема генетического программирования. См.:

http://en.wikipedia.org/wiki/Genetic_programming

Здесь сами программы создаются, запускаются и оцениваются (с полученным значением пригодности, где оптимальное value = 42), так же, как и с помощью генетических алгоритмов, так что да, это обеспечит ваше перечисление. Кроме того, в течение нескольких поколений вы могли бы разработать программу для создания 42.

Ответ 5

Это невозможно. Проблема в том, что некоторые программы могут занять много времени для завершения вычислений. И некоторые программы могут застревать в бесконечном цикле. В идеале вы хотели бы прекратить выполнение тех программ, которые застряли в бесконечных циклах, и сохранить только длинные программы. Вы могли бы реализовать таймер, но что, если у вас была программа, которая работала дольше, чем таймер, но все же вернула бы правильный ответ?

В общем, единственный способ проверить, заканчивается ли компьютерная программа, - запустить его и увидеть, рискуя войти в бесконечный цикл. Конечно, вы могли бы реализовать некоторые эвристики, чтобы распознавать общие формы бесконечных циклов, но в целом это невозможно. Это называется проблема с остановкой.

EDIT:

Я понимаю, что я лишь частично отвечаю на ваш вопрос. Вы спрашиваете, можно ли перечислять сами программы. Это, безусловно, возможно. У вас уже есть способ генерировать все возможные строки по порядку. Затем вы можете просто увидеть, какие строки правильно обрабатываются как javascript-программа, и просто сохраняйте их.