饭饭TXT > 学习管理 > 《直觉泵和其他思考工具(出版书)》作者:[美]丹尼尔·丹尼特/译者:冯文婧【完结】 > 直觉泵和其他思考工具.txt

第 7 页

作者:美-丹尼尔·丹尼特/译者:冯文婧 当前章节:15947 字 更新时间:2026-6-22 18:01

也许你已经看到,减量跳转分支对寄存器机起着至关重要的作用。正是这个指令让计算机(近似)“注意”到了这个世界,继而它开始用这些注意到的事情指导自己之后的“行为”。其实这种条件跳转分支对所有存储程序计算机都是至关重要的。埃达·洛夫莱斯(Ada Lovelace)早在19世纪就认识到了这一点,这从她关于查尔斯·巴贝奇(Charles Babbage)的分析机的精彩论证中就可以看出。而分析机正是所有现代计算机的原型。 (41)

其实,只要掌握一定的诀窍,组合这些程序就会变得轻车熟路。更何况,运算程序一经写成便可以多次重复地使用。现在,我们对各个运算程序进行编号,运算0是相加,1为相减,2是相乘,以此类推。复制可以是运算5,移位是6,等等。随后,当我们再次使用寄存器机时,就完全可以用数字来发号施令了。

练习4(可选)

写出一个流程图及它的RAP程序,用来将寄存器机转变成一台便携式计算器。

a.寄存器2用来执行运算:

0=相加

1=相减

2=相乘

3=相除

b.在寄存器1和3中放入参与运算的数据。

那么“306”就表示“3 + 6”,“513”表示“5 - 3”,“425”表示“4×5”,“933”表示“9÷3”。然后,我们用寄存器4、5、6、7来显示操作结果:寄存器4用于表示符号,其中0代表+,1代表-;寄存器5显示答案的数值;寄存器6用于存放除法结果的余数;寄存器7表示警示,即对输入的内容进行报错,例如除数为0,或者寄存器2中出现了没有定义的操作的情况。

在这个例子中,寄存器中的内容(其中的数字)起到了四种不同的作用:表示一个数字,表示一种算数运算,表示数字的正负号,还有充当报错旗帜。

秘密2

寄存器中的数字会代表什么取决于我们所编写的是什么样的程序。

利用已经创造出的构造模块,我们可以建造出更厉害的操作程序。所以,只要有信心,我们就能写出一个程序并画出它们的流程图,例如:计算出寄存器7中数值的平方;或者是,算出从寄存器1到寄存器20这20个数字的平均数;或者,分解寄存器6中的整数,如果5是其中的一个因数,就在寄存器5中放入一个1;或者是比较寄存器3和4中数值的大小,将较大的内容放入到寄存器5中,但假如它正好大出一倍,就用寄存器7示警。

我们可以写出一个程序,它能检索100个寄存器,找出具有特定内容的那个,并把所在寄存器的地址写入寄存器101中。这是怎么做到的呢?先将那个特定的数字存入寄存器102,然后将它复制一份存入寄存器103。清零101,然后从寄存器1开始与寄存器103中的数字进行减法运算,被减数是寄存器103中的数字,但每次减量前都首先要在寄存器101中增量记录一次。需要找到的是减去特定数字后结果是0的那个寄存器。不是寄存器1就继而再检测寄存器2,以此类推,直到检验出相减结果为0的那个寄存器,这时减法运算停止,该寄存器地址也正好写入了寄存器101。在减量运算时,寄存器能及时“注意到”0的出现,正是这种基本的“感知力”使我们得以将寄存器机的“目光”转向它自身,让它也尝试检测自己的寄存器单元:来回移动其中的内容,在需要的地方切换操作,等等。

秘密3

寄存器中的数字可以表示任何事物,这说明,寄存器原则上也可以处理任何事物,它们可以“识别”所有用数字(包括用一个数字或是用一系列数字)表示的图案或特征。

举个例子,一个大型寄存器库就可以是一张黑白图片(任何黑白图片,例如这一页书的照片),每个寄存器都占一个像素,内容为0时显示为白点,为1时显示为黑点。现在,请书写这样一个程序:可以从成千上万的图片中搜索出一张白底上画有一条黑色水平直线的图片。先不要着急去做。生命很短暂,请先详细地考虑一下,要完成这项任务会遭遇到哪些困难和费事繁琐的步骤。通过想象,如果你真的设计出了水平线识别器、垂直线识别器或者半圆形识别器,那么,再请思考一下该如何将它们与另外几个有用的识别器融合起来去鉴别出一个大写的A,包括它成百上千种的字体。光学字符识别(OCR)软件可以通过扫描,把页面上的图形非常准确地转换成一个计算机文本文件,这无疑是近代计算机程序的一次胜利。其中,每个字母或者数字符号都由一个美国信息交换标准码(ASCII)中的数字表示,所以我们可以搜索文本,还可以完成那些神奇的文字处理工作。利用的不是别的,就只是算术。那么OCR真的可以读懂些什么吗?不然。它并不理解摆在它面前的东西,它只是近似在读录。但对于我们那收纳丰富、装满了可动部件的工具箱来说,这无疑也是件不错的工具。

