购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

24 计算机施展魔法的7个秘密
部分之和大于总体的最好实例

计算机隐藏的那种巨大能量一定会被几个世纪之前的人们看作神迹,看作“真正的魔法”。不过,尽管很多计算机程序都复杂得吓人,但它们全都是由一些基本的步骤组成的,仅凭几个简单的术语我们就能把这些步骤解释清楚。计算机上的运算从不夹杂任何神秘成分,作为思考工具,这正是它的重要价值之一,而且解释这件事儿本身就是一个很有趣的哲学问题。到底计算机是怎样施展它的“魔法”的?能在一个比较基础的层面上理解这个问题非常必要,这一章会提供给我们一些相关启示。

先从一台我们能想到的最简单的计算机——一台寄存器机开始吧,看看它有什么能力以及为什么它会有这样的能力。然后,我们再去考察图灵机和冯·诺依曼机。其实它们也只是一些更高效率的寄存器机而已,笔记本电脑能做的事情寄存器机也都能做,只是你得用它算上几个世纪。之后,我们才能慢慢地搞清楚,更高级的那些计算机“架构”如何在寄存器机这台本体机上增长了速度、壮大了技能,而人类大脑正是其中最重要也是最有趣的一种架构。

等一下,我从没说过“大脑就是一部庞大的计算机”,没有,一次都没有。我想说明的是,如果大脑真的是一部庞大的计算机,那么我们就一定能够找到一种方法将大脑所有的活动都解释清楚,不留下任何神秘色彩。我们采用的这种方法就是逆向工程:探究一个复杂的系统,分析它能做什么,是怎样做到的。逆向工程让我们知道了,心脏是如何像一个泵那样履行自己的职责,肺又是怎样收集氧气排放二氧化碳的。神经科学是逆向工程应用于大脑研究的一种尝试。我们都知道大脑能做什么:预测、支配、记忆、学习,接下来还需要我们搞清楚的是,大脑是怎样做到这些的。

这是个充满争议的话题。2000年,小说家汤姆·沃尔夫(Tom Wolfe)曾在自己的文章《抱歉,你的灵魂已死》( Sorry, But Your Soul Just Died )中详细描述过这一敏感话题,随即便引起了一场激烈的论辩。如果我们不想把时间都浪费在那些雄辩和谴责上,而是要真正探索这个艰险的领域,我们还需要一些更锐利的思考工具。我们的大脑是否真的具有并利用了计算机永远都无法企及的那些神奇能力呢?要想有根据地解决这个问题,我们首先得知道计算机都能做些什么,它们是怎么做到的。而要想圆满地证明“我们的大脑不是,也不会是一台计算机”,可用的方法是:(1)证明大脑某些“活动部件”所参与的信息处理工作是计算机无法模仿的;或者(2)证明我们平日熟悉和喜爱的那些精神壮举并不能由组合、汇总计算机部件的简单工作或通过任何计算机形式的处理方式来实现。

除了哲学家外,还有神经科学家、心理学家、语言学家,甚至物理学家都认为这种人类大脑的“计算机隐喻”极具误导性,因为大脑显然能够做很多计算机不能做的事情。他们通常(但也不绝对)先对“计算机是什么”或者“应该是什么”这个问题规划出一个很天真的预设,最后就得出了这个明显但又不相干的命题,即大脑可以做很多笔记本电脑不能做的事情,因为笔记本电脑没有充足的传感器和效应器,内存低,运行速度也有限。而事实上,关于“通常情况下计算机能做什么”这个问题,如果真要回答,我想我们需要弄清楚的应该是:在通常情况下,计算机的能力来自哪里,是怎样得以发挥的?

1957年,那还是计算机时代的初期,王浩发明了寄存器机这个神奇的东西。他是逻辑学家,是库尔特·哥德尔的学生,顺便提一句,他也是一位哲学家。寄存器机是一件简洁的思考工具,每个人的工具箱里都该有这么一件,可惜它至今还仍未被大家所熟知。 寄存器机是理想化的、假想出的计算机,它只是由有限数量的寄存器和一个处理单元构成的,具有十足完美的合理性。

寄存器是一些存储单元,每个寄存器都有自己各自的地址,比如寄存器1、寄存器2、寄存器3,以此类推,它的内容包含一个整数,如0、1、2、3,……。寄存器就像是一个一个的大盒子,里面可以放置任意多的(从0到无限多个)豆子。不管这些盒子有多大,而我们总是希望这些盒子能无穷大,因为那样它们才能装上无限大的整数。但我们对大盒子的这些设定是有目的的。

