什么是停顿问题?

时间:2009-07-10 18:18:42

标签: computer-science halting-problem

每当人们询问与编程有关的暂停问题时,人们会回答“如果你只是添加一个循环,那么你就有了停止程序,因此无法自动执行任务

有道理。如果你的程序有一个无限循环,那么当你的程序运行时,你无法知道程序是否仍在处理输入,或者它是否只是无限循环。

但其中一些似乎与直觉相反。如果我正在编写一个暂停问题求解器,它将源代码作为输入,那该怎么办? rascher@localhost$ ./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或更高,程序将停止。在所有其他输入上,它将再次循环。”

这意味着,即使有数百个输入,一个也应能够列出程序停止的条件。实际上,当我编写程序时,我总是确保有人能够终止它!我并不是说结果条件列表是琐碎来创建,但对我来说似乎并不可能。您可以从用户那里获取输入,使用它们来计算指针索引等等 - 但这只会增加条件的数量以确保程序终止,不会使它们无法枚举。

那么停止问题到底是什么?我不能理解我们不能写一个问题来检测无限循环的想法?或者,为什么“循环”这样一个经常被引用的例子?

更新

所以,让我稍微改变一下这个问题:什么是停止问题适用于计算机?然后我会回复一些评论:

很多人都说程序必须能够处理“任意输入”。但在计算机中,从来没有任何输入。如果我只输入一个字节的数据,那么我只有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位整数...那么我可以直观地告诉你这是否会停止。

我想这个编辑不是在问什么,但我见过的最有说服力的例子是this one

假设您有神奇的程序/方法来确定程序停止。

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

因此,对于这个例子,我们可以编写一个程序,与我们神奇的暂停方法完全相反。如果我们以某种方式确定给定的程序将停止,我们只是跳进一个无限循环;否则,如果我们确定程序处于无限循环中,我们就会结束程序。

然后再次,如果你故意写一个包含无限循环的程序......“解决停止问题”有点没有实际意义,不是吗?

25 个答案:

答案 0 :(得分:51)

编辑(比原始答案晚得多):Good Math, Bad Math的MarkCC最近用具体的例子写了一个excellent discussion停顿问题。

  

暂停问题基本上是一个问题   正式询问你是否能说出来   是否是任意程序   最终会停止。

     

换句话说,你能写一个   程序称为停止oracle,   HaltingOracle(程序,输入),其中   如果程序(输入)会返回true   最终停止,并返回   如果它不会错误?

     

答案是:不,你不能。

跟进有关停止问题的输入是否相关或红鲱鱼的问题:是的,输入很重要。此外,似乎有一些混乱,因为我看到“无限”被用于“任意”更正确的地方。

实际示例:想象一下,您正在处于QA职位,并且您要编写一个暂停检查程序(也称为oracle),该程序将确认任何任意由开发团队(D)编写的程序和最终用户(I)提供的任何任意输入,程序D最终将在给定输入I时停止。

提示管理员语音:“好吧,那些愚蠢的用户,让我们确保无论他们输入什么垃圾,我们的服务器任务永远不会以无限循环结束。实现它,代码猴子!“

这似乎是一个好主意,对吧?您不希望服务器挂起,对吧?

暂停问题告诉你的是,你正在接受无法解决的任务。相反,在这种特殊情况下,您需要规划超过阈值时间的任务并准备取消它们。

Mark使用代码而不是输入来说明问题:

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

在我的评论讨论中,我走了恶意输入操作的路线,迫使一个无法解决的问题。马克的例子更加优雅,使用停止的神谕击败自己:

  

所以,欺骗者的输入实际上是   两个元素的列表:第一个元素   是一个提议停止的神谕。该   第二个是另一个输入。什么的   停止杀手做的是问Oracle:   “你认为我会停止输入吗?”   如果神谕说:“是的,你会   停止“,然后程序进入   无限循环。如果神谕说“不,   你不会停止“,然后停止。所以不行   无论神谕说什么,它都是   错。

换句话说,在没有作弊,重新格式化输入,可数/不可数无限或任何其他干扰的情况下,Mark写了一段代码,可以击败任何停止oracle程序。你不能写一个oracle来回答Deceiver是否会停止的问题。

原始答案:

来自伟大的Wikipedia

  

在可计算性理论中,停止   问题是一个决定问题   可以说如下:给出一个   程序的描述和有限的   输入,决定是否该程序   完成运行或将永远运行,   鉴于此输入。

     阿兰图灵在1936年证明了这一点   解决暂停的一般算法   所有可能的程序输入的问题   对不可能存在。我们说的   停止问题是不可判定的   图灵机。谷轮(2004)   属性实际术语停止   马丁戴维斯的问题。