秘密4

既然一个数字可以表示任何事物,那么它就一定能表示一条指令或是一个地址。

寄存器中的数字可以表示指令,例如增量、减量、移动或者搜索;也可以表示地址,即寄存器在计算机里的地址。所以,我们可以用一连串的寄存器来存储一整套指令。现在,我们有一个主程序:程序A,它让机器通过一个一个地执行寄存器中的指令来完成要求的任务,然后我们可以在这些寄存器中存入另一个程序B。在我让机器运行程序A的时候,它首先要做的事情是去访问这些寄存器,而这些寄存器发出的是程序B的指令,机器随即执行。我想要指出的是,其实我们可以在寄存器机中央处理机中的一组寄存器中一劳永逸地保存下程序A,使其成为烙印在只读存储器上的“固件”,然后再利用它去运行程序B、C、D,等等。正是因为在寄存器机中设置了这个程序A,我们便把寄存器变成了一台存储程序计算机。

有了程序A,寄存器机才能忠实地执行我们通过数字输入寄存器的各项指令。寄存器机所运行的每一个程序都是由一连串数字组成的,它们顺次排列,程序A会依次访问它们,完成每个数字指定的任务。如果我们设置一套系统使这些指令互不混淆,比如,给每一条指令都以两位数字来命名(或任何相同位数的数字),那么我们就可以将构成程序B的一系列指令,例如,

86,92,84,29,08,50,28,54,90,28,54,90

改写成下面这一大串长数:

869284290850285490285490

这一长串数字既是程序B特有的“名称”,也是程序B本身,它需要通过程序A一步一步完成执行。这里是另外一个程序:

28457029759028752907548927490275424850928428540423

另外还有:

8908296472490284952498856743390438503882459802854544254789653985

比较有趣的程序常常会有更长的名称,由成千上万个数字构成。你笔记本电脑里的那些程序,比如文字处理软件或者浏览器,就是由如此长串的数字写成的,通常由几百万个二进制数字构成。10兆大的一个程序写出来基本就是800万个0和1构成的一长串数字。

秘密5

所有可行的程序都能由一个单独的数字指代,都可以被看作是一串指令,等待通用万能机去执行。

杰出的理论家、哲学家阿兰·图灵想出的是另一种简单的假想机。它在被分成一个一个小方格的纸带上来来回回、嘎嘎作响,根据磁头在当前小方格中读到的数字,即0或者1来决定自己的动作(啊哈,这就是条件分支)。图灵机的工作就是轻轻地敲出比特(清除0把它改写为1,或者相反)或者保持比特不变,然后将纸带向左或者向右移动一个方格,进入下一指令。你可能会认为,仅靠一次移动一小方格,用二进制数字0和1而不是自然数0、1、2、3、4、5等来编写图灵机相加、相减程序或者运行其他功能,这听起来,比我们之前寄存器机的练习还要让人头疼,但事实上,两者的运行原理完全一致。带有程序A的图灵机就是通用图灵机,它可以从纸带上“读取”程序B,并利用纸带上任意位置的数据或输入来执行程序B。我们可以把王浩的寄存器机执行程序的过程分解为数字演算和按条件跳转,图灵的图灵机也是如此。两种机器都有着很强的提取程序数字名称并执行它的能力。我们要建造的是一个自带程序A的多用途通用机,而不是建造成千上万个固定不同功能的计算机器,让它们分别去执行各自复杂的任务。对于这台通用机,我们只要喂食各种程序(软件)它就能按要求办事,这就是虚拟计算机。

换句话说,通用图灵机就是一部通用拟态机。尽管名气稍逊,我们的通用寄存器机也是如此。你的笔记本电脑也如此。笔记本电脑能做的事情通用寄存器机也一样能做,反之亦然。但是先别大惊小怪,我并没有说两者有相同的运行速度。我们已经看出,寄存器机在处理某些问题时极其缓慢,例如在进行除法运算时,它需要很费劲地一遍一遍地做减法,我的天哪!就没有什么方法来提速吗?当然有。事实上,自图灵机出现以来计算机的整个发展进程,专家们所做的一切就是要让寄存器机能做的一切工作变得高效起来,其他别无所求。

秘密6

自图灵的假想纸带机之后,计算机所有方面的改善,提高的都只是运算速度。

例如,冯·诺依曼为第一台严格意义上的可操作计算机所设计的那种架构就是为了提高机器的运算速度。他拓宽了交流窗口,让图灵机的磁头从一次只读取1比特变成了一次读取多比特。早先的一些计算机可以读取8比特或者16比特的“字”,有时甚至还可以读取12比特的字。而今天,一次读取32比特已经司空见惯,虽然这也成为了一个瓶颈(即所谓的冯·诺依曼瓶颈),但与当年的图灵机瓶颈相比,它还是宽多了。简单说来,字会从存储器复制到特定的寄存器(指令寄存器)中,一次复制一个,在那里再接受读录和执行。每个字通常由两部分构成:操作码和地址。前者如相加、相乘、移动、比较、遇零则跳转,后者则告诉计算机所要操作的内容在哪一个寄存器。所以,10101110 11101010101就表示,对地址为11101010101寄存器中的内容执行10101110操作,运算结果会显示在一个叫作累加器的特殊寄存器里。

