Что именно является проблемой остановки? - PullRequest
52 голосов
/ 10 июля 2009

Когда люди спрашивают о проблеме остановки, связанной с программированием, люди отвечают: «Если вы просто добавите один цикл, у вас есть программа остановки, и поэтому вы не можете автоматизировать task »

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

Но кое-что из этого кажется нелогичным. Что, если бы я писал программу решения проблем, которая принимает исходный код. rascher@localhost$ ./haltingSolver source.c

Если мой код (source.c) выглядит так:

for (;;) {  /* infinite loop */  }

Кажется, моей программе было бы довольно легко это увидеть. «Посмотрите цикл и посмотрите на условие. Если условие основано только на литералах, а не на переменных, то вы всегда знаете результат цикла. Если есть переменные (например, while (x <10)), посмотрите, эти переменные когда-либо изменяются. Если нет, то вы всегда знаете результат цикла. "</p>

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

int x = 0
while (x < 10) {}

может быть обнаружено. вместе с - хотя и не тривиально:

int x = 0
while (x < 10)
{
   x++;
   if (x == 10)
   {
      x = 0
   }
}

А как насчет ввода пользователя? Это кикер, это то, что делает программу непредсказуемой.

int x = 0;
while (x < 10) 
{
   scanf("%d", &x); /* ignoring infinite scanf loop oddities */
}

Теперь моя программа может сказать: «Если пользователь введет 10 или больше, программа остановится. На всех других входах она снова зациклится».

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

Так в чем именно проблема остановки? Что я не понимаю в идее, что мы не можем написать задачу для обнаружения бесконечных циклов? Или почему "петли" такой часто цитируемый пример?

UPDATE

Итак, позвольте мне немного изменить вопрос: в чем заключается проблема с остановкой применительно к компьютерам? А потом я отвечу на некоторые комментарии:

Многие люди говорили, что программа должна быть в состоянии справиться с «любым произвольным вводом». Но в компьютерах нет никакого произвольного ввода. Если я введу только один байт данных, то у меня будет только 2 ^ 8 возможных входов. Итак, в качестве примера:

int c = getchar()

switch (c) {
   case 'q':
      /* quit the program */
}

Внезапно я только что учел все возможности. Если c имеет битовую комбинацию 0x71, это делает одно. Для всех других шаблонов он делает что-то еще. Даже программа, которая принимает произвольный ввод строки, никогда не бывает действительно «произвольной», поскольку ресурсы конечны, что означает, что, хотя теория «произвольной» применима ... она не совсем однозначна с практикой.

Другой пример, на который ссылаются люди:

while (n != 1)
    if (n & 1 == 1) 
        n = 3 * n + 1;
    else 
        n /= 2;

Если n - 32-разрядное целое число ... тогда я могу визуально сказать вам, остановится ли это.

Полагаю, это редактирование ни о чем не спрашивает, но самый убедительный пример, который я видел, это этот :

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

public bool DeterminesHalt(string filename, string[] args){
    //runs whatever program you tell it do, passing any args
    //returns true if the program halts, false if it doesn't
}

Теперь допустим, что мы пишем небольшой фрагмент кода, такой как ...

public static void Main(string[] args){
    string filename = Console.ReadLine(); //read in file to run from user
    if(DeterminesHalt(filename, args))
        for(;;);
    else
        return;
}

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

Опять же, если вы намеренно напишите программу, которая содержит бесконечный цикл ... "решение проблемы остановки" - это спорный вопрос, не так ли?

Ответы [ 22 ]

51 голосов
/ 10 июля 2009

РЕДАКТИРОВАТЬ (намного позже, чем оригинальный ответ): MarkCC из Good Math, Bad Math недавно написал превосходное обсуждение проблемы остановки с конкретными примерами.

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

Другими словами, вы можете написать программа называется остановка оракула, HaltingOracle (программа, ввод), который возвращает true, если программа (входная) в конечном итоге остановить, и который возвращает ложь, если это не так?

Ответ: нет, вы не можете.

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

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

Cue manager voice : "Хо-хо, эти глупые пользователи, давайте удостоверимся, что независимо от того, какой мусор они печатают, наши серверные задачи никогда не окажутся в бесконечном цикле. ! "

Это похоже на отличную идею, верно? Вы не хотите, чтобы ваш сервер зависал, верно?

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

Марк использует код вместо ввода для иллюстрации проблемы:

def Deciever(i):
  oracle = i[0]
  in = i[1]
  if oracle(Deceiver, i):
    while True:
      continue
  else:
    return i

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

Итак, вход в Deceiver на самом деле список из двух элементов: первый является предложенным останавливающим оракулом. второй это еще один вход. Что за Остановить убийцу - спросить у Оракула: «Как вы думаете, я остановлюсь для ввода я?». Если оракул говорит: «Да, вы остановка », то программа переходит в бесконечный цикл. Если оракул говорит: «Нет, ты не остановишься », а затем остановится. Так нет Что бы ни говорил оракул, это неправильно.

Говоря иначе, без мошенничества, переформатирования входных данных, счетных / неисчисляемых бесконечностей или чего-либо еще отвлекающего, Марк написал фрагмент кода, который может победить любую программу остановки оракула. Вы не можете написать oracle, который отвечает на вопрос, останавливается ли Deceiver когда-либо.

Оригинальный ответ:

Из великого Википедия :

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

