停机问题的证明是否有点含糊?

看过离散数学上的证明。

也在知乎上搜过。

还有刘未鹏关于停机问题的那篇的博客http://mindhacks.cn/2006/10/15/cantor-godel-turing-an-eternal-golden-diagonal/

基本都是文字加代码论述,但我始终有几个问题没太想明白,感觉证明得牵强,于是自己理解了一下,记于此处,希望能有人来讨论、指点。

一般都是这么证明的(此处我用C++代码):

假设存在这么一个算法可以判断出别的程序是否可以停机。

1 bool halting_judge (program a, char* input)
2 {
3     if (程序a会停止)    
4         return true;
5     else
6         return false;
7 }

因为一般的程序都是要有输入输出的,这里的input就是程序a的输入。使用char形式是因为后面证明时需要把程序代码作为输入,而程序代码是字符串,这里就直接规定好了。

然而图灵大佬想说具有这种能力的程序是不存在的。于是我们创造下面这个函数:

 1 bool hack (program a, char* input)
 2 {
 3     if (halting_judge(a, input))//如果程序a可以停机
 4     {
 5         while (1);//这里就无限循环出不去了
 6         return false;
 7     }
 8     else
 9         return true;
10 }

这个函数的作用是:如果程序a可以停机,则接下来进行无限循环让hack函数无法停机;如果程序a无法停机,则return使hack函数可以停机。

接下来用hack程序代入a,用hack的代码代入input:

bool hack (program a, char* input)
{
    if (halting_judge(hack, hack))//如果程序hack可以停机
    {
        while (1);//这里就无限循环出不去了
        return false;
    }
    else
        return true;
}

于是人们说:如果halting_judge判断hack为停机,则hack(hack)就无限循环,不停机;如果halting_judge判断hack为无法停机,则hack(hack)会返回,为停机。So,hack到底是不是停机的呢?矛盾了,所以halting_judge无法判断。

可能是我理解错什么了吧?个人认为这样的证明未免有些模糊。

首先想到,所谓hack(hack),括号里的hack应该指的是程序吧?那作为程序它的两个参数是什么呢?难道什么参数都行?这里不解释一下吗?

然后看一看halting_judge的定义,想一下上面的问题,则发现括号里的hack它的参数是hack代码字符串。

至此可以发觉,我们上述的hack函数是不能代入进去的,它和program a不是一类函数!因为hack有两个参数,一个是程序,一个是字符串;而当时定义halting_judge函数的时候,它只能承受这样一类程序a:这个程序的参数只需要一个——字符串。

我的意思是说:虽然halting_judge里随便敷衍过去了,但实际上默认的a运行起来只需要input,而不是像hack那样需要一个program加一个input才能运行。

也就是说,要把halting_judge改成(hack程序,hack程序,hack代码)这种形式,或者把hack改成(char* input)这种形式,才可以实现hack自身代入。而第二种改法就是我之后要使用的。

另外,我们先抛开这个小bug不说,假如hack函数输入只需要用input来代替,则,(接下来我只说一种情况),如果hack(input)用halting_judge判断完是停机的,那么hack(hack(input))是不停机的,也只能说明hack的输入为input时停机,hack的输入为hack(input)时不停机,凭什么用一个简单的hack就代替了全部情况呢?就说hack停不停机是矛盾的呢?这逻辑就像“父亲(我的)是程序员,父亲(父亲(我的)的)——也就是爷爷~不是程序员,所以父亲到底是不是程序员这矛盾了判断有误”……单说父亲不说谁的,耍流氓。

当然了还是那句话,很可能是我理解上的差错。但各种短证明都会在结尾矛盾的那个地方轻飘飘一笔带过,确实难以捉摸。

下面是我的想法:

我修改一下hack函数:

 1 bool hack (char* code)
 2 {
 3     program a = 编译code;//这是我加的一句话
 4     if (halting_judge(a, code))//如果程序a可以停机
 5     {
 6         while (1);//这里就无限循环出不去了
 7         return false;
 8     }
 9     else
10         return true;
11 }

然后hack就可以自身代入了,把hack这段代码传入进去就是下面这样:

 1 bool hack (char* hackcode)
 2 {
 3     program a = 编译hackcode;//现在a即hack函数
 4     if (halting_judge(hack, hackcode))//如果程序a可以停机
 5     {
 6         while (1);//这里就无限循环出不去了
 7         return false;
 8     }
 9     else
10         return true;
11 }

程序往下走,if判断语句我们进去,情况是:

1 bool halting_judge (program hack, char* hackcode)
2 {
3     //现在a就是hack,input就是hack的code
4     if (程序hack会停止)    
5         return true;
6     else
7         return false;
8 }

而程序hack会不会停止呢?取决于它while(1)了还是return true了,即,注意,我们现在是在用halting_judge判断:当hack函数的输入为hackcode时是否停机

有没有发现这个和第一步一样?又递归回来了!最初一步也就是还没走到halting_judge的时候,就是用hack函数输入hackcode。

那么总结一下就是,只有我们把hack函数的输入设置为hackcode,才真正变成了用halting_judge去检测hack程序它自己(而不是什么hack(hack(……))),之后再讨论两种情况才能达到自相矛盾的效果。

这时才表现了证明中的输入非要用hack本身(其实是它的code)的重要意义:你把输入换成POJcode试试,判断就成了(POJ,POJcode),没法指向hack自身。

刚刚我说hack(hack(input))和hack(input)不能全统一为hack,那这里为什么可以?因为这里这俩hack都是用hackcode编译出来的。比喻来说,你就是hackcode,假如把你拎出来会导致我们能从人群中确定你爸爸。现在把你拎出来,和我们先把你带进幼儿园再把你拎出来,我们从人群中找出来的你爸爸,是一个人。

这上面的表述大费周章其实就是为了真真正正地达到:让hack程序运行是否停机,然后让halting_judge判断的也是hack程序本身是否停机,然后通过矛盾反证halting_judge不存在。两种情况都矛盾我就不多说了。

以上就是我的思考。等有能力看懂刘未鹏先生关于Y combinator和哥德尔不完备性的精彩论述,以及我自身不断学习计算机技术以后,我会继续补充和改正以上论述的。由于初涉计算机领域,个人逻辑也不怎么样,非常希望大家指出我文章中的错误,感谢!

原文地址:https://www.cnblogs.com/AlphaWA/p/9598436.html