处理单元只具备三种简单的能力:它只能“遵循”三种“指令”,一次执行一条。每一串指令构成的序列都是一个程序,而每一条指令由一个数字来标记,即给每条指令分配一个ID号。这三条指令分别是:

结束。 表示将自己停止或者关闭。

增量。 将寄存器n中的数字加1,或者说在盒子n中再填入一粒豆子,然后进入下一步,步骤m。

减量。 将寄存器n中的数字减1,或者说从盒子n中拿走一粒豆子,然后进入下一步,步骤m。

减量指令与增量指令的运行原理基本相同,只是减量指令中多了一个非常重要的难点:如果寄存器n中的数字是0该怎么办?我们已经无法再从中减1,因为寄存器中不能持有负的整数,正如你无法从一个空盒子中再拿走一粒豆子,面临如此窘境只得另寻它路。办法是:跳转到其他地方运行下一步指令。每一个减量指令都需要在程序中列出那个接受跳转的位置,以应对当前寄存器数字为0的情况。所以,减量的完整定义应该是:

减量。 将寄存器n中的数字减1,如果可以执行则继续进行步骤m,如果寄存器n不能减1,则跳转至步骤p。

一部寄存器机能做的所有工作,简单表示就是:结束、增量、减量(或者跳转)。

乍一看,你会觉得这台机器的工作实在有些无聊,只做着一些把一粒豆子放进盒子里或者从盒子里拿出一粒豆子的工作(只要它能找到一粒豆子的话,如果找不到,它会跳转到另一条指令)。但事实上,它可以完成一台计算机能做的所有运算。

让我们从简单的相加开始吧。假设你想让寄存器机将某一个寄存器(比如寄存器1)里的数字加到另一个寄存器(寄存器2)里的数字上。如果寄存器1中的数字是“3”,寄存器2中的数字是“4”,那么,我们要设计出的程序就是:相加结束后,寄存器2中的数字为“7”,因为3+4=7。这个程序用简单的RAP(Register Assembly Programming,寄存器汇编程序)语言编写出来就是:

程序1:相加[1,2]

前两个指令构成了一个循环,寄存器1每减一次,寄存器2就加一次,直至寄存器1中的值变成0,处理单元能“注意”到寄存器中数值为0这一点,继而会将运行步骤跳转到3,即寄存器机停止工作。处理单元并不知道寄存器中的数字,它只能识别出寄存器中数字为0的情况。用盒子和豆子来打个比方:把处理单元想象成是一个盲人,它看不见盒子(寄存器)里面有多少豆子,但是通过探测,它可以识别出盒子空了。所以,尽管事实上处理单元不知道寄存器里确切的内容,但只要步骤1开始运行,寄存器1的内容(不管寄存器1中的数字是几)就会累加到寄存器2里(不管寄存器2中的数字是几),然后停止。(你能知道它为什么会这样运作吗?多试几个例子看看。)我们甚至可以这样说:即使不知道哪两个数相加,不知道数字是什么,甚至也不知道加法是什么,寄存器机也能完美地完成累加计算!

练习1

a.运用程序1,想一下寄存器机需要几步可以计算出2+5=7?(“结束”也算是一步。)

b.要计算出5+2=7需要几步完成?(你从中可以得出什么结论?)

有一种图解可以很好地说明这一运算过程,我们称之为流程图。圆圈中的数字代表将要接受运算的寄存器的地址(它不是寄存器中的内容),“+”代表增量,“-”代表减量。程序从α(alpha)开始,至Ω(omega)处停止。箭头表示进入下一指令。请注意,每个减量指令处都会向外引出两个箭头,可以减量时有箭头指向一个步骤,而无法减量时(当寄存器中的内容为0时,遇0跳转)则有另一个箭头指向另外一个步骤。

现在,我们就来编写一个程序,将一个寄存器中的内容移动至另外一个寄存器中。

程序2:移动[4,5]

流程图如下所示:

可以看到,第一个循环是清空寄存器5。这样一来,不管一开始寄存器5中的数字是多少,它都不会影响到在第二个循环中要移进来的内容——第二个循环正是增量运算的环节,将寄存器4中的内容添加到已清空的寄存器5中去。这种初始化步骤被称作寄存器清零。它是一项非常有用的标准化运算,我们会不断用到它,好让寄存器能重新投入使用。

第三个程序是,简单地将一个寄存器中的内容复制到另一个寄存器中,被复制的那个寄存器中的内容保持不变。仔细观察这个流程图,思考它的程序设计:

程序3:复制[1,3]

