图灵机Turing Machine
图灵机来自图灵1936年的文章"On Computable Numbers, with an Application to the Entscheidungsproblem"(论可计算数及其在判定性问题上的应用)中提出的思想,论文指出,只要图灵机可以被实现,就可以用于解决任何可计算问题。
图灵机的结构为:
- 一条无限长的纸袋(tape),纸带被分成一个个相邻的格子(square),每个格子都可以写上至多一个字符(symbol);
- 一个字符表(alphabet),即字符的集合,它包含纸带上可能出现的所有字符(包括特殊的空白字符);
- 一个读写头(head),可理解为指向其中的一个格子的指针。它可以读取,擦除,写入当前格子的内容,此外也可以每次向左或右移动一个格子;
- 一个状态寄存器(state register),它追踪着每一步运算过程中,整个机器所处的状态(运行或终止)。当这个状态从运行变成终止,则运算结束,机器停机并交回控制权。
- 一个有限的指令集(instructions table),它记录着读写头在不同情况下应该执行的行为。可以想象为读写头对应的操作指南。比如"当前处于编号53的格子,看到其中内容为0,则擦除,改写为1,并向右移动一格,下一状态为运行"。指令集就对应着计算模型,即函数。
在计算开始前,纸带可以是完全空白的,也可以预先写入字符。运算开始时,读写头从某一位置开始,严格按照此刻的配置(configuration),即:
- 当前所处位置;
- 当前格子内容;
一步步对照着指令集去进行操作,直到状态变成停止,运算结束。而纸带留下的字符序列作为输出,由人为解码为自然语言。
可计算问题
图灵在图灵机思想中证明了,假设上述所说的功能都能以某种物理形式实现,那么任何可计算问题都可以被解决。
在计算机领域,我们研究的一切问题都是计算问题,计算问题泛指一切与计算相关的问题。比如:
- 给定一个正整数 n n n,判断它是否是质数;
- 给定一个0-1序列,把它们按位取反;
- 给定一个字符串,判断某个字符是否存在,以及查找存在的位置;
- 给定一个蕴含逻辑的命题,求它的逆否命题;
相反,非计算问题为:
- 今晚看什么书;
- 你为什么叫白景屹;
计算问题有的可解决,有的不可解决。从而引出计算问题的可计算性(computability),它可以被理解为"是否存在一个算法,能解决在任何输入下的此计算问题"。比如上面的问题"给定一个正整数,判断它是否是质数"。我们确实可以找到一个算法判断一个数是不是质数,比如从2遍历到 n − 1 n-1 n−1,看 n n n是否可以整除它,因此,这个问题是可计算的。
对于不可计算的计算问题,比如停机问题(Halting Problem):给定一段程序的描述和该程序的一个有效输入,运行这个程序,程序最终是会终止还是陷入无限循环。
很明显,这个计算问题不可计算,我们不能找到一个算法在任何情况下都判断它。
回到可计算问题,换言之,对于一个问题,对于任意输入,只要人类可以保证算出结果(不管花费多久),则图灵机也可以保证算出结果(不管花费多久)。
图灵完备Turing Completeness
图灵完备性是针对一套数据操作规则而言的概念。数据操作规则可以是一个编程语言,也可以是计算机里具体的一个指令集。当这套规则可以实现图灵机模型里的全部功能时,就称它具有图灵完备性。
也就是当一套数据操作具有图灵完备性时,该数据操作可以解决任何可计算问题。
RNN已被证明是图灵完备的,因此,RNN可以解决任何可计算问题
基于Brainfuck感受图灵完备
当下的主流编程语言(C++,Java,Python)都是图灵完备的语言。回到语言的底层,一切编程语言实现的功能完全一样,本质上就是一个图灵机。
Brainfuck(BF),是一种极小化的程序语言,它是由Urban Müller在1993年创造的。它一共只包含8个有效字符,每个有效字符就是一条指令。语言非常接近机器指令,比如:
++++++++++[>+++++++>++++++++++>+++<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.
编译后,控制台打印"Hello World!"。BF的工作机制与图灵机高度一致。首先存储数据的方式是一个不限长的一维整数数组,里面的数值全部初始化为0。此外,还有一个数据指针,每一时刻都指向数组的某一任意元素。指针可以向左或向右移动,也可以读取或修改当前值。
BF中的8个有效字符为:
字符 | 含义 |
---|---|
> | 指针向右移动1格 |
< | 指针向左移动1格 |
+ | 使指针当前格中的数值加1 |
- | 使指针当前格中的数值减1 |
. | 把当前格中的数值按ASCII表输出到终端 |
, | 从终端接受1byte数据,存储其ASCII对应的数值到当前格 |
[ | 当指针当前值为0时,程序跳转至与之对应的 ] 之后;否则程序正常执行 |
] | 程序跳转回与之对应的 [ 处 |