其中一个关键点是您无法控制程序或输入。你被交给那些人,你可以自己回答这个问题。

另请注意,图灵机是有效可计算性模型的基础。换句话说,您在现代计算机语言中所做的一切都可以映射回这些原型图灵机。因此,停止问题在任何有用的现代语言中都是不可判定的。

答案 1 :(得分:40)

要解决暂停问题,您必须开发一种算法,可以确定任意程序是否对任意输入停止,而不仅仅是相对简单的情况在你的例子中。

答案 2 :(得分:29)

这是一个简单的解释,证明停止问题是不可判定的。

假设您有一个程序H,它可以计算程序是否停止。 H有两个参数,第一个是程序的描述,P,第二个是输入,如果P在输入I上停止,则H返回true,否则返回false。

现在编写一个程序p2,它接受另一个程序的描述,p3。 p2调用H(p3,p3),如果H返回true则循环,否则停止。

当我们运行p2(p2)时会发生什么?

它必须同时循环和停止,导致宇宙爆炸。

答案 3 :(得分:21)

这已经被打死了,实际上有一个poetic proof,以 Lewis Carroll 的形式写成了Geoffrey Pullum博士(Language Log的他FAME)。

有趣的东西。这是一种品味:

  

这是我将要使用的技巧 - 这很简单   我将定义一个程序,我称之为Q,
  将使用P的停止成功的预测   激起一场可怕的逻辑混乱。

     

...

     

无论P如何表现,Q都会舀到它:
  Q使用P的输出使P看起来很愚蠢   无论P说什么,都无法预测Q:
  当它出错时P是对的,当它是真的时它是假的!

答案 4 :(得分:9)

维基百科上有Halting Problem可以证明。

为了说明,为什么仅仅将一些技术应用于循环是不够的,请考虑以下程序(伪代码):

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?

Think Carefully

如果你偶然发现了菲尔兹奖牌,请设想these problems代替上述代码。

答案 5 :(得分:7)

  

“如果你只是添加一个循环,你就有了暂停程序,因此无法自动执行任务”

听起来有人过分概括了暂停问题的应用。有许多特定的循环可以证明终止。存在可以对广泛的程序执行终止检查的研究。例如在Coq中,您仅限于可以证明终止的程序。微软有一个名为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;

给定每个整数&gt; = 1,这个程序会停止吗?嗯,它到目前为止已经奏效了,但是没有定理说它会停止每个整数。由于Lothar Collatz,我们有一个猜想,其历史可以追溯到1937年,但没有证明。

答案 6 :(得分:5)

图灵的一个很好的例子是自我指涉 - 假设有一个程序可以检查另一个并确定它是否会停止。将停止程序检查器ITSELF送入停止程序检查程序 - 它应该做什么?

答案 7 :(得分:5)

参考子点“人们回应”如果你只是添加一个循环,你就有了停止程序,因此你无法自动执行任务“”,我将添加这个细节:

那些说你无法通过算法计算任意程序是否会停止的帖子对图灵机来说绝对正确。

问题是,并非所有程序都需要图灵机。这些程序可以用概念上“较弱”的机器计算 - 例如,正则表达式可以完全由有限状态机实现,总是在输入时停止。不是很好吗?

我打赌,当人们说“添加一个循环”时,他们试图表达这样的想法:当一个程序足够复杂时,它需要一个图灵机,因此停止问题(作为一个想法)适用

这可能与问题略有不同,但我相信,鉴于问题的细节,这值得指出。 :)

答案 8 :(得分:4)

这是一个暂停问题永远无法解决的程序。

假设您有神奇的程序/方法来确定程序停止。

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

因此,对于这个例子,我们可以编写一个程序,与我们神奇的暂停方法完全相反。如果我们以某种方式确定给定的程序将停止,我们只是跳进一个无限循环;否则,如果我们确定程序处于无限循环中,我们就会结束程序。

无论你做了多少输入检查,都没有可能的解决方案来确定每个程序写入是否停止。

答案 9 :(得分:3)

到目前为止,有很多有趣的具体例子/类比。如果你想更深入地阅读背景,有一本关于图灵的原始论文The Annotated Turing的好书,由Charles Petzold撰​​写。

在一个相关的,侧面分类的静脉中,网上有一篇非常简洁的文章,Who Can Name the Bigger Number?在图灵机和阿克曼函数上刷。