寄存器机与冯·诺依曼机之间存在着一个巨大的差异:在寄存器机上,你可以在它的任何一个寄存器中进行操作,当然,只能进行增量和减量运算;而在冯·诺依曼机上,所有的算术操作都是在累加器中实现的,寄存器只负责存放复制、移动或者储存过来的内容以构成内存。硬连线可以单独完成各自的基本操作,这能省掉很多额外的移动和复制步骤。也就是说,有专门的电子路线负责加法,也有专门的电子路线负责减法,还有的负责遇零则跳转等等。操作码很像是区号或者邮编:指令要送到正确的地方才能执行。软件和硬件就这样相遇了。

今天的实体计算机中会有多少种原始操作呢?几百或者上千个?或者回归了旧时的美好,成了一台精简指令集计算机(Reduced Instruction Set Computer,即RISC),运作的几乎全是原始操作,但条件是,它们必须得以闪电般的速度运行。要想单纯用增量和减量指令去替代相加硬连线,并在速度上领先,条件是:对于运行步骤小于一百万步的加法运算,增量和减量指令的运行速度要比相加硬连线的运行速度快一百万倍。

那今天的实体计算机中有多少个寄存器呢?数百万甚至数十亿个,但它们每一个容量是有限的,因此那些巨大的数字需要分散储存到许多寄存器中。一个字节是8比特,如果你电脑上有64兆字节的随机存取存储器(RAM),那么你就会有1 600万个32比特的寄存器或者是其他规格的等量物。我们已经知道,寄存器中的数字能代表的并不止是正整数。像π、还有1/3这样的实数是由“浮点”系统来保存的,它将数字拆分为底数和指数两部分,就像科学记数法那样(“1.495×1041 ”),使计算机除了运算自然数以外还可以近似运算其他数值。浮点运算是以浮点数作为数值的算术运算,突出运用在乘法和除法中。20年前(这一章首版时),我们能买到的最快的超级计算机每秒钟可以完成超过400万次的浮点运算。

如果你还想要更快的速度,可以尝试将多台机器并行的方法,让它们在同一时间一起工作,而不是一个做完再交代给下一个做的串行式。并行机能做的事情与串行机大致相同,只是在速度上,它会略胜一筹。事实上,在过去的20年中,专家们一直都在致力于研究的并行机,大多是在标准的非并行冯·诺依曼机上实现的虚拟机。专用并行硬件开发出来以后,计算机专家们一面要忙着测算拓宽冯·诺依曼瓶颈所需的成本与最终获益,一面又要想方设法地提高数据传输速度,不惜调用协同处理器、高速缓冲存储器以及各种各样其他的途径。今天,日本富士通公司的超级计算机K-computer已经可以运行实现每秒超过一亿亿次的浮点运算。

这个速度基本上要与你大脑的即时运算速度旗鼓相当了。你的大脑是一台最卓越的并行处理机,包含着大约一千亿个神经元,每个神经元都相当于一个复杂的微型行动者,有着各自的行动规划。视觉“神经”有数百万神经元宽度的通道,单凭自己就可以把眼睛看到的视觉信息传入到你的大脑里。但神经元运行起来比计算机电路慢得多了,一个神经元在几毫秒(一毫秒是千分之一秒,不是百万分之一,更不是十亿分之一)之内可以转换状态并发送一次脉冲(可能是它们的增量或减量指令),而计算机使二进制数字移动的速度几乎接近光速。所以说,让机器更快速运转的一个至关重要的方法是要将它们做得更小。光走一尺大致需要十亿分之一秒的时间,如果你想让两个程序之间的通讯比之前更快,那就让它们离得更近些吧。

秘密7

没有再多的秘密了!

也许计算机最美妙的地方就是,它由各部分(操作)简单地组合而成,而各部分本身也很简单地组合,简单到没有什么地方是无法给出解释的。这里没有仙气,没有“形态共振”(morphic resonance),没有无形的力场,没有未知的物理定律,也没有那些神奇的组织。要知道,即使你用计算机成功地模拟出一些情景,完成这一切的也只是一些算术运算而已。

目前流行的量子计算机是什么呢?量子计算机能做普通计算机不能做的事情吗?算是,但也不完全。得益于微妙而奇特的“量子叠加”态,那些不被观测的物质能够同时处于“所有可能的”量子态上,直到观测导致“波包塌缩”。这个性质使量子计算机可以同时计算大量数值,解决诸多问题。(要想了解更多,请查阅你最喜爱的那些物理科普书籍或网站。)基本上,在计算机的提速进程中,量子计算机的发明是最新一次的重大变革,可以说,在提高计算机的处理速度上,它实现了一次巨大的突破。图灵机沿着它的纸带喳喳作响,寄存器机奔波于各个寄存器之间不停地增量减量,但时间紧迫,在几分钟、几个小时或者几天内,它们能做的工作也总是显得那么有限。日本富士通公司的超级计算机K-computer数万亿倍地加快了运行速度,但还是不能解决所有的问题,这在密码学中体现得尤为突出。量子计算机本该弥补这部分的缺陷,只可惜,我们还没有克服工程上的诸多难题,没有让它变得更加稳定、更加实用。就目前来看,每秒钟运行一千万亿次的浮点计算就已经是天方夜谭了。

