我用MATLAB脚本语言编写了一个简单的brainfuck解释器。它被随机的bf程序执行(作为遗传算法项目的一部分)。我遇到的问题是,程序在相当多的情况下都会出现无限循环,因此GA会卡在这一点上。
所以,我需要一种机制来检测无限循环并避免在bf中执行该代码
一个明显的(微不足道的)案例是我有
[]
我可以检测到这一点并拒绝运行该程序 对于非平凡的情况,我发现基本思想是:确定循环的一次迭代如何改变当前单元格。如果变化为负,我们最终将达到0,所以它是一个有限的循环。否则,如果变化是非负的,那么它就是一个无限循环 对于单个循环来说,实现这一点很容易,但是使用嵌套循环会变得非常复杂。例如,(在下面的(1)中指的是单元格1的内容等)
++++ Put 4 in 1st cell (1)
>+++ Put 3 in (2)
<[ While( (1) is non zero)
-- Decrease (1) by 2
>[ While( (2) is non zero)
- Decrement (2)
<+ Increment (1)
>]
(2) would be 0 at this point
+++ Increase (2) by 3 making (2) = 3
<] (1) was decreased by 2 and then increased by 3, so net effect is increment
因此代码会一直运行。然而,对单元格1上的+和 - 的完成次数的天真检查会说-s的数量更多,因此不会检测到无限循环。
任何人都可以想到一个很好的算法来检测无限循环,给定bf中任意数量的循环的任意嵌套?
编辑:我知道暂停问题一般无法解决,但我不确定是否存在特殊情况例外。就像,也许Matlab可以作为一个超级图灵机器,能够确定停止bf程序。我可能会非常错误,但如果是这样,我想知道究竟是怎么回事。
第二次编辑:我写过我声称是无限循环检测器的东西。它可能会错过一些边缘情况(或者不太可能,以某种方式逃脱了图灵先生的魔力),但似乎现在对我有用。 在伪代码形式中,这里是:
subroutine bfexec(bfprogram)
begin
Looping through the bfprogram,
If(current character is '[')
Find the corresponding ']'
Store the code between the two brackets in, say, 'subprog'
Save the value of the current cell in oldval
Call bfexec recursively with subprog
Save the value of the current cell in newval
If(newval >= oldval)
Raise an 'infinite loop' error and exit
EndIf
/* Do other character's processings */
EndIf
EndLoop
end
答案 0 :(得分:74)
Alan Turing想和你谈谈。
答案 1 :(得分:23)
当我使用线性遗传编程时,我只使用了单个程序在其生命周期中允许执行的指令数的上限。我认为这在两个方面是明智的:无论如何我无法真正解决停止问题,而且计算时间太长的程序无论如何都不值得花更多的时间。
答案 2 :(得分:13)
假设您确实编写了一个程序,可以检测此程序是否会在无限循环中运行。让我们说为了简单起见,这个程序是用brainfuck编写的,用于分析brainfuck程序(虽然这不是以下证明的前提条件,因为任何语言都可以模仿brainfuck,而brainfuck可以模拟任何语言)。
现在让我们说你扩展了检查程序来制作一个新程序。当它的输入无限循环时,这个新程序会立即退出,并且当它的输入在某个时刻退出时会永远循环。
如果你自己输入这个新程序,结果会是什么?
如果这个程序在运行时永远循环,那么按照它自己的定义,它应该在以自身作为输入运行时立即退出。反之亦然。检查程序不可能存在,因为它的存在意味着矛盾。
如前所述,您基本上是在重述着名的暂停问题: http://en.wikipedia.org/wiki/Halting_problem
版。我想说清楚,上面的反对不是我自己的,但基本上是1936年阿兰图灵给出的着名的反抗。
答案 3 :(得分:7)
bf中的状态是一个字符数组。
如果我是你,我会在每个“]”(或一次在兰德(1,100)“]”s *)中获取bf解释器状态的哈希值,并断言哈希值是唯一的。
第二次(或更多次)我看到一个哈希,我把整个国家都放在一边。
我看到某个哈希的第三(或更多)时间,我将整个状态与保存的状态进行比较,如果匹配,我就退出。
在每个输入命令('。',IIRC)上,我重置已保存的状态和哈希列表。
优化只是对已触摸的状态部分进行哈希处理。
我还没有解决暂停问题 - 我在运行程序时检测到无限循环。
* rand是使检查独立于循环周期
答案 4 :(得分:4)
无法检测无限循环,但您可以检测程序是否花费了太多时间。
每次运行命令时通过递增计数器来实现超时(例如<
,>
,+
,-
)。当计数器达到一个由观察设置的大数字时,可以说执行程序需要很长时间。为了你的目的,“非常长”和无限是一个足够好的近似。
答案 5 :(得分:3)
如前所述,这是暂停问题。 但在你的情况下,可能有一个解决方案:暂停问题正在考虑的是图灵机,它具有无限的内存。
如果你知道你有一个内存上限(例如你知道你不使用超过10个内存单元),你可以执行你的程序并停止它。这个想法是计算空间限制了计算时间(因为你不能一步编写多个单元格)。执行尽可能多的步骤后,您可以使用不同的内存配置,您可以中断。例如。如果你有3个单元格,256个条件,你最多可以有3 ^ 256个不同的状态,所以你可以在执行那么多步骤后停止。但要小心,有隐式单元,如指令指针和寄存器。你做得更短,如果你保存每个状态配置,一旦你检测到一个,你已经有了一个infite循环。这种方法在运行时肯定要好得多,但因此需要更多空间(这里可能适合散列配置)。
答案 6 :(得分:2)
这不是暂停问题,但是,即使在如1000台BF机器这样有限的机器中试图检测停机也是不合理的。
考虑这个程序:
+[->[>]+<[-<]+]
这个程序不会重复,直到它已经填满了整个内存,只有1000个单元格需要大约10 ^ 300年。
答案 7 :(得分:1)
脱离我的头脑(我可能是错的),我认为在没有实际执行程序本身的情况下检测程序是否具有无限循环会有点困难。
由于程序部分的条件执行取决于程序的执行状态,因此在没有实际执行程序的情况下很难知道程序的特定状态。
如果你没有要求执行具有无限循环的程序,你可以尝试使用“指令执行”计数器,并且只执行有限数量的指令。这样,如果一个程序确实有一个无限循环,那么解释器可以终止陷入无限循环的程序。
答案 8 :(得分:1)
如果我没记错的话,暂停问题证明只适用于涉及自我参照的极端情况。然而,显示一个为什么不能制作无限循环检测器的实际例子仍然是微不足道的。
考虑Fermat's Last Theorem。创建一个遍历每个数字(或在这种情况下为3个数字)的程序很容易,并检测它是否是该定理的反例。如果是这样,它就会停止,否则它会继续。
因此,如果你有一个无限循环检测器,它应该能够证明这个定理,以及许多其他的(也许所有其他的,如果它们可以简化为搜索反例)。
一般而言,任何涉及迭代数字且仅在某些条件下停止的程序都需要一般定理证明器来证明是否可以满足该条件。这是最简单的循环案例。