答案 10 :(得分:2)

来自另一个角度的证据

假设我们有一个带有mov,add,jmp等指令的cpu,但是没有in或out。我们得到了记忆。与其他cpus不同,这个有另一个寄存器,名为 paraReg 。这个寄存器就像一个文件,我们可以将无限的内容移入其中,获取它的大小,寻找它的中间,从中删除一些内容,这些都是通过一些额外的指令完成的。

在开始之前,让我们定义一些单词。 程序是一串指令,是一个字符串。在我们运行程序之前,我们将所有寄存器和内存清零,除了paraReg,它保存参数(一个字符串),然后将程序放入内存位置零并将ip寄存器设置为零。 进程是程序运行的时间。

现在暂停问题可以这样说:给定任何一个名为proObj的程序(如果它接受参数para0,我们在它的第一行添加一条指令:mov paraReg,para0),是否有一个程序需要proObj作为参数,可以决定当proObj开始在paraReg设置为零时运行时proObj是否会停止?

假设我们有一个名为p1的程序。然后我们可以创建另一个程序,称为p2,它接受参数para0。通过p1,我们可以判断一个程序的内容是para0,其参数是para0,是否会停止。(我们这样做。构造一个程序,其第一行是[mov paraReg,para0],其余的是para0。将此程序命名为pro0。然后我们将paraReg设置为pro0并调用p1。)如果它将停止,我们让p2进入无限循环,否则我们让p2停止。

如果我们将p2放入paraReg并运行p2,进程是否会停止?如果它停止,从p2的定义,我们知道当我们将p2放入paraReg并运行p2时,它不应该停止;同样,如果它没有停止,我们知道何时将p2放入paraReg并运行p2,它应该停止。然后我们可以说没有p2,也没有p1。

答案 11 :(得分:2)

它是halting dog problem的一个变体,除了用程序代替狗而停止而不是吠叫。

答案 12 :(得分:2)

已经有很多好的答案,但我没有看到有人说过这样一个事实,即在理论和实践的选择性融合中,停止问题确实是可以解决的。

首先,暂停问题基本上是编写程序的任务,该程序接受任意第二个程序并确定辅助程序是否将停止任意输入。所以你说“是这个程序会停止输入”或“不会不会”。事实上,在一般情况下(其他人似乎已经在图灵机上提供了证据)这是无法解决的。真正的问题是,你可以通过运行它来找出某些东西是否会停止(只是等到它停止),但是你无法通过运行来确定某些东西是否会停止(你将会只是永远等待。)

这是图灵机上的一个问题,根据定义,它具有无限的内存量,因此具有无限多的状态。但是,我们的计算机只有有限的内存。计算机上只有这么多位。因此,如果您能够以某种方式跟踪您在运行程序时看到的所有先前状态(位配置),则可以保证您的检查器永远不会进入无限循环。如果辅助程序最终停止,则说“是,此程序将停止此输入”。如果在停止之前两次看到相同的位配置,则会知道“不会不会”。可能没有太大的技术重要性,但很高兴知道我们面临的很多次真正“困难”的问题在理论上比在实践中更难。

答案 13 :(得分:1)

来自Programming Pearls,由Jon Bentley撰写

4.6问题

5.证明当输入x为正整数时,该程序终止。

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

答案 14 :(得分:1)

您的计划如何解决Collatz conjecture

答案 15 :(得分:1)

您列出了一些简单的案例。

现在,考虑考虑所有其他案例。

有无数可能的场景,你必须全部列出。

除非您当然可以概括它。

这就是暂停问题的来源。你如何概括它?

答案 16 :(得分:0)

你可能会觉得有必要考虑那些不修剪自己草坪的人修剪草坪的人的故事,并问问自己是否他自己修剪草坪。

答案 17 :(得分:0)

问题的确切定义是您需要编写执行以下操作的程序: - 采取任意程序 - 确定程序是否在程序中任意有限输入时停止

然而,这是一个非常高的标准。暂停问题有许多部分解决方案,但没有一般解决方案。更糟糕的是,即使找到部分解决暂停问题的程序也很困难:

BBC h2g2 article on the halting problem

如果您真的已经解决了暂停问题,那么您可以在像rentacoder.com这样的网站上工作。几个月前,其中一位来自名为ATuring的用户提供了一份解决暂停问题的合同。 :)

答案 18 :(得分:0)

假设您编写的算法可以检查任意一段代码并判断它是否停止。

现在给你的算法进行检查。

答案 19 :(得分:0)

