H 是否正确地决定 P 永不停止? 对于具有相同参数的两个调用来自 main 与 P,H 的行为不同在外部 H 检测到 inf 递归后继续模拟内部 H假设 H 返回,停止的 P 可以产生相同的轨迹其他漏洞?完全没有调用H的类似跟踪?

问题描述

H 是否正确地决定 P 永不停止?
(下面提供了 P 的 C/x86 源代码

停止问题不可判定性和无限嵌套模拟
创建 x86utm 操作系统是为了可以用 C 的高级语言具体检查停机问题。 H 是用 C 编写的函数,它分析其他用 C 编写的函数的 x86 机器语言。 H 识别无限递归的简单情况和无限循环。传统的停机问题证明反例模板只是一个不停机的输入。

H 用 x86 模拟器模拟它的输入,直到它确定它的输入永远不会停止。一旦 H 认识到它的输入永远不会停止,它就会停止模拟这个输入并返回 0。对于停止的输入,H 的行为就像一个 x86 模拟器一样,只是简单地运行它的输入直到完成然后返回 1。

通过模拟暂停决策器 H 对 P(P) 的 x86 仿真的执行轨迹 [如下所示] 最终证明 P 不可能达到其最终状态 0xc50。这提供了完整的证明,即 H 的输入永远不会停止,因此 H(P,P)==0 是正确的。

停止计算:是任何最终达到其最终状态的计算。

病态输入一个停止决策器被规定为意味着任何输入被定义为与 Sipser 描述的其相应的停止决策器决定的相反:

现在我们以 H 作为子程序构建一个新的图灵机 D。这 new TM 调用 H 来确定当 M 的输入是它的时候 M 做什么 自己的描述⟨M⟩。一旦 D 确定了这个信息,它就会 相反。 (Sipser:1997:165)

这个问题只有在病理去除后才能正确回答。当停止决策器仅充当其输入的纯模拟器直到做出其停止状态决策之后,停止决策器与其输入之间的反向通道通信的反馈回路会阻止正确的停止状态决策。

标准伪代码停机问题模板“证明”停机问题永远无法解决,因为真(停机)和假(不停机)的值都不能从停机决策器正确返回到混淆输入。

procedure compute_g(i):  // (Wikipedia:Halting Problem) 
  if f(i,i) == 0 then   // adapted from (Strachey,C 1965) 
    return 0             // originally written in CPL 
  else                   // ancestor of the BCPL,B and C
    loop forever         // programming languages

这个问题的解决是因为模拟暂停决策器会在向该输入返回任何值之前中止其输入的模拟。它中止其输入的模拟,因为它的输入指定了对任何模拟暂停决策器本质上是无限递归(无限嵌套模拟)的内容

H 在模拟输入 (P,P) 的每条指令后,分析其 P(P) 的 x86 仿真的(当前更新的)存储的执行跟踪。一旦与非停止行为模式匹配,H 就会中止其输入的模拟并决定其输入不停止。

模拟输入到 H(P,P) 的可验证正确的 x86 执行跟踪证明该输入不可能达到其最终状态,而 H 充当此输入的纯 x86 模拟器。这明确地证明 H 确实正确地决定其输入永不停止。

// Simplified Linz Ĥ (Linz:1990:319)
// Strachey(1965) CPL translated to C
void P(u32 x) 
{
  if (H(x,x)) 
    HERE: goto HERE; 
} 

int main() 
{   
  Output("Input_Halts = ",H((u32)P,(u32)P));
}

_P()
[00000c36](01)  55          push ebp
[00000c37](02)  8bec        mov ebp,esp
[00000c39](03)  8b4508      mov eax,[ebp+08] // 2nd Param
[00000c3c](01)  50          push eax
[00000c3d](03)  8b4d08      mov ecx,[ebp+08] // 1st Param
[00000c40](01)  51          push ecx
[00000c41](05)  e820fdffff  call 00000966    // call H
[00000c46](03)  83c408      add esp,+08
[00000c49](02)  85c0        test eax,eax
[00000c4b](02)  7402        jz 00000c4f
[00000c4d](02)  ebfe        jmp 00000c4d
[00000c4f](01)  5d          pop ebp
[00000c50](01)  c3          ret
Size in bytes:(0027) [00000c50]

_main()
[00000c56](01)  55          push ebp
[00000c57](02)  8bec        mov ebp,esp
[00000c59](05)  68360c0000  push 00000c36
[00000c5e](05)  68360c0000  push 00000c36
[00000c63](05)  e8fefcffff  call 00000966
[00000c68](03)  83c408      add esp,+08
[00000c6b](01)  50          push eax
[00000c6c](05)  6857030000  push 00000357
[00000c71](05)  e810f7ffff  call 00000386
[00000c76](03)  83c408      add esp,+08
[00000c79](02)  33c0        xor eax,eax
[00000c7b](01)  5d          pop ebp
[00000c7c](01)  c3          ret
Size in bytes:(0039) [00000c7c]

 machine   stack     stack     machine    assembly 
 address   address   data      code       language
 ========  ========  ========  =========  =============
Begin Local Halt Decider Simulation at Machine Address:c36
[00000c36][002117ca][002117ce] 55          push ebp
[00000c37][002117ca][002117ce] 8bec        mov ebp,esp
[00000c39][002117ca][002117ce] 8b4508      mov eax,[ebp+08]
[00000c3c][002117c6][00000c36] 50          push eax       // push P
[00000c3d][002117c6][00000c36] 8b4d08      mov ecx,[ebp+08]
[00000c40][002117c2][00000c36] 51          push ecx       // push P
[00000c41][002117be][00000c46] e820fdffff  call 00000966  // call H(P,P)

[00000c36][0025c1f2][0025c1f6] 55          push ebp
[00000c37][0025c1f2][0025c1f6] 8bec        mov ebp,esp
[00000c39][0025c1f2][0025c1f6] 8b4508      mov eax,[ebp+08]
[00000c3c][0025c1ee][00000c36] 50          push eax       // push P
[00000c3d][0025c1ee][00000c36] 8b4d08      mov ecx,[ebp+08]
[00000c40][0025c1ea][00000c36] 51          push ecx       // push P
[00000c41][0025c1e6][00000c46] e820fdffff  call 00000966  // call H(P,P)
Local Halt Decider: Infinite Recursion Detected Simulation Stopped 

在上面模拟P(P)的14条指令中我们可以看到P的前7条指令重复了。这 7 条指令序列的结尾 P 调用 H,并使用自己的机器地址作为 H(P,P) 的参数。因为 H 只检查其输入的行为,而在调用 H(P,P) 时忽略其自身的行为,所以我们只能看到 P 的第一条指令被模拟。

任何对 x86 语言足够了解的人都可以看到 P 的这 7 条模拟指令都无法摆脱其无限重复的行为模式。当 H 识别出这种无限重复的模式时,它会中止对 P(P) 的模拟并报告其输入:(P,P) 从未达到其最终状态 0xc50。

[00000c68][0010172a][00000000] 83c408      add esp,+08
[00000c6b][00101726][00000000] 50          push eax
[00000c6c][00101722][00000357] 6857030000  push 00000357
[00000c71][00101722][00000357] e810f7ffff  call 00000386
Input_Halts = 0
[00000c76][0010172a][00000000] 83c408      add esp,+08
[00000c79][0010172a][00000000] 33c0        xor eax,eax
[00000c7b][0010172e][00100000] 5d          pop ebp
[00000c7c][00101732][00000068] c3          ret
Number_of_User_Instructions(27)
Number of Instructions Executed(23721)

Strachey,C 1965。 一个不可能的程序 The Computer Journal,第 7 卷,第 4 期,1965 年 1 月,第 313 页,https://doi.org/10.1093/comjnl/7.4.313

林茨,彼得,1990 年。。形式语言和自动机简介。列克星敦/多伦多:DC Heath and Company。 (318-320)

Sipser,Michael 1997。 计算理论导论。波士顿:PWS 出版公司 (165-167)

论文中提供了更多详细信息:
Halting problem undecidability and infinitely nested simulation

解决方法

对于具有相同参数的两个调用(来自 main 与 P),H 的行为不同

我不认为将某些对 H 的调用视为创建无限递归是有效的,而是认为来自最终父级 main 的调用具有相同的参数以停止并返回值。

H 试图回答的最终问题是如果 main 直接调用 P(P) 会发生什么。在那种情况下,将没有“外部” H 来中止整个事情并使 H 不返回。所以 P(P) 对 H(P,P) 的调用会返回。

考虑一个不想通过调用 noreturn 函数而停止的 main,因此它调用 H 来询问包括 P(P) 在内的各种函数。 mainH(P,P) 的调用返回 0,表示 P(P) 不会停止,因此 main 然后调用 P(P),假设它不会返回。但是P(P) 确实会返回。 H(P,P) 对主要的 P(P) 会做什么撒了谎。如果它以这种方式工作,它就不是一个正确的停止检测器。

int main(){
   printf("P(P) %s halt\n",H(P,P) ? "will" : "won't");
   P(P);        // then call it
   puts("P(P) did halt");
}

您的 H 将产生以下输出:

P(P) won't halt
P(P) halted

我认为这是整个想法中最根本的缺陷。如果你有“这句话是假的”。您不能简单地决定何时将其视为错误以及何时将其视为悖论。

或者对于非悖论函数:可能有一致的答案:

void R(void *x) {
    H(x,x);         // don't care what it returns
    return;         // definitely return after H returns
}

int main(){
   printf("R(R) %s halt\n",H(R,R) ? "will" : "won't");
   R(R);        // then call it
   puts("R(R) did return");
}

H(R,R) 可以而且应该返回 1,但你的决策者仍然会返回 0。(因为它进入了来宾模拟并使 H 表现得好像它没有返回,而不是表现得像当称为最外层。因此它不是对被测函数及其调用内容的准确模拟)。

如果 H 返回 1,则不会在无限循环中发生说谎者悖论。如果您提议的 H 将模拟中的 H(R,R) 视为不返回,则会得到错误的答案,从而使决策与实际不匹配从 main 调用时的行为。

被调用的 H 包括中止逻辑。将外部 H 连接到访客 H 并将某些 H 调用视为非暂停意味着访客内部的 H 无法正常工作,并且未正确模拟。


在外部 H 检测到 inf 递归后继续模拟内部 H

为了修复这个问题,并模拟一个总是返回的 H, 我认为在检测到嵌套模拟递归时,你需要模拟最里面的未中止的 H 实际返回。但是你还不知道它应该返回 0 还是 1。(或者 NaN 表示悖论?)

因此,与其放弃并在外部 H 中返回 1,您还需要内部 H 继续使用两种可能的返回值进行模拟,看看是否会导致矛盾。 >

在 R 中,该函数简单地以任一方式返回(不会根据内部 H 返回值发散,因此模拟简单高效,而不是分支)。因此我们可以模拟中止的 H 调用返回和 R(R) 暂停,因此下一个 H 向上返回 1。

如果 R 的进一步执行在 H(R,R) == 0 的情况下(或两者)达到了无限循环,则 H(R,R) 可以确定为 0。

如果两者都有效(H(R,R) == 1 返回值导致暂停,H(R,R) == 0 返回值导致非暂停),那么您可以简单地选择一个。 H 的实现细节将决定对 R(R) 的真正调用是否停止,这很好,因为 R(R) 涉及调用这个特定的 H。

一旦我们有了有效的决定(但不是之前),我们就可以将所有嵌套展开回到称为 R(R) 的最外层,并从那里继续模拟。 (例如,对于函数在执行其他递归调用后仅调用 H(R,R) 并且稍后会执行更多操作的一般情况)。

在相反的情况下,就像证明中的悖论 P,其中 H(P,P) == 1 导致非停机,H(P,P) == 0 导致非停机,那么两者都不是返回值有效。 对 H(P,P) 的最外层调用应该抛出异常或返回非数字或其他东西,或者本身不会停止。所以检测悖论是可能的,但仍然没有真正的方法来处理它。

(我认为 R(R) 调用某些 R 不可能涉及 H(R,R) 的交替 0 和 1 返回值作为达到一致性的一种方式,除了没有有效的正确答案。所以这个 H 的父母可能能够当场返回而不是他们自己继续直到检测到不涉及嵌套 H 的循环或递归,但我认为这只是一个优化。可能如果 R(R) 本身返回 int,而 R(R) 直接调用 R(R) 并使用该返回值,以及调用 H(R,R),这是否引入了新的复杂程度?不确定 :P)


我不知道是否有任何/所有这些都是有效的反对意见。我认为至少第一个是,基于您对 H 工作方式的描述。 (以及您在上一篇文章中对系统的更详细描述,即单独的流程)。如果您能证明这些反对意见实际上对您的推理没有问题,那就太好了。 (Stack Overflow 的问答格式不适合反复提炼,但您可能可以发布自己的答案,以显示即使面对这些反对意见,您的方法也是如何工作的。)


(原始答案。我在上面添加了更多关于 CS 理论的想法。下面是关于您展示的特定案例的更多细节。)

假设 H 返回,停止的 P 可以产生相同的轨迹

你显示的轨迹与永不停止是一致的,但是一个停止的 P 可以产生相同的轨迹。由于您规定您的 H 在看到这样的跟踪时确实会停止,因此“客人”P 可以调用它而不会产生无限递归。但是之后不是无限循环,而是 P 可以简单地返回。

// halting program your H as described would detect as non-halting
void Q(u32 x) {
  u32 Input_Halts = H(x,x);   // returns after 2 nestings detect what it thinks is inf recursion
  return;
} 

对于 Q 返回 false 的 H 不是停机检测器。 因为对于被调用的 H,Q 将停机。仅凭跟踪无法判断 H 返回后函数将执行什么操作。

我没有立即看到用您正在尝试做的事情来修复此问题的方法,但也许您或其他人会这样做。也许就像深入3层看看返回后会发生什么一样简单?但是在那种情况下,带有原始 P 的 H(P,P) 将达到无限循环,因此您的停止检测器现在卡住了,而不是自己做出决定。 (我还没有详细研究过那个逻辑;我可能弄错了。)

更新来自评论:当最外层的 H 看到无限递归的开始时,它必须向最内层的 H 发出信号返回 false,而不是中止其模拟.然后它可以正确地决定这个答案中的一些例子。 H 是一个停止检测器,它本身总是停止,并且使用外部实例监控事物的嵌套模拟是实现它的一种方法。

(这也将解决从 H 模拟的 P 的调用返回的 main 调用被假定为无限的问题,我将其移至本答案的开头)。


其他漏洞?

我认为在上一篇关于此的帖子中,您说 H 实际上会将嵌套模拟生成为一个单独的进程,具有自己的地址空间,因此我想这排除了 P 在静态存储中保留计数器的可能性。否则

    static u8 counter=0;
    if (--counter == 0)
        return;     // return after 256 recursions
    // else call H and do the rest of your P

您还需要确保来宾环境是 100% 确定的,并且每次都以相同的方式初始化,否则 P 可能会根据某些内存位置或寄存器值有条件地返回。或者在 rand() == 0_rdtsc() & 0xff == 0。当然,简单地提供这些很容易:除非你赋予它这样的能力,否则图灵没有能力在每次运行程序时产生不同的随机序列。


事实上,您的跟踪显示了使用随机(?)堆栈指针的嵌套 P,例如 002115d3 用于第一次调用,然后 0025bffb 用于第二次调用。 (另外,为什么你的堆栈指针值是奇怪的?你是从 ESP=0x???ff 而不是 ...00 或 FC 开始的?)这可能是可以解决的,因为你似乎没有让它们共享一个实际的堆栈嵌套 P 获得比原始 P 更低的堆栈地址。如果你这样做了,那么你可以写

// return after some recursion,based on non-fixed ESP
void P(u32 x) 
{
  u32 stackptr = &x;   // force the compiler to spill x from a reg to memory
  if (stackptr <= threshold)
     return;
  if (stackptr & 0xf0 == 0xf0)  // 2nd 4 bits all set,does this happen randomly?
     return;
  // else keep recursing
  u32 Input_Halts = H(x,x);  
  if (Input_Halts) 
    HERE: goto HERE; 
} 

完全没有调用H的类似跟踪?

如果递归涉及 H(P,P),我猜你会写你的 H 来检测可能的无限递归,这意味着 P 在一个新初始化的环境中被调用,状态已知。

否则你可以简单地使用一个计数器。

void R(u32 x) 
{
  --x;        // or x >>= 1;   to return in at most ~31 calls.
  if (x == 0)
    return;      // base-case
  else
    R(x);
}

如果 x 在寄存器中传递,永远不会溢出到堆栈,跟踪看起来很像你展示的那个。 (我认为。)或者你当然可以使用 static u8 counter = 0;

或者因为 P 可以用 x86 asm 编写(或者可以在某处创建一个可执行缓冲区),它甚至可以推送 H 的地址但调用它。但这听起来像一个足够聪明的 H 可以注意到的东西。

被这些函数中的任何一个愚弄(以任何方式)的 H 不是停机检测器,因此首先不是作为停机问题证明的一部分的候选者.


您是否有 H 的真正具体实现?我认为您在另一个问题中提到过,但在这个问题中我什至看不到它的链接。看起来应该可以按照你描述的方式实现,但这有多有趣取决于它有多容易被愚弄,而这取决于细节。


可以处理这样使用的H已经被证明假定了

您在评论中提问(强调我的):

H是否正确决定P永不停止

经过更多时间考虑之后,目前认为 H 决定 P(P) 停止是正确的。

由于 H 是一个工作停止检测器,不会陷入无限递归,P 可以安全地使用它而不会导致无限递归。您已经实现了那个重要的功能(很简洁)。

你的 H 会检测到递归的开始并返回 false,表明 P(P) 不是一个停止的程序。

因此,if (Input_Halts) 未被采用,因此 P 返回。 糟糕,P 确实停止了,与 H 检测到的情况相反。 荒谬的推理,H 没有正确检测到 P 停止。 (如果 H 回复说 P 停止,P 会决定不停止。)

我认为,这个矛盾是证明的核心,而不仅仅是 H 自己停止的能力,尽管 P 是这样称呼它的。这是有效停止检测器的一项要求,您显然已经满足了这一要求,但这不是唯一的要求。另一个要求是对所有输入使用正确答案暂停,否则它不是暂停检测器。

你不能规定 P 自己不能期望得到正确的答案,只有原始调用者可以。这意味着当你看到看起来像无限递归的开始时你不能中止(如果是这样,请参阅前面的反对意见),你必须实际模拟 H 返回到 P 以确定 P 是否真的是一个停止的程序与否。

H(P,P) 的第一次调用是询问 P(P) 是否停止,而不是这个 H 的实现是否以 P 作为输入停止,因此必须至少有一个 P,其中 H 实际返回而不是被一些外部父母中止。否则 H(x,x) 不是对停止检测器的调用,它基本上是对 exit() 的调用。或者我猜是无限循环函数。

您的外部 H(从 main 调用)决定 P 对 H(P,P) 的调用不会返回。这意味着 H 不起作用(当被 P 调用时),即它不是一个停止检测器。

再说一次,也许你有办法解决这个问题,或者我没有抓住你想要展示的重点。但我很确定那个 P 的重点不是模拟的无限递归,而是根据返回值实际返回或循环。

,

x86utm 操作系统的创建是为了可以具体地检查停机问题,没有任何细节只是留给想象。暂停决策器 H 使用 x86 仿真器检查其输入 P 在调试步骤模式下的行为。

直到 H 在其输入 H 的行为中识别出无限重复模式之前,它的行为就好像它只是一个纯粹的 x86 模拟器。这意味着当 P 调用 H(P,P) 时,这在计算上等同于 P 调用自身:递归地调用 P(P)。

当我们仔细检查 H 对 P 的模拟,了解到当 P 调用 H(P,P) 时这与 P 调用 P(P) 是一样的,那么我们可以看到 x86 的执行轨迹表明P 陷入无限递归。 H 识别出这种模式,中止其对 P 的模拟并报告 P 从未停止。

前提(1)(公理) 除非模拟被中止,否则每一个永远不会停止的计算都是永远不会停止的计算。这根据其词义证实是正确的。

前提(2)(已验证的事实) 对 H(P,P) 的输入模拟永远不会停止而不被中止是基于其 x86 执行跟踪的已验证事实。 (如下所示)。

结论(3) 从上述真实前提可以得出,模拟停止决策器 H 正确报告其输入:(P,P) 永不停止。

// Simplified Linz Ĥ (Linz:1990:319)
void P(u32 x) 
{
  u32 Input_Halts = H(x,x);  
  if (Input_Halts) 
    HERE: goto HERE; 
} 

int main() 
{   
  u32 Input_Halts = H((u32)P,(u32)P);  
  Output("Input_Halts = ",Input_Halts);
}

_P()
[00000b1a](01)  55              push ebp
[00000b1b](02)  8bec            mov ebp,esp
[00000b1d](01)  51              push ecx
[00000b1e](03)  8b4508          mov eax,[ebp+08]
[00000b21](01)  50              push eax       // 2nd Param
[00000b22](03)  8b4d08          mov ecx,[ebp+08]
[00000b25](01)  51              push ecx       // 1st Param
[00000b26](05)  e81ffeffff      call 0000094a  // call H
[00000b2b](03)  83c408          add esp,+08
[00000b2e](03)  8945fc          mov [ebp-04],eax
[00000b31](04)  837dfc00        cmp dword [ebp-04],+00
[00000b35](02)  7402            jz 00000b39
[00000b37](02)  ebfe            jmp 00000b37
[00000b39](02)  8be5            mov esp,ebp
[00000b3b](01)  5d              pop ebp
[00000b3c](01)  c3              ret
Size in bytes:(0035) [00000b3c]

_main()
[00000bda](01)  55              push ebp
[00000bdb](02)  8bec            mov ebp,esp
[00000bdd](01)  51              push ecx
[00000bde](05)  681a0b0000      push 00000b1a  // push address of P
[00000be3](05)  681a0b0000      push 00000b1a  // push address of P
[00000be8](05)  e85dfdffff      call 0000094a  // call H
[00000bed](03)  83c408          add esp,+08
[00000bf0](03)  8945fc          mov [ebp-04],eax
[00000bf3](03)  8b45fc          mov eax,[ebp-04]
[00000bf6](01)  50              push eax
[00000bf7](05)  683b030000      push 0000033b
[00000bfc](05)  e869f7ffff      call 0000036a
[00000c01](03)  83c408          add esp,+08
[00000c04](02)  33c0            xor eax,eax
[00000c06](02)  8be5            mov esp,ebp
[00000c08](01)  5d              pop ebp
[00000c09](01)  c3              ret
Size in bytes:(0048) [00000c09]

Columns
(1) Machine address of instruction
(2) Machine address of top of stack
(3) Value of top of stack after instruction executed
(4) Machine language bytes
(5) Assembly language text  
===============================
[00000bda][00101647][00000000] 55         push ebp
[00000bdb][00101647][00000000] 8bec       mov ebp,esp
[00000bdd][00101643][00000000] 51         push ecx
[00000bde][0010163f][00000b1a] 681a0b0000 push 00000b1a // push P
[00000be3][0010163b][00000b1a] 681a0b0000 push 00000b1a // push P
[00000be8][00101637][00000bed] e85dfdffff call 0000094a // call H

Begin Local Halt Decider Simulation at Machine Address:b1a
[00000b1a][002116e7][002116eb] 55         push ebp
[00000b1b][002116e7][002116eb] 8bec       mov ebp,esp
[00000b1d][002116e3][002016b7] 51         push ecx
[00000b1e][002116e3][002016b7] 8b4508     mov eax,[ebp+08]
[00000b21][002116df][00000b1a] 50         push eax      // push P
[00000b22][002116df][00000b1a] 8b4d08     mov ecx,[ebp+08]
[00000b25][002116db][00000b1a] 51         push ecx      // push P
[00000b26][002116d7][00000b2b] e81ffeffff call 0000094a // call H
[00000b1a][0025c10f][0025c113] 55         push ebp
[00000b1b][0025c10f][0025c113] 8bec       mov ebp,esp
[00000b1d][0025c10b][0024c0df] 51         push ecx      
[00000b1e][0025c10b][0024c0df] 8b4508     mov eax,[ebp+08]
[00000b21][0025c107][00000b1a] 50         push eax      // push P
[00000b22][0025c107][00000b1a] 8b4d08     mov ecx,[ebp+08]
[00000b25][0025c103][00000b1a] 51         push ecx      // push P
[00000b26][0025c0ff][00000b2b] e81ffeffff call 0000094a // call H
Local Halt Decider: Infinite Recursion Detected Simulation Stopped 

在上面模拟P(P)的16条指令中我们可以看到P的前8条指令重复了。这个 8 条指令序列的结尾是对 H(P,P) 的调用。因为 H 只检查其输入的行为,而在调用 H(P,P) 时忽略其自身的行为,所以我们只能看到 P 的第一条指令被模拟。

任何对 x86 语言足够了解的人都可以看到 P 的这 8 条模拟指令都无法摆脱它们无限重复的行为模式。当 H 识别出这种无限重复的模式时,它会中止对 P(P) 的模拟并报告其输入:(P,P) 永远不会在其输入上停止。

[00000bed][00101643][00000000] 83c408     add esp,+08
[00000bf0][00101643][00000000] 8945fc     mov [ebp-04],eax
[00000bf3][00101643][00000000] 8b45fc     mov eax,[ebp-04]
[00000bf6][0010163f][00000000] 50         push eax
[00000bf7][0010163b][0000033b] 683b030000 push 0000033b
[00000bfc][0010163b][0000033b] e869f7ffff call 0000036a
Input_Halts = 0
[00000c01][00101643][00000000] 83c408     add esp,+08
[00000c04][00101643][00000000] 33c0       xor eax,eax
[00000c06][00101647][00000000] 8be5       mov esp,ebp
[00000c08][0010164b][00100000] 5d         pop ebp
[00000c09][0010164f][00000080] c3         ret
Number_of_User_Instructions(33)
Number of Instructions Executed(26452)

在计算 int main() { P(P); } 中,如果必须中止无限调用链的任何调用以防止此链无限执行,那么我们知道此计算:P(P) 指定了无限调用链。

Linz,Peter 1990。 形式语言和自动机简介。列克星敦/多伦多:DC Heath and Company。 (318-320)