Можно ли решить проблему остановки, если у вас есть ограниченный или предсказуемый ввод?


18

Проблема остановки не может быть решена в общем случае. Можно придумать определенные правила, которые ограничивают разрешенные входы, и можно ли решить проблему остановки для этого особого случая?

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

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

Ответы:


10

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

Рекурсия

Общим ограничением в языках сценариев является динамическое предотвращение рекурсии: если A вызывает B, вызывает C, вызывает ... вызывает A, то интерпретатор (или проверка, в вашем случае) сдается или сигнализирует об ошибке, даже если рекурсия может фактически завершиться. Два конкретных примера:

  • Препроцессор C оставляет макрос без изменений, пока расширяет этот макрос. Чаще всего используется для определения оболочки вокруг функции:

    #define f(x) (printf("calling f(%d)\n", (x)), f(x))
    f(3);
    

    Это расширяется до

    (printf("calling f(%d)\n", (3)), f(3))
    

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

    #define f0(x) x(x)x(x)
    #define f1(x) f0(f0(x))
    #define f2(x) f1(f1(x))
    #define f3(x) f2(f2(x))
    f3(x)
    
  • Оболочки Unix рекурсивно расширяют псевдонимы, но только до тех пор, пока не встретятся псевдонимы, которые уже расширяются. Опять же, основная цель состоит в том, чтобы определить псевдоним для команды с аналогичным именем.

    alias ls='ls --color'
    alias ll='ls -l'
    

NN

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

Loops

forмN

В частности, с помощью циклов for (плюс разумные языковые конструкции, такие как условные выражения) вы можете написать все примитивные рекурсивные функции и наоборот. Вы можете распознать примитивно-рекурсивные функции синтаксически (если они написаны беспристрастно), потому что они не используют цикл while, goto, recursion или другой трюк. Примитивно-рекурсивные функции гарантированно завершаются, и большинство практических задач не выходят за рамки примитивной рекурсии.


4

См. Терминатор и утверждение . Они склонны полагаться на эвристику, и я не уверен, что они четко описывают класс программ, для которых они работают. Тем не менее, они рассматриваются как современные, поэтому они должны быть хорошей отправной точкой для вас.


4

Да, это возможно. Одним из распространенных способов решения таких проблем является рассмотрение дополнительного (монотонного) невычислимого параметра в зависимости от кода как части ввода. Сложность проблемы с этим параметром может быть значительно уменьшена.

Мы не можем вычислить параметр, но если вы знаете, что входные экземпляры, с которыми вы имеете дело, имеют небольшие значения параметров, вы можете зафиксировать его в небольшом числе и использовать алгоритм.

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

Что касается другого вопроса, если вы достаточно ограничите входные данные, тогда проблема остановки может быть легко решена. Например, если вы знаете, что входные данные являются алгоритмами полиномиального времени, решение проблемы остановки для них тривиально (поскольку каждый алгоритм полиномиального времени останавливается).

Проблемы, которые возникают в формальных методах, обычно неразрешимы, вы можете проверить литературу о том, как они решают эти проблемы на практике.


4

Не формально жесткий ответ, но здесь он идет:

Проблема в определении, останавливается ли он или зацикливается навсегда. Цикл на конечных коллекциях по одному элементу за раз или между интервалами чисел - это нормально. РЕДАКТИРОВАТЬ: Очевидно, это будет работать, только если итеративная коллекция или интервал запрещено изменять (например, неизменяемость), когда он повторяется (или, по крайней мере, запрещено расти).

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

Произвольные goto вообще плохие. Обратно-gotos очень вероятно приведут к петлям, которые могут быть бесконечными.

Операции Whiles и do-whiles представляют собой проблему, поскольку они зависят от условия, которое не гарантируется изменить или нет во время выполнения. Возможный (но, вероятно, очень неудовлетворительный) способ ограничения, который состоит в том, чтобы дать максимальное количество возможных итераций.


2

Вам нужно дать определение вашего языка сценариев, и что вы подразумеваете под «ожидать» от авторов сценариев.

О(Nω)

Есть аналогичный результат для класса полиномиальных программ Аарона Р. Брэдли, Зохара Манны и Хенни Б. Сипмы. Но AFAIK (я могу ошибаться здесь), время выполнения вдвойне экспоненциально (по сути, время, необходимое для вычисления базиса Гребнера).

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.