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

Что такое проблема с остановкой?

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

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

Но некоторые из них кажутся противоположными интуитивными. Что делать, если я пишу решатель проблемы с остановкой, в качестве исходного кода которого используется исходный код. [email protected]$ ./haltingSolver source.c

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

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

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

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

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;
}

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

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

4b9b3361

Ответ 1

EDIT (намного позже оригинального ответа): MarkCC Good Math, Bad Math недавно написал отличное обсуждение проблемы с остановкой с конкретными примерами.

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

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

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

Последующие вопросы о том, является ли входной сигнал проблемы с остановкой релевантной или красной селедкой: Да, вход очень важен. Кроме того, кажется, есть некоторая путаница в том, что я вижу, что "бесконечный" используется там, где "произвольный" является более правильным.

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

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

Это кажется отличной идеей, верно? Вы не хотите, чтобы ваш сервер зависал, правильно?

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

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

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

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

Итак, вход в Deceiver на самом деле список из двух элементов: первый является предлагаемым оракулом. второй - другой вход. Что за остановить убийцу - это спросить Oracle: "Как вы думаете, я остановлюсь для ввода i?". Если оракул говорит: "Да, ты halt", то программа переходит в бесконечная петля. Если оракул говорит: "Нет, вы не останавливаетесь", тогда он останавливается. что говорит оракул, его неправильно.

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

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

Из большой Wikipedia:

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

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

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

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

Ответ 2

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

Ответ 3

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

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

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

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

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

Ответ 4

Это настолько избито до смерти, что на самом деле существует поэтическое доказательство, написанное в стиле Льюис Кэрроллудаp > Д-р Сьюз Джеффри Пуллум (он Знание журнала).

Смешные вещи. Здесь вкус:

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

...

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

Ответ 5

Там подтверждено OK проблема с остановкой в википедии.

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

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 в противном случае?

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

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

Ответ 6

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

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

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

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

Пример программы, которая может или не может быть остановлена ​​(в 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 годом, но она не содержит доказательств.

Ответ 7

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

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

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

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

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

Ответ 8

Хороший пример Тьюринга был самореферентным - предположим, что есть программа, которая может изучить другую и определить, остановится ли она. Подайте программу проверки-остановки программы ITSELF в программу проверки остановки - что делать?

Ответ 9

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

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

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;
}

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

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

Ответ 10

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

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

Ответ 11

Уже есть много хороших ответов, но я не видел, чтобы кто-то обращал внимание на тот факт, что в каком-то выборочном смешении теории и практичности проблема с остановкой действительно разрешима.

Итак, в первую очередь проблема с остановкой в ​​основном заключается в написании программы, которая принимает любую произвольную вторую программу и определяет, будет ли вторичная программа останавливаться на произвольном входе. Поэтому вы говорите: "Да, эта программа остановится на этом входе" или "Нет, это не будет". И на самом деле это неразрешимо в общем случае (другие люди, похоже, уже доказали это) на машине Тьюринга. Реальная проблема заключается в том, что вы можете узнать, остановится ли что-то, запустив ее (просто подождите, пока она не остановится), но вы не можете действительно узнать, не остановится ли что-то, запустив ее (вы просто продолжайте ждать всегда).

Это проблема на машине Тьюринга, которая по определению имеет бесконечное количество памяти и, следовательно, бесконечно много состояний. Однако наши компьютеры имеют ограниченный объем памяти. На компьютере так много бит. Поэтому, если вы можете каким-то образом отслеживать все предыдущие состояния (конфигурации бит), которые вы видели во время запуска программы, вы можете гарантировать, что ваша контрольная панель никогда не войдет в бесконечный цикл. Если вторичная программа в конечном итоге останавливается, вы говорите: "Да, эта программа остановится на этом вводе". Если вы видите одну и ту же конфигурацию бит дважды, прежде чем она остановится, вы знаете "Нет, это не будет". Наверное, не имеет большого технического значения, но хорошо знать, что много раз "тяжелые" проблемы, с которыми мы сталкиваемся, сложнее в теории, чем на практике.

Ответ 13

Вы указали несколько простых случаев.

Теперь подумайте о всех остальных случаях.

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

Если вы, конечно, не можете его обобщить.

Вот где возникает проблема с остановкой. Как вы ее обобщили?

Ответ 16

От Программирование жемчуга, Джон Бентли

4.6 Проблемы

5. Докажите, что эта программа завершается, когда ее вход x является положительным целым.

while x != 1 do
    if even(x)
        x = x/2
    else
        x = 3*x +1

Ответ 17

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

Теперь дайте свой алгоритм сам проверить.

Ответ 18

Точное определение проблемы заключается в том, что вам нужно написать программу, которая выполняет следующие действия: - принимает произвольную программу - определяет, приостанавливает ли программа заданный произвольный конечный ввод в программу

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

статья BBC h2g2 о проблеме остановки

Если вы действительно решили проблему с остановкой, вы работаете на таких сайтах, как rentacoder.com. Несколько месяцев назад на одном из них появилась почта от пользователя по имени ATuring, который предложил контракт для решения проблемы с остановкой.:)

Ответ 19

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

Ответ 20

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

f(n) is odd  -  f(n+1) = 3*f(n)+1
f(n) is even -  f(n+1) = f(n)/2

В настоящее время предполагается, что все начальные точки в конечном итоге достигнут 1, а затем повторите 4,2,1,4,2,1,4,2,1... Однако для этого нет доказательств. Поэтому прямо сейчас единственный способ определить, заканчивается ли число при подаче в последовательность градиента, - это фактически вычислить его до тех пор, пока вы не достигнете 1.

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

Ответ 21

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

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