显然,这是完成拷贝的一条迂回路线。首先,我们要将寄存器1中的内容移动到寄存器3中,同时比照3中的内容做一个副本,将它复制到寄存器4中。最后,再将寄存器4中的内容重新移动到寄存器1中——也就是将寄存器4中的内容复制回了寄存器1。程序就这样按部就班地运作,不管一开始寄存器1、3、4中的内容是什么,到程序结束时,寄存器1中一定保留着自己原来的内容,复制的内容保存在了寄存器3中。

如果你认为自己对这个程序的运作方式还不是很清楚,那么你可以尝试“手动模拟”整个过程。取出几个杯子充当寄存器(用铅笔在每个杯子上写一个数字,这是它们各自的地址),再找一些硬币或是豆子。在每个寄存器里都放置几枚硬币,然后记录下寄存器1和寄存器3中硬币的数目。接下来,依照程序按部就班地执行完毕,你会发现,寄存器1中硬币的数量与先前一致,而寄存器3中也拥有了与寄存器1数目相同的硬币。能将累加寄存器的基本运作过程都吸收内化是非常有必要的,我们得做到无需再去费劲思量它们。那么现在,就让我们先用几分钟去变成一部寄存器机吧(就像一个演员要去饰演哈姆雷特那样),因为接下来还要用到这种技能。

我发现好多学生常会犯这样的错误:他们认为,在做寄存器减量时,硬币从一个寄存器里取出之后就一定得放入到另一个寄存器中去。这是不对的。减量取出的硬币只需放回到那一大堆硬币中去即可,在简单的加减法套路中,有“无限多”的硬币可供你使用。

掌握了移动、复制还有归零这些技能之后,我准备重新回到之前的加法程序,对它进行改进。之所以要这样做是因为,程序1虽然成功地将加法运算的结果存进了寄存器2中,但在运行过程中,寄存器1和寄存器2中原有的内容却受到了破坏。我要设计出一个更加专业的加法程序,它能保留住原有的那些数值以备后用,将得出的结果放到另外一个地方。现在就让我们来思考这个程序,将寄存器1的内容与寄存器2的内容相加,两个寄存器中的数值保持不变,将结果显示在寄存器3中。

下面是完成这一程序的流程图:

现在,我们就来分析一下各个循环,看看每一步是怎样运行的。首先我们要将显示最终答案的寄存器3归零,同时清零那个空闲的寄存器4,让它充当临时的接受器或者缓冲器。然后,我们把寄存器1中的内容复制到寄存器3和寄存器4中,随后将寄存器4,也就是缓存器中的内容再次移动到寄存器1,让它恢复原有的数据;在这个过程中,作为缓冲器的寄存器4再次被清零,以备后用。接下来,我们将上一步的寄存器1替换为寄存器2再执行一遍,将寄存器2的数据加到寄存器3的数据上。待整个操作完成后,缓存器(寄存器4)清零,加法答案显示在寄存器3中,同时,我们选用的两个加数也保留在了原来的出处——寄存器1和寄存器2里。

将这套由13个步骤构成的RAP程序改写成下面的流程图,可供处理单元读录:

程序4:无损加法[1,2,3]

我不建议你拿杯子和硬币去手动模拟这个程序。人生苦短,一旦你把这些基本步骤都刻入脑海,那就可以切实享受一下“义肢”给你带来的便利了,这副义肢叫RodRego,你可以从网站http://sites.tufts.edu/rodrego/上下载到这部寄存器机。

最初的寄存器机RodRego的网站首页,1986年。

RodRego有PC、Mac 两个版本可在您的计算机上运行。20多年前,我和乔治在课程软件工作室里研发了这件思考工具,如今,成百上千的学生和其他行业的人员通过它成为了熟练的寄存器机思考者。你可以将自己编写的RAP程序键入电脑,观察它们如何运行,不管寄存器中的内容是数字还是豆子。另外,有的多媒体软件还可以顺着流程图将处理单元的程序路径淋漓尽致地展现出来,让你可以清楚地看到RAP指令与流程图各环节之间的相互对应。

接下来,我们再来看看减法。这是我的第一次尝试,从寄存器1中减掉寄存器2中的内容,将答案显示在寄存器4中。你能看出我这个流程图存在的问题吗?