25

虚拟机

模仿硬件运行的计算机程序

般来说,实体机器由可活动的物质部件构成。设计者们常常会根据其功能去命名它们,如:割草机、开瓶器、咖啡研磨机。它们的形状设计和构造各不相同,利用的是不同的物理定律。但它们也有共同之处:拥有相同名字的机器都在某种程度上做着与名字的描述相差无几的工作。它们基本上都能满足使用者的需求,可能有些会稍微好用一点儿。有私家庭院的人会挑选一款噪音相对较小的割草机,小咖啡馆的老板则更需要那种能精确研磨出不同粗细粉粒的咖啡机,尽管它操作起来非常复杂。有些机器是多功能的,比如一把电钻,插上不同的附件就能变成一把电锯或是一部打磨器。

计算机与这个类似,不同的是它不仅能按要求做几件不同的事,它甚至能做成千上万件事。而且,它也不需要插上不同的附件,你只要点开不同的程序,即点开一串串由0和1组成的超长数字,就能让计算机内部实现一些必要的切换,从而转换成不同的装置去完成不同的工作。每套装置系统都是一台单独的小机器,没有齿轮、轴承、电线或者滑轮,这就是模拟机,“构成它们的是指令”。不像和面团、压纸浆或者造钢坯,计算机处理的只有信息。

所以,对于计算机来说,指令就是那些齿轮和滑轮。所有的信息都要转换成二进制代码0和1,它们是计算机能够和需要“读录”的唯一代码。印在芯片上的电路会数以万亿计地分流0和1这些代码,逻辑门不断地开合,将信息流输送到这条或者那条电路,并以这种方法来下达指令。计算机上唯一“可移动的部件”是硬件上数百万的小空位,它们可以在1态和0态之间任意切换。至于计算机在某一时刻具体是一台具有何种功能的机器,则取决于数千个至数百万个微型元件的设置。

若一台实体机的各部分相互配合可以呈现许多不同的状态,那它就具备极强的可塑性,只需要在其上施加一套特殊的指令,就能把它变成一部虚拟机。由于虚拟机处理的是信息,那么它就能够与实体计算机做相同的工作:后者中的“可移动部件”只是硬件中的状态变化,而前者可以直接操纵状态变化来代表那些“可移动部件”。这也不难理解:你可以在一张纸上用铅笔演算一个长除法,但如果足够熟练,你也可以“在头脑中”进行这种运算,即通过想象,在脑海中的纸张或者黑板上书写过程。两者都会收到很好的效果,原因是,在面对这类情况时,我们想要得到的答案只是一种信息。相比之下,想象出的一个火腿三明治是不能用来充饥的,你得真的去做一个。计算机十分擅长在“头脑中”处理信息事务,所以我们有时真的分不清自己在使用和操作的到底是一台处理专项信息的“专用”“硬连线”机,还是一部在多用途芯片上运行的虚拟机。

今天,几乎所有的升降机、空调,还包括你的汽车、电冰箱和电视机遥控器里,都有那种微小、廉价的计算机芯片,它们实际上就是一些多功能计算机,能运行你笔记本电脑里操作的所有程序。但我们却选择让它们倾其一生只运行刻录在只读存储器上的一个简单的程序,将它们所有杰出的能力都封锁在那一两个技能上,例如点火控制或者周期除霜,等等。原因是,比起去制作那种只负责专项工作的专用芯片,这样做的成本要低多了。

虚拟机的概念是计算机科学中出现的一个最有用的想象支架,在计算机界,这早已人尽皆知。现在,到了将它引入其他领域的时候了。因为要从广义上使用虚拟机这个词(我会在适当的地方说明原因),所以我们有必要先了解一下它最初的意思,也就是说它本身的意思。“虚拟机”是计算机科学家杰拉尔德·波佩克(Gerald Popek)和罗伯特·戈德堡(Robert Goldberg)在1974年引入的一个概念,它最初表示的是“实体机的一种独立有效的复制品”,即各种指令的一种复制品。实体机是实在的硬件,由硅芯片和电线之类的东西组成,我们称它为A。而虚拟机是运行在另一台实体机B上的计算机程序,它能完美地模拟硬件A:速度可能会慢点,毕竟它要首先保障自己硬件上的基本操作有效运行,才能组织起硬件A上的那些操作,但的确都是同样的程序。如果你写了一个在硬件A上运行的程序,那么硬件B在运行模拟硬件A的虚拟机时,该程序应该能在硬件B上畅通无阻地运行。