又一个例子。我最近碰到了一个叫做冰雹数字的东西。这些数字形成了具有这些规则的序列

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。

这是 I 如何理解暂停问题的关键。我的理解是,除非你实际运行程序,否则你不能肯定知道程序将会/不会停止。因此,您编写的任何程序都可以为您提供肯定的停止问题的答案,而且必须实际运行该程序。

答案 20 :(得分:0)

我建议您阅读:http://en.wikipedia.org/wiki/Halting_problem,尤其是http://en.wikipedia.org/wiki/Halting_problem#Sketch_of_proof,以便了解为什么无法以算法方式解决此问题。

答案 21 :(得分:0)

停止问题的重要性不在于问题本身的重要性;相反,自动化测试在软件工程中几乎没有实际用途(证明程序暂停并不能证明它是正确的,并且在任何情况下假设算法仅提供给定有限的证据输入,而现实生活中的软件开发人员对所有可能的有限输入的测试更感兴趣。

相反,暂停问题是第一个被证明不可判定的问题之一,这意味着在一般情况下不存在适用的算法。换句话说,图灵在70多年前证明存在一些计算机无法解决的问题 - 不仅仅是因为尚未找到正确的算法,而是因为这种算法在逻辑上不存在。

答案 22 :(得分:0)

这是我的尝试,请谨慎对待。

停止问题:是否有可能构建一个程序来判断任意程序是否会因任意输入而停止?

让我们称这样的程序为 decider

现在假设这两个程序:

program_1 (input){
    loop forever
}

program_2 (input){
    halt
}

对于 program_1,我们可以很容易地看出它永远不会停止在任何输入上。类似地,我们可以看出 program_2 将始终停止在任何输入上。

我们可以通过查看他们的源代码而不实际执行程序来判断这一点。

decider 是否可以一直查找源代码并分析控制结构以判断程序是否会在输入时停止?

要回答这个问题,假设以下程序:

program_3 (input) {
    
    ...func definition...

    result = func(input)

    if result = 12345

    then loop forever

    else halt
}

假设 func 是一个将整数映射到整数的函数。并且还假设 func 没有封闭形式。例如,func 可能是一个函数,用于返回素数序列中的第一个输入素数。

现在我们的 decider 会遇到麻烦。为了分析 program_3 的源代码,它必须告诉 func(input) 将映射到什么。所以它必须以某种方式包含由 func 定义的所有映射。但是有无数个整数,因此有无数个这样的映射。因此,在 decider 中包含所有映射细节是不可能的,这进一步意味着 decider 无法分析 program_3 的源代码和控制结构。

这回答了我们的问题。不,decider 不能总是查看源代码并告诉程序将如何运行。它可能适用于某些程序,但不适用于所有程序。

那么您如何假设 decider 可以说明 program_3 的任何信息。它必须在其输入上执行/模拟程序以查看 func 返回什么。

但假设 func 有以下定义:

func (input){
    ...definition of prime(k): return k-th prime number...
    
    result = prime(input)
    
    i = prime(input - 1)
    j = prime(input - 2)

    if(i mod j = 5)

    then loop forever

    else return result
}

现在 func 可以在某些输入上永远循环,导致 program_3 也永远循环。这意味着由于 decider 必须执行/模拟 program_3,如果 program_3 碰巧永远循环,它也可能永远循环。

这终于解决了停机问题。不,我们不能创建一个 decider 来判断任意程序是否会在输入时停止。

答案 23 :(得分:-1)

学习这个程序:

for (x= 1; ; x++)
  for (y= 1; y <= x; y++)
    z= pow(x * x * x + y * y * y, 1./3.)
    if (z == int(z))
      stop;

会停止吗?编译器能否预测它是否会停止?

[注意这个例子并不能证明停机问题的不可能性]

答案 24 :(得分:-2)

解决方案:H检测H+内部的H,忽略H+的输出,取自身H的输出。这样通过检测自引用解决了自引用问题,从而将停机问题简化为自检测的约简问题lol :)

这有点道理,循环是一种自我引用的形式,它指的是它自己的起点。

现在艾伦图灵的“骗局”机器做了同样的事情,它引用自己,修改它的输出并将其作为某种搞砸了的证据。

现在你的任务是证明 Alan 是错的,并编写一个计算机程序,该程序可以将任何程序简化为基本的“公理”等等……并解构,甚至可能检测任何类型的自引用,但也可能是完全机器/软件自我参考,以防止像艾伦这样的骗子误导和愚弄您的机器! ;)

也许这样的机器甚至可能具有自我意识,也许与自我意识有关:)