这个流程图只有在寄存器1中的内容大于寄存器2的内容时才能正常工作。但如果实际情况不是这样,又该怎么办呢?寄存器1在不断减量的过程之中就已被“清零”,那接下来会发生什么?此时我们不能中止电脑运行,因为那样的话,寄存器4中会留下一个错误的答案“0”。事实上,在寄存器1归零时,我们可以开启一个新的程序,它会先倒退半个循环,将寄存器2中最后减去的1再加回来。此时,将寄存器2中(而不是寄存器1)的数字转化成负数,所得的结果就是正确答案。所以我们只需把此时寄存器2中的数字移动至寄存器4中(之前,其中的内容已清零),再将答案标注为负数即可。我们还需要另外一个寄存器,比如说寄存器3。与寄存器4一样,寄存器3也要首先清零,然后,通过程序设计,它要像一面“旗帜”那样为答案做标记,内容为0时表示正号“+”,为1时表示负号“-”。下面就是这个程序的流程图,各个循环和步骤都有相关注释。(这些注释也可以写入RAP程序中供你以及其他人参考阅读,你要用“#”在其首尾做出标记,这样RodRego就会自动忽略掉它们)。

练习2

a.请为这个流程图写出它的RAP程序。(请注意,由于程序有不同的分支,你可以采用不同的顺序为各步骤编号,只要“下一步”对应的步骤编号填写正确即可。)

b.如果从3中减3,或者从4中减4,这个程序会出现哪些不同?

c.将寄存器3在第三步之前就清零而不是推延到完成第四步之后,这样会避免哪些可能的错误?

熟练掌握了加法和减法的编程以后,再设计乘除运算就易如反掌了。n乘以m表示m个n相加。我们可以这样来设计程序:用一个寄存器作为计数器,从m递减到0,每完成一次加n的运算就减去1。

练习3

a.画一个流程图或者写一个RAP程序,让寄存器1中的数字与寄存器3中的数字相乘,将答案显示在寄存器5中。

b.(可选) 利用复制和移动改进你上面写出的乘法编程,使寄存器1与寄存器3在程序停止后都能保持原有的内容,方便你在运算完成后清楚地检查输出和输入的内容正确与否。

c.(可选)画一个流程图或者写一个RAP程序,要求它能识别寄存器1与寄存器3中的内容(同时保证这些内容不受损坏),将内容较大的寄存器地址(1或3)显示于寄存器2;当两个寄存器中的内容相等时,寄存器2中显示2。(程序完成后,寄存器1和寄存器3中的内容要保持不变,寄存器2显示出所含数字较大的寄存器地址,当内容大小相等时,寄存器2将报出数字2。)

与乘法类似,除法运算只需要从被除数中一次一次不断地减去除数并记录下相减的次数即可。如果有余数,我们可以留出一个专门的寄存器储存它。另外,我们还要小心地添加一个必要的检查项目:看看除数是不是0(0不能作除数,请问这是为什么?)。所以,在进行除法运算之前,我们得对除数作一个简单的核查——看看它能否被减量。如果可以,我们还必须再安排一次增量,以便使除数恢复原值,让除法运算继续进行。如果在进行减量核查时,除数真的是0,那就需要拉响警报了。为此,我们可以专门准备一个寄存器负责报错:寄存器5里如果有1出现就表示,“快!我就要除以0了!”

下面就是一个除法流程图,用寄存器1中的内容除以寄存器2中的内容,将答案写入寄存器3,余数显示在寄存器4,醒目的寄存器5用来提示“错误信息”(显示1表示“我将要除以0”)。

顺着这幅流程图梳理,你就能看到“除数为0”是如何中止掉整个运算的,报错的旗帜究竟是如何竖起的。另外还需要注意的是,寄存器4身兼双重职责,不仅要复制并恢复除数数据,以便下次继续成功地减量,而且,还要时刻准备着成为一个余数寄存器。如果在寄存器4将自己的内容倾倒回寄存器2以备下次减量之前,寄存器1中的内容就已经为0,那么留在寄存器4中的数字就是余数,它也正好显示在了余数寄存器中。

秘密1

发挥能力,无需理解力:有些东西——如一台寄存器机——它能完成精密的运算,但无需理解自己在做什么。

寄存器机没有头脑,因而它不能理解。但因为一碰上增量、减量、停这三条“指令”,它就会老老实实地执行,所以我们也有理由说,寄存器机是近似于明白这三件事情的。不过这三条指令不是真正的指令——它们只对于我们来说像是指令,但对于寄存器机,它们只是近似于指令而已。

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

其实,只要掌握一定的诀窍,组合这些程序就会变得轻车熟路。更何况,运算程序一经写成便可以多次重复地使用。现在,我们对各个运算程序进行编号,运算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×10 41 ”),使计算机除了运算自然数以外还可以近似运算其他数值。浮点运算是以浮点数作为数值的算术运算,突出运用在乘法和除法中。20年前(这一章首版时),我们能买到的最快的超级计算机每秒钟可以完成超过400万次的浮点运算。

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

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

秘密7

没有再多的秘密了!

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

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

点击中间区域
呼出菜单
上一章
目录
下一章
×