这是一个非常有用的方法。部分原因是,它显然可以让一些资源免于浪费:假设这里有很多只能在Mac上运行的软件,但是如果你没有Mac,它们就无法使用。这时,如果可以在PC上编写出一个模拟Mac的虚拟机,然后成功运行,那么你电脑上的这些Mac软件就都可以运行了。你的电脑会“假装”认定它是真的Mac,而那些软件则根本就浑然不知!试想一下,一个人胳膊骨折了,然后打上了石膏。石膏可以让骨折处不再活动,而它的重量和形状会让受伤者身体其他部分的动作产生一些相应的调整。现在,有个小丑想要模仿这个手臂上打着石膏的人,如果他技艺足够精湛,他就一定会让自己的肢体行动做出一些相应的调整,“显而易见”的是:他胳膊上打着石膏呢!如此说来,那些运行软件和我们这些旁观者可能就真的看不出此时是一台在运行Mac虚拟机的PC还是一台真正的Mac。

但现实与上面的例子却多少有些出入。虽然人们已经开发出了PC上可运行的Mac虚拟机,但据我所知,这个软件并没有宣传的那么可靠。另一方面,Mac却已经有了一个简单易行又非常可靠的虚拟机,它可以运行PC的操作系统Windows,借用它,Mac持有者可以运行所有自己喜欢的Windows软件。目前,绝大多数编写出的程序都需要专门的操作系统而不是专门的硬件,而相同的硬件上可以运行不同的操作系统。所以,虚拟机的概念还需要继续扩展,还应该包括对操作系统的虚拟模仿。一种操作系统本身就是一部虚拟机,它能让一个程序在略有差异的硬件上同样有效地运行。但一个操作系统毕竟只是一种软件,并没有模拟任何实际存在的硬件,它只是按照实际要求创建出虚构的机器,遵守相关的规定、接收特定的输入等等。

Java虚拟机(Java Virtual Machine,即JVM)已成为当今最流行的一种虚拟机,这是我们扩展虚拟机概念的又一个原因。Java虚拟机不是对任何一种实体硬件机器的模仿,它只作为软件机而存在,比较像一个操作系统。可以说,是Java的出现让今天的因特网变得如此丰富多彩。有了它,你可以从网站上下载一些小程序(Java的应用小程序),让你能够填纵横字谜、玩数独、探索地图、放大照片、与世界各地的玩家一起参加探险游戏。当然,它也能处理很多“严肃”的事务。用Java语言编写程序的网站不需要在意浏览网站的用户手上拿的到底是PC、Mac还是运行Linux操作系统的计算机。因为Java程序只在Java虚拟机上运行,而PC、Mac以及Linux操作系统上都有各自专门的Java虚拟机,匹配的Java虚拟机会自动下载到你的电脑上,几秒钟便可以安装完成。然后,各种Java小程序就都能运行了,犹如魔法一般。可能你有时会注意到Java更新正下载到电脑上,也可能你注意不到。理想状态下,你不用记住自己电脑上安装的是哪一款Java虚拟机,随意浏览网站就可以了,有时网站上的Java小程序能顺利在你的电脑里运行,而不能运行时匹配的Java升级软件就会自动下载安装,以确保能够继续运行。

从我说的这些虚拟机的扩展意义出发,我们可以看到,几乎所有的计算机程序都是虚拟机,因为它们都是由一系列指令构成的软件,一旦开始运行,就将通用计算机转变成了专用计算机,而这种专用计算机本可以专门设计并建造成专门的硬件。“通用”计算机,即今天我们所说的通用图灵机理念的提出,是阿兰·图灵为科学、也是为20世纪中期以来的人类文明做出的一项最杰出的贡献。只需要在上面简单地安装和运行程序,我们就能把它转化成任意一种功能清晰的计算机!(为了防止你想跳过这一部分,我在24章中已详细介绍过了。)你不用再去组装其他的硬件计算机,有一台就足够了,剩下的事情就交给软件吧。图灵机时代的开启让我们实现了一个非凡的想法:只要有一个大块头的硬件,在里面安装无数可塑可调的“记忆”盒子或者寄存器,把需要的指令放进这些记忆盒子里,让它们运行,这样你就把这个大块头的硬件变成了你想要的任何一种计算机。

图灵机,或是笔记本电脑,只能一条一条地执行指令。我们在这个基础上扩展出了“并行”计算机的概念,它可以一次执行多条甚至数百万条指令。硬件中的任何一个地方,只要它们本身保持着一种状态,收到指令后会变成另一种状态或者不变,那这个地方就是寄存器。这种状态可以是你电脑中一个比特的0态或1态,但不限于两种状态。以这种状态为基础,任何寄存器系统都可以完成一些特定的基础操作。例如,通过让寄存器由一种状态变为另一种状态,或者用寄存器的某一种状态判定下一步的操作,寄存器系统就可以按照“计算一个函数”或者“运行一个程序”来配置自己的各个寄存器。所以任意硬件上都可以运行利用了上述基本步骤的虚拟机。一种技巧可以使用一次就可以使用第二次、第三次、更多次,在硬件上部署一部虚拟机,虚拟机中可以再部署一部虚拟机,在其中再部署一部虚拟机……