Алан Тьюринг доказал в 1936 году, что Общий алгоритм решения остановки проблема для всех возможных программ ввода пары не могут существовать. Мы говорим, что проблема остановки неразрешима Машины Тьюринга. Copeland (2004) приписывает фактический срок остановки проблема для Мартина Дэвиса.

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

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

40 голосов
/ 10 июля 2009

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

29 голосов
/ 10 июля 2009

Вот простое объяснение доказательства того, что проблема остановки неразрешима.

Предположим, у вас есть программа H, которая вычисляет, останавливается ли программа. H принимает два параметра, первый - описание программы, P, а второй - вход, I. H возвращает true, если P останавливается на входе I, и false в противном случае.

Теперь напишите программу, p2, которая принимает в качестве ввода описание другой программы, p3. p2 вызывает H (p3, p3), затем зацикливается, если H возвращает true и останавливается в противном случае.

Что происходит, когда мы запускаем p2 (p2)?

Он должен зацикливаться и останавливаться одновременно, вызывая взрыв вселенной.

21 голосов
/ 10 июля 2009

Это было избито до смерти настолько хорошо, что на самом деле есть поэтический доказательство , написанный в стиле Льюис Кэрролл Доктор Сьюз Джеффри Пуллум (он Язык журнала слава).

Забавные вещи. Вот вкус:

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

...

Независимо от того, как P может работать, Q будет черпать его:
Q использует вывод P, чтобы P выглядел глупо.
Что бы P ни говорил, оно не может предсказать Q:
P прав, когда это неправильно, и ложь, когда это правда!

9 голосов
/ 10 июля 2009

В порядке подтверждения Проблема остановки в Википедии.

Чтобы проиллюстрировать, почему недостаточно просто применить некоторую технику к циклам, рассмотрим следующую программу (псевдокод):

int main()
{
  //Unbounded length integer
  Number i = 3;

  while(true)
  {
    //example: GetUniquePositiveDivisiors(6) = [1, 2, 3], ...(5) = 1, ...(10) = 1, 2, 5, etc.
    Number[] divisiors = GetUniquePositiveDivisiors(i);
    Number sum = 0;
    foreach(Number divisor in divisiors) sum += divisor;

    if(sum == i) break;

    i+=2;
  }
}

Можете ли вы придумать подход, который вернет true, если этот код остановится, и false в противном случае?

Думай внимательно .

Если случайно вы серьезно боретесь за медаль Филдса, представьте себе код для этих проблем вместо вышеперечисленных.

7 голосов
/ 10 июля 2009

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

Звучит как кто-то, кто обобщает применение проблемы остановки. Есть множество определенных циклов, которые вы можете доказать, что они прерываются. Существует исследование, которое может выполнять проверку завершения для широких классов программ. Например, в Coq вы ограничены программами, которые вы можете прекратить. У Microsoft есть исследовательский проект под названием Terminator, который использует различные приближения, чтобы доказать, что программы будут прекращены.

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

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

Пример программы, которая может или не может остановиться (в Haskell):

collatz 1 = ()
collatz !n | odd n     = collatz (3 * n + 1)
           | otherwise = collatz (n `div` 2)

или в более доступном:

while (n != 1)
    if (n & 1 == 1) 
        n = 3 * n + 1;
    else 
        n /= 2;

Учитывая каждое целое число> = 1, остановится ли эта программа? Ну, это сработало до сих пор, но нет теоремы, которая говорит, что она остановится для каждого целого числа. У нас есть гипотеза из-за Lothar Collatz , которая датируется 1937 годом, но она не доказана.

5 голосов
/ 10 июля 2009

Отличным примером Тьюринга была самоссылка. Предположим, что есть программа, которая может исследовать другую и определить, остановится она или нет. Вставьте программу проверки остановки самостоятельно, в программу проверки остановки - что она должна делать?

5 голосов
/ 12 июля 2009

В отношении подпункта "люди отвечают" Если вы просто добавите один цикл, у вас есть программа остановки и, следовательно, вы не можете автоматизировать задачу "", я добавлю эту деталь:

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

Дело в том, что не все программы требуют машин Тьюринга. Это программы, которые могут быть вычислены на концептуально «более слабой» машине - например, регулярные выражения могут быть полностью реализованы конечным автоматом, который всегда останавливается при вводе. Разве это не хорошо?

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

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

4 голосов
/ 10 июля 2009

Вот программа, которую никогда не сможет решить проблема остановки.

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

public bool DeterminesHalt(string filename, string[] args){
    //runs whatever program you tell it do, passing any args
    //returns true if the program halts, false if it doesn't
}

Теперь допустим, что мы пишем небольшой фрагмент кода, такой как ...

public static void Main(string[] args){
    string filename = Console.ReadLine(); //read in file to run from user
    if(DeterminesHalt(filename, args))
        for(;;);
    else
        return;
}

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

Независимо от того, сколько проверок ввода вы выполняете, невозможно определить, останавливается ли КАЖДАЯ написанная программа или нет.

3 голосов
/ 10 июля 2009

Много интересных конкретных примеров / аналогий. Если вы хотите углубиться в историю, есть хорошая книга Чарльза Петцольда об оригинальной статье Тьюринга «Аннотированная Тьюринг» .

В смежном, в некотором роде, ключе, в сети есть действительно аккуратное эссе: Кто может назвать большее число? , которое касается машин Тьюринга и функций Аккермана.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...