说到图灵机,我们首先要说说图灵这个人。笔者觉得我们这种搞计算机的人都应该知道并记得这个人。
图灵,1912年6月23日生于英国帕丁顿。是数学家、密码破译专家,当然还有很多“家”,我们就先不说了。后来他又到美国普林斯顿大学取得博士学位,二战爆发后返回英国,帮助军方破解德国的著名密系统Enigma,帮助盟军取得了二战的胜利。啊,看到这里你是不是瞬间觉得他很伟大了。不过笔者认为他真正伟大的地方不在这里,而是他提出了两个最重要的东西——图灵机和图灵测试。也是因为这两个东西,他后来被人们尊称为计算机之父、人工智能之父。人们为了纪念他,专门设置了图灵奖,学计算机的不会不知道图灵奖。
图灵先后提出了图灵机和图灵测试,我们这里只关注图灵机,看看它究竟有什么神奇之处,又是如何与我们现代的计算机关联起来的。
图灵机是图灵提出的一种思想模型,是抽象的,是存在于大脑之中、存在于想象之中的。也就是说图灵并没有做出他所描述中的这种物理机器。那么这种机器是什么样子呢?它到底能做些什么呢?简单的说它是这样的,它有一条无限长的纸带,纸带上面分成了一个个小方格。有一个机器读头在纸带上来回移动。机器读头能根据读取小方格中的信息作出一些动作。在每个时刻,机器读头都要从当前纸带上读入一个方格中的信息,然后作出相应的动作,比如向小方格内写入信息或者移动自己到下一个小方格。如此反复直到遇到一个小方格中放的是停止标志。
图灵想出这种机器,是想用这种机器来模拟人们用纸笔进行数学运算的过程,他把这样的过程看作下列两种简单的动作:
1.在纸上写上或擦除某个符号;
2.把注意力从纸的一个位置移动到另一个位置;
而在每个阶段,人要决定下一步的动作,依赖于此人当前所关注的纸上某个位置的符号和此人当前的思维状态。
为了模拟人的这种运算过程,图灵构造出一台假想的机器,该机器由以下几个部分组成:
1.一条无限长的纸带。纸带被划分为一个接一个的小格子,每个格子上包含一个来自有限字母表的符号,字母表中有一个特殊的符号表示空白。纸带上的格子从左到右依此被编号为 0,1,2,...,纸带的右端可以无限伸展。
2.一个读写头。该读写头可以在纸带上左右移动,它能读出当前所指的格子上的符号,并能改变当前格子上的符号。
3.一套控制规则。它根据当前机器所处的状态以及当前读写头所指的格子上的符号来确定读写头下一步的动作,并改变状态寄存器的值,令机器进入一个新的状态。
4.一个状态寄存器。它用来保存图灵机当前所处的状态。图灵机的所有可能状态的数目是有限的,并且有一个特殊的状态,称为停机状态。
是不是觉得这太简单了,就是这么简单的思想。在人类的世界里构建出的任何复杂的东西,都是从最简单的思想开始的。
好了关于图灵机的介绍,笔者就不在啰嗦了,总体感觉是这个假想的机器很简单。和我们现代用到的计算机好像差的很远,甚至没有什么联系。下面我们就来用个实例来看看图灵是如何运转并最终完成计算任务的。为了正确并顺利的说明这个实例在图灵机上的运转,我们得把图灵机小小的扩展一下,增加几条规则。
如果让读者用C语言写出一个1累加到100(每次加1)的程序,笔者可能立马就能听到一阵不屑一顾的声音,然后是读者以迅雷不及掩耳之势“盲码”出和下面差不多的代码。
代码:
……
int sum=1;
for(int i=0;i
{
sum++;
}
……
写这几行代码读者可能不需要20秒钟就能完成,不需要5秒钟就能看懂它,甚至不需要2秒钟就能知道它的结果。但是我们要想让我们的图灵机去执行这计算过程,就不是那么容易了。可能我们的图灵机和图灵描述的机器有点差异,但我们保留了图灵的核心思想,所以我们的机器仍然可以称为图灵机。
首先我们假定我们的图灵机能根据纸带上小方格中的信息执行如下规则:
1.加法规则;当图灵机在小方格内读到的符号是“+”时,我们的图灵机就执行加法规则,并且规定紧接着下面两个方格里放着两个加数。执行的动作是:将两个数相加结果放在内部寄存器中。
2.存放规则;当图灵机在小方格内读到的符号是“S”时,我们的图灵机就执行存放规则,并且规定紧接着下面1个方格里放着存放的地址,也就是哪个小方格。执行的动作是:将内部寄存器中的数存放在指定的小方格中。
3.装载规则;当图灵机在小方格内读到的符号是“L”时,我们的图灵机就执行装载规则,并且规定紧接着下面1个方格里放着装载的地址,也就是要装载哪个小方格。执行的动作是:装载指定的小方格中的数到内部寄存器中。
4.跳转规则;当图灵机在小方格内读到的符号是“J”时,我们的图灵机就执行跳转规则,并且规定紧接着下面1个方格里放着跳转的地址,也就是跳到哪个小方格。执行的动作是:将读头移动到指定的小方格上读取这个小方格中的内容。
5.小于则跳转规则;当图灵机在小方格内读到的符号是“JL”时,我们的图灵机就执行小于则跳转规则,并且规定紧接着下面1个方格里放着跳转的地址,也就是跳到哪个小方格。执行的动作是:根据内部寄存器中的内容,决定是否将读头移动到指定的小方格上,否则不执行任何动作,读头直接顺序移动一个小方格并读取其内容 。
6.比较规则;当图灵机在小方格内读到的符号是“
7.累加规则,当图灵机在小方格内读到的符号是“A”时,我们的图灵机就行累加规则。执行的动作是:将内部寄存器中的值自动加一,读头顺序移动一个小方格并读取其内容。
现在我们的图灵机有了7条执行规则,我马上就用我们的这些规则来描述上面C代码,把我们的C代码转换成我们图灵机能识别的规则。转换的结果如下:
1:
2: 0 //对应于上面C代码中i变量。
3: 100
4: JL
5: 8
6: J
7: 20
8: +
9: 1 //对应于上面C代码中sum变量。
10: 1
11: S
12: 9
13: L
14: 2
15: A
16: S
17: 2
18: J
19: 1
20: STOP
下面我们就把上面这些规则和数据依次放进我们图灵机的纸带的小方格中。最右边的数字代表纸带上小方格的编号,STOP表示图灵机的停机标志,当读头读到这个标志,图灵机就停止运行。
我们来开始运行这个图灵机,看看它是如何完成上面C程序的计算任务的。我们假定图灵机开始运行时的读头R指向的1号方格。
1. 读头R指向1号方格:读出“
2. 读头R指向4号方格:读出“JL”符号,根据上述规则,读头R继续下移数据读取5号方格并根据上一步比较的状态,对其执行跳转动作。因为0
3. 读头R指向8号方格:读出“+”号,根据上述规则,读头R继续下移分别读取9、10号两个方格的数据并对其执行加法动作,结果放在寄存器中。读头R继续下移。
4. 读头R指向11号方格:读出“S”符号,根据上述规则,读头R继续下移读取12号方格的数据并对其执行存放动作。将寄存器的结果存放到9号方格中,因此9号方格中变成了2。读头R继续下移。
5. 读头R指向13号方格:读出“L”符号,根据上述规则,读头R继续下移读取14号方格的数据,并装载1号方格的数据到寄存器中,因此寄存器中是0。读头R继续下移。
6. 读头R指向15号方格:读出“A”符号,根据上述规则,对寄存器执行累加动作。由于第6步中寄存器中保存的数据是0,累加后变成了1。读头R继续下移。
7. 读头R指向16号方格:读出“S”符号,根据上述规则,读头R继续下移读取17号方格的数据并对其执行存放动作。将寄存器的结果存放到2号方格中,因此2号方格中变成了1。读头R继续下移。
8. 读头R指向18号方格:读出“J”符号,根据上述规则,读头R继续下移数据读取19号方格的数据并执行跳转动作。所以读头R跳转到1号方格。又从第1步开始重复执行到第8步。
9. 反复执行第1步到第8步100次后,会发现2号方格中的数据不在小于100,因此JL规则不会发生跳转。读头R继续下移指向6号方格。
10. 读头R指向6号方格:读出“J”符号,根据上述规则,读头R继续下移数据读取7号方格的数据并执行跳转动作。所以读头R跳转到20号方格。
11. 读头R指向20号方格:读出“STOP”符号,由于这个符号表示停机,如果到这里我们图灵就停止了。
啊哈!终于停机了。奇怪吧,如此简单的程序让图灵机去运行却变得如此复杂,因为它是机器不是人,没有人如此高等的智慧。只懂几个有限的规则和动作,傻傻在纸带上蹦来跳去。
你或许发现我们给这个图灵机赋以的规则越多,这个机器的功能也就越强,我们还可以赋以它减法规则、乘法规则、除法规则、移位规则、数据交换规则、各种跳转规则等。还可以在读头里加入更多的寄存器。
时间在向前走,人类从未停止对这种机器的神往。人们不断的尝试着各种电子元件,梦想着有天能做出完成上面这些计算规则的电路和实现那条纸带的电路。终于人们用晶体管配合其它电子元件,做出了加法器、乘法器、除法器、移位器、译码器……最重要的还有存储器。人们又做出了用于控制这些部件的逻辑,比如:什么时间开始访问存储器,又在什么时刻进行控制这些部件完成各自的计算任务。最后人们把这些部件封装在一起,并起了个响亮的名字——CPU。现代CPU无论是通用计算机的CPU还是嵌入式系统的CPU。逻辑上都和下图差不多。
CPU就好像是我们图灵机的那个读头,只不过它比我们的那个读头所具有规则功能要多很多,执行规则的速快也快很多。做CPU的工程师还对这个CPU能完成的所有规则进行统一的编码,并对每个编码写明它是干什么的,比如:完成加法运算、数据传送、数据移位……给这个编码集起了个名字——指令集,最后把这一套指令集交给程序员。程序员用这些指令不同的组合,构建出了各种复杂的程序……
但是我们用一条条指令写出的程序还没有地方放呢,更不谈让CPU去执行了。所以还要有像图灵机那样的纸带。于是人们又做出了存储器,叫内存。也许今天的内存所用的技术和电子元件比以前所用的可能高级好多倍。但是我们从逻辑上看仍然和下图差不多。
内存中的每个位都能表示两种状态,如果你还能想到这两种状态就是我们逻辑上常常说的0和1,那就太好了。内存中8个状态位,组成一个字节,每个字节分配一个编号,叫内存地址,每个以字节为单位的内存单元中保存的电子状态,就是我
们逻辑上说的数据。内存中放的数据可以是常规数据,也可以是CPU定义的指令。CPU主要的任务就是根据指令“改变”这些电子状态,即我们说的数据。“改变”的手段有多种:比如对这些电子状态做加法运算、做移位计算等……
图灵机为什么要让读头能够在纸带上来回移动,原因很简单,当然是为了读取或者改写纸带任意位置上的小方格中的数据。我们要如何才能让CPU读取和写入内存单元呢,总不能让CPU在内存条上移来移去吧,即使这样可以,但是速度和稳定
性实在是太差了。既然只有两个动作——读内存单元和写内存单元。那么我们期望是下面的情况:
1. 读内存单元
1.CPU对内存发出读内存的控制信号,并等待内存的确认信号。
2.CPU发现内存已确认,就发出地址数据信号。
3.内存收到CPU的地址,读出这个地址内存单元上的数据并通知CPU数据已经准备好。
4.CPU通过数据信号线,把数据传送到CPU中。
2. 写内存单元
1.CPU对内存发出写内存的控制信号,并等待内存的确认信号。
2.CPU发现内存已确认,就发出地址数据信号。
3.内存收到CPU的地址,并等待数据信号线上的数据。
4.CPU通过数据信号线,把数据传送到内存单元中
也许实际上比笔者描述的更加复杂,但是比图灵机那种纯粹的物理机械运动要好很多,让地址信号的变化,代替机械运动。于是,一个逻辑上最简单的计算系统就算做好了。
现代的CPU都有专门的指令地址寄存器(IP),这个寄存器里放着下次即将执行的指令的地址,即该指令放在这个地址的内存单元中。假如我们规定一条指令的长度为4个字节,每个执行完一条指令,IP就自动加4。如果是跳转指令就将跳转地址直接放进IP寄存器中,接着读取指令,然后执行指令,如此反复,直到切掉电源。下面我们用一副图来表示这种运行机制。
就这样在IP寄存器的引导下,CPU一条一条的、高速的执行着内存中的指令。时而顺序执行,时而跳转执行,它从来不问,也不知道执行那么条指令后会有什么结果,只是任劳任怨的完成每条指令规定的功能和动作。
不管现代的CPU功能多么强,速度多么快,体积多么小,功耗多么低,但它最核心的机理的实现从来没有跳出图灵的思想。
到这里,你的大脑中应该能呈现出一副计算机如何执行程序的图像。
领取专属 10元无门槛券
私享最新 技术干货