请考虑一个下国际象棋的程序,它由Common Lisp这种高级计算机语言写成,在一台PC上运行的Windows7操作系统上运行。事实上,这台PC佯装成了一台Windows机器,这台Windows机器又佯装成了一台Lisp机,这台Lisp机又佯装成了一台国际象棋游戏机。对于那些谙熟计算机或者国际象棋的观察者来说,要是从最上面一层观察的话,这个程序的诸多细节还多少可以理解(“啊哈,这个子程序生成了所有移动“象”的合法走法,然后调用另一个子程序来评估每一步……”)。然而,要是让他们去看这个程序实际的机器代码,看着那一串串0和1被吃进硬件的指令寄存器,这倒十足成了一个让人抓狂的好办法。所以,还是让我们把注意力放在那些较高的层次上吧。

其实,无论在哪一层次上,我们都只能只见森林不见树木,较低级层次上的那些细节总是很容易忽略。计算机上虚拟机的层层嵌套和关乎心智的小人儿功能主义中小人儿的层层嵌套,两者之间的雷同并非巧合。虚拟机推动着人类的创造和理解,实质性地完成了一些至今都超越我们想象的任务,比如完成航线预定、下象棋、预测天气、做记录等等,这些惊人的成就鼓励着我们:如果对大脑也采用类似(仅仅是类似)的技巧来实施逆向工程,或许我们也能得到一些收获。

在面对“说法语的人的大脑之间的相似性”这个问题时,排除解剖学上那些显著的差异,也许利用虚拟机的例子能为我们展示出一种最好的解释:所有说法语的人都具备某种版本的法语虚拟机,它藏身于他们大脑数以亿计的寄存器中,是一套由细微的习性和连锁装置构成的系统。说英语的人的大脑里同样也有这么一套固定模式的系统,即英语虚拟机。当你对一个法国人说,“Donnez-moi le sel, s'il vous plait.(请把盐递给我)”你基本上就能预见到法语虚拟机会作出的反应,因为这种反应与你,这样一个说英语的人为自己脑中的英语虚拟机输入“Please pass me the salt.(请把盐递给我)”时所给出的反应是一致的。但现在的问题是,要怎样在人类的大脑中建立起一个法语虚拟机或者英语虚拟机?

人们在下棋或是在说法语时,大脑各个不同的层面上到底都发生了一些什么样的活动,到目前为止我们还不得而知。 (42) 毫无疑问,我们对此给不出什么精确的描绘,不像是编译器,这种程序可以将高层指令翻译成可在硬件上运行的代码,只要计算机程序员能在最高层面上设计出一个想法,它就有把握吐出一个可执行的程序来。这里出现了一个非常重要的概念验证:至少我们找到了一种方法,它能够清楚地道出“具有数万亿活动部件的计算机,它的那种高等能力到底是什么”,而无需动用神奇组织。

26

算法

有效解决问题的策略机制

曾在《达尔文的危险观念》中提到过,达尔文的那些伟大观念想要引出的是:

地球上的生命经由这种或者那种算法,在上亿年的时间里谱出了一棵布满枝桠的生命之树。

那么,准确来说什么是算法?事实上,这个概念有很多相互抵触的含义,而我取的是最广义上的那种理解。以下内容是我从《达尔文的危险观念》中摘录出来的,略有修改。

是达尔文发现了算法的力量。算法是一种特定类型的形式步骤,只要可以“运行”或者开始具现化,它就能合乎逻辑地产生特定类型的结果。其实,即使是在达尔文时代,算法也并不十分新颖。很多我们熟悉的算数运算,例如进行一个长除法或者结算支票簿,都是算法。另外,像走出井字棋的完美一步,还有按字母顺序排列单词时使用的那些判定程序,它们也属于算法。数学和逻辑学界对算法的本质和威力的理论反思出现得则相对较晚,它们是20世纪发展的产物,让我们开始重新认识达尔文的发现,直接关系到了计算机的诞生,而计算机的出现也让我们对算法的威力有了更加深刻、更加生动的理解。

要查看算法的词源,我们得向前追溯:经由拉丁文的algorismi到早期英语的algorism,之后又错写为algorithm的现代形式。再往上,算法这一术语其实得自于波斯一位数学家M?us? al-Khow?rizm的名字,他在9世纪写过一本关于算术步骤的书,到11世纪,巴斯的阿德拉德(Adelard of Bath)或是切斯特的罗伯特(Robert of Chester)把它译成了拉丁文。数个世纪以来,算法这个概念表示的一直都只是那种万无一失甚至略显“机械”的程序过程,20世纪30年代,阿兰·图灵、库尔特·哥德尔还有阿隆佐·丘奇(Alonzo Church)这一代人经过了一系列创始性的工作,才多多少少确立起了如今人们对这个概念的一些理解。对我们来说,算法的概念有这样三个至关重要的特征,其中每一个总结起来都有些难度:

(1)底层中立:不管你用的是哪种符号体系,也不管你用的是铅笔还是钢笔,纸张或是羊皮卷,霓虹灯或者空中文字,它们都不会影响到你运算长除法的过程。运算程序依据的是逻辑结构,各种物质材料的因果力量也许能为运算保驾护航,但它们不直接参与运算。

(2)潜在无头脑:过程的整体设计可以非常华丽,结果也可能十分耀眼,但算法的各个既成步骤,包括步骤之间的过渡却出奇地简单。有多简单?一个听话的白痴或者是一个简单的机械装置就能执行。教科书中这样说:算法就像是为新手厨师设计的烹饪说明。一本写给主厨的料理书上可能多是这样的措辞:“放入葡萄酒适量,将鱼煮熟。”而转换成算法之后,它会变成这样:“取一瓶标签上写有‘干’字样的白葡萄酒,用螺旋开瓶器打开,把瓶中的酒倒入锅底至3厘米深,然后打开锅下的燃炉旋钮调到高火,……”整个过程分解得相当仔细,最后都落实到一个个简单得不能再简单的小步骤上。你只需要充当一部食谱阅读器就可以了,不需要做出什么明智的决策或是精准的判断,也不需要发挥直觉。

(3)结果有保障:不管什么样的算法,只要不出意外,它就能每次都按照同样的步骤运作。一种算法就是一份简单的、不易有出入的食谱。

显而易见,计算机就是靠这些特点成就的。每一种计算机程序都是一套算法:根本的组成元素是一些简单的步骤,它们的运行过程惊人地可靠,而运行机制却十分简单。电子电路是计算机常用的选料,虽然电子总是在硅芯片上飞来飞去,但计算机的能力却与它们的因果特性完全无关,运算速度除外。同样的算法,用在使用光纤分流光子的设备上就可以完成得更快些,而通过人力用纸和笔来计算就会变得很慢很慢。

达尔文发现的不是一种算法,而是一大类相关的算法,只是他还没办法对这些算法做出明确的划分。

27

让电梯实现自动控制

机器取代人类的逻辑

离开这段有关计算机的插曲之前,我还想再多介绍另外几个有用的概念——源代码、注释和目标代码,它们对于我们探索“大脑怎样产生意义”这个问题有很重要的作用。一般来说,比起直接去解决一个智力难题,先研究透彻一个简单纯粹的相关实例、抓住概念的一些实质对我们来说可能会更有帮助。在人工智能领域,这种简单实例叫作“玩具问题”。先解决掉玩具问题,顺势再让我们去处理现实中那些恼人的大问题吧!下面所讲的是一个有关升降机操作员如何被计算机芯片取代的故事,我是为了简单纯粹才编造了这个故事,但这一点儿也不影响它的现实性。

在我年轻的时候,那时候升降电梯里还需要有操作员,他们每天的工作就是跟随电梯上上下下,在需要的楼层停下机器,让乘客们出入。在那个年代里,他们操作的是一根很奇怪的手柄,手柄以顺时针或者逆时针方向转动,这样就能使电梯上升或者下降。能让电梯准确地停靠在某一楼层是需要技术的,那时的电梯总是停得要比楼层地面高出或者下陷三五厘米,所以在乘客们出入时,操作员不得不提醒大家要多注意脚下。先到哪一层,到了要说些什么,还有怎样开电梯的门等等,这些都是有规矩的,操作员们要接受训练,记住这些规矩,不断地练习操作,直到把这些规矩都彻底变成了他们的第二天性。规矩本身是发明者们在设计过程中经过多年的苦思冥想才制定出来的,期间还会进行大量的微调和改进。我的设定是这样的:这种电梯的设计已经基本固定,设计师们留下了一本详细的操作手册。无论什么人,只要按照手册正确操作就一定能成为优秀的电梯操作员。

现在,让我们来想象一下,要是操作员所有的工作都能由一个简单的计算机程序来完成,情况又会是怎样?事实上,随着各种自动机械的发明不断出现,纯技术性工作已经开始与人工操作发生分离,我们想象的这种情况也早已逐步实现,但现在我们的设定是,电梯从人工操作到彻底地实现计算机系统控制是一步到位的。

我们猜想,电梯制造商们会召集一批软件设计师,也就是程序员,把操作员一直使用的操作手册交给他们:“这里有我们想要的产品规格。请根据书中的操作规则设计一个电脑程序,我们要求它能实现最棒的人工操作员所有的操作。”程序员们开始仔细对照这本操作手册,并列出了一个包括所有操作的清单,以及在哪些情况下操作必须执行或者禁止执行。在这个过程中,程序员可以避免手册中出现的那些不规范的情况。举个例子,如果给电梯安装上传感器,那么它以后就会在需要的层面准确地停下来,再也不需要操作员们像以前那样要根据不同的情况说“请向上迈步”或“请向下迈步”,升降机每次只需要简单地播放一句录音“第××层到了,请注意脚下”即可。那么接下来,需要程序员们去做的是要用一种名为伪代码的语言写出这个程序的草稿。伪代码是介于人类日常语言和更严格、更系统的源代码语言之间的一种混合语言,一串伪代码写出来大概是这个样子:“如果呼叫楼层>所在楼层,那么上升,直到呼叫楼层=所在楼层即停止;开门。等待……”

如果伪代码条理清晰,能够达到想要的操作效果,那么就可以把它转译成源代码了。源代码是一种更加规范严密、更加结构化的操作体系,而且,它还包含了各种术语的定义,例如变量、子程序等等。人类其实可以轻松地破译源代码,毕竟源代码也是我们写成的。一旦找到突破口,你就会发现,电梯操作手册中的那些规定和术语在源代码里表达得清清楚楚。而且,源代码下面的这两个特点还会让这个过程变得更加简单:一、变量和运算的名称直接显示它们自身的作用,例如呼叫楼层(callfloor),总重量(weightsum),报楼层(tellfloor),等等;二、就像我们在第24章中已经说过的,程序员们可以为源代码添加注释,这些附加说明能让其他人明白他们写这些代码的目的,弄清楚代码各个部分的功能。你在编写一个程序的时候也应该添加一些注释,防止自己忘记当时设计这串代码时的用意,在重新对程序进行纠错时,这些注释也会相当有用。

源代码必须严格按照语法编写,所有的构成元素和标点符号都要写在正确的位置。只有源代码书写格式完全正确,编译器才能读录,然后将它们以目标代码的形式转化成基础的操作序列,供实体机或是虚拟机执行。我们不能要求编译器从源代码中揣测程序员的意图,源代码必须让编译器准确知道该执行什么操作。不过,编译程序有能力利用多种途径去执行操作,它还会根据具体情况选择最有效的一种。编译器也有好有坏,如果将同一个程序的源代码输入到两个不同的编译器中,它们得出的目标代码程序很可能就会以不同的速度在计算机上运行。

举个例子:你编写了一个下棋的程序,然后将它的源代码输入到两台不同的编译器中,得出了两个版本的操作程序。将这两个程序输入同一台电脑让它们对抗。你会发现,尽管这两个程序“按照同样的顺序做着同样的思考”(当然会这样,因为它们源自相同的源代码),但也会分出胜负,因为两者的速度不一样,快的一方使用的基础计算周期较少,在有效的时间内它能预想得更多、更周全。

回到我们的电梯上来。编译器编译出目标代码以后,就生成了一个可执行文件,文件名中通常有扩展名“.exe”。这个文件就可以在虚拟计算机上运行了。也许还需要几经调试,比如回到源代码进行微调,甚至重写,它才能最终成为“完成”版的程序。我们将这套目标代码“烧”到一块小芯片的只读存储器上,而这块小芯片上通常带有一个通用计算机及其之上的很多层虚拟机,然后,我们将这块芯片安装到电梯上。安装时芯片的输入端要与传感器连接,传感器能接受到按钮发送的信息和电梯测量到的乘客们的总重量等;芯片的输出端连接的是效果器,它可以指挥电机转动来开门闭门、升高或者降低箱体,更新显示器,播放录音等等。

再见了!机器已经彻底把真实的人类取代了,而不只是取代了一个我们塑造出来的小人儿,它们就跟人类操作员一模一样,遵守着同样的规则。真的是这样吗?果真如此?好吧,但事实并不如此。它们只是近似于遵守着同样的规则罢了。这是一种很经典的中间状况,它既不像人类那样,能够逐字逐句地把规则保存在大脑中,然后随时查阅那些规则从而指导自己行动;也不同于那些行星,总是能不紧不慢地“按照”设定好的轨道运行。我们人类也常常处于这种中间状况。有时,通过不断训练一些显性规则就会内化或者变成一种惯例,我们甚至都忘记了它们,不自觉地就把它们丢弃了。比如,一般来说英语单词中字母“i”都会位于字母“e”前,除了这两种情况:一种是,前面的字母为“c”;另一种是,在“neighbor”和“weigh”中,发音近似于“a”时。一些还处于调试阶段的规则也似乎是可以遵守的:例如英语的语法规则,它对语言学家们来说一直是一个挑战。时至今日,他们费尽周折也没能写出一本标准的规则手册让我们把英语说得更好,而事实上,几乎每个十岁大的母语是英语的孩子都能调试建立起一些相当不错的目标代码,供他们脑中的英语虚拟机使用。 (43)

在结束这一章之前,我还想提醒大家一点:植入到源代码中的那些注释可以帮助程序员们去理解软件中相互啮合的各个部分的意图,但是,在我们为了方便描绘人类大脑所创作出的那些硬件、固件和软件中,它们却并不出现。自然选择在我们的大脑中安装的那些功能结构就像是一些没有注释的代码,这些功能是有目的的,但由于代码并没有对结构的目的有所注释,大脑也就无从理解这些意图了。(在第40章中,我们会有更详细的讨论。)在发育和学习的过程中也存在着未加注释的调整或者是未说明意图的调整。就像是语言学家们那样,我们还在奋力实施着逆向工程来破译所有那些“规则”和“程序”,虽然它会比用逆向工程目标代码来复原源代码更有难度,但理论上的确可行。

目录
设置
设置
阅读主题
字体风格
雅黑 宋体 楷书 卡通
字体大小
适中 偏大 超大
保存设置
恢复默认
手机
手机阅读
扫码获取链接,使用浏览器打开
书架同步,随时随地,手机阅读
首 页 < 上一章 章节列表 下一章 > 尾 页