一种可以自我修改的计算机器

有这样一堆可以存储数字的方格:

00000000
00000000
00000000
00000000

如果我们可以对其中任意2个方格进行“或"运算并写入任意1个方格,或者对任意1个方格进行"非"运算并写入任意1个方格,那么由无限个方格和操作组成的机器是图灵完备的吗?答案是肯定的,下面我将给出这种机器的一个实例:

该机器具有如下特点:

  • 该机器的所有状态几乎都由自身描述,并且它可以直接改变自己的内部状态(即自我修改,与冯·诺依曼架构类似但又不相同),不像图灵机一样内部状态和纸带是分离的。
  • 它的状态转移是清晰可见的,而图灵机由一个内部状态(格局)转移到另一个状态像幽灵一样。
  • 它没有未定义行为,也没有停机状态。
  • 它有清晰可见的时序实现,而《编码:隐匿在计算机软硬件背后的语言》一书中构造的物理机器时序并不是显而易见的。
  • 它的条件跳转和其他所有操作均由“或”和“非”两种基本操作实现(其实只用与非门也行),即由软件实现,而论文《URISC: The Ultimate Reduced Instruction Set Computer》中的条件跳转指令由硬件实现。
  • 它使用表格表示,不需要画逻辑电路图,也不需要使用硬件描述语言(HDL)。
  • 它既可以很容易地进行书面表示(即人类可读),又可以很容易地构造实际的物理机器。

该机器实例的构成:

  1. 有限个方格,每个方格可以存储16位二进制数,即可以表达十进制数0到65535,为了增加可读性本文所有数字都用十进制表示。
  2. 每行放8个方格,每个方格都有一个编号,编号从0开始,从左往右递增,从上往下换行,即行主序。
  3. 一堆方格可以组成模块,每个模块只能寻址到自身,模块与模块之间可以硬连线。增加模块的目的是增加可读性,如果所有数字都写在一个模块里很可能不是人类可读的。
  4. 每个模块都有一个自身的振荡器(即时序),但所有振荡器都是同源的。也就是说,所有模块都是并行的。
  5. 每个模块的行为比较简单,它由一个振荡器驱动,反复执行动作。首先,读取第0号方格中的数字,设为a,再读取编号为a的方格中的数字,设为b,根据b的值决定接下来的动作:
  • 若b为1,读取后面的3个方格,设为c、d、e,再分别读取编号为c、d的方格,进行“或”运算并写入编号为e的方格。然后再读取后面的3个方格,设为f、g、h,再分别读取编号为f、g的方格,进行“或”运算并写入编号为h的方格。(该步骤执行了两次16位“或”运算)
  • 若b为2,读取后面的2个方格,设为o、p,再读取编号为o的方格,进行“非”运算并写入编号为p的方格。然后再读取后面的3个方格,设为q、r、s,再分别读取编号为q、r的方格,进行“或”运算并写入编号为s的方格。(该步骤执行了一次16位“非”运算和一次16位“或”运算)
  • 若b为3,读取后面的3个方格,设为c、d、e,再分别读取编号位为c、d的方格位(按位寻址,只读1位),进行“或”运算并写入编号位为e的方格位中。然后再读取后面的3个方格,设为f、g、h,再分别读取编号为f、g的方格,进行“或”运算并写入编号为h的方格。(该步骤执行了一次1位的“或”运算和一次16位的“或”运算)
  • 若b为其他值,执行和b为1时同样的动作。

下面通过一个例子来说明:

80000000
2521140160
2631220240
1237131032
277138080

该模块的执行流程是:

  • 先读取0号方格,即第一行第一列,读到了8,再读取8号方格,即第二行第一列,读到了2,发现这是第二种指令,接下来的两个数字是5和2,所以对第5号方格执行非运算并写入第2号方格。再后面是1、14、0,所以对1号方格和14号方格执行或运算并写入0号方格。可以看到14号方格的值是16,刚好是第三行第一列的编号,而1号方格的值是0,0或16等于16,这就是将16写入了0号方格,这是实现绝对跳转的关键。(这里的“CPU”执行完一条“指令”并不会自动转到下一条“指令”,因此需要我们手动实现)
  • 接下来进入下一个振荡器周期,读取0号方格,读到了16,再读取16号方格,读到了2,发现还是第二种指令,执行流程和上面一样。最后24被写入了0号方格。
  • 接下来进入下一个振荡器周期,读取0号方格,读到了24,再读取24号方格,读到了1,发现是第一种指令,将2号方格和3号方格进行或运算并写入7号方格,再将1号方格和31号方格进行或运算并写入0号方格,31号方格的值是32,即将32写入了0号方格。
  • 接下来进入下一个振荡器周期,读取0号方格,读到了32,再读取32号方格,读到了2,发现是第二种指令,执行非运算和或运算后,最终8被写入了0号方格。至此0号方格又回到了最初的值,开始了新一轮的循环。

那么这个模块到底在干嘛?其实它就是一个16位2输入与门,根据德·摩根定律,A * B = ~ (~ A + ~ B),假设每条指令的计算耗时均不超过1个时间单位,我们将2个输入分别放入5号和6号方格,等待4个时间单位后,7号方格就得到了结果。由于与门不在我们的操作列表里,所以这里我们用或门和非门实现了与门。我们把输入和输出方格标识出来,对这个模块进行重新描述如下:(注意表格中第一行的加粗字体并不表示什么含义,只是MarkDown的表格第一行会自动加粗)

A:16位2输入与门,振荡器周期1个时间单位,计算耗时4个时间单位
80000ininout
2521140160
2631220240
1237131032
277138080

接下来基于2输入与门,我们就可以实现3输入与门:

B:16位3输入与门,振荡器周期4个时间单位,计算耗时20个时间单位
8000inininout
114Aa5115016
115Aa6123024
11Aa7Ab5131032
116Ab6139040
11Ab7714708

表格中大写字母开头的方格Aa5表示该方格硬连线到模块A的第5号方格,是编码为a的实例,就是说Aa和Ab都是2输入与门,但他们是两个不同的实例。另外由于该模块用到了A模块,所以它必须要等待A模块操作完,振荡器周期应设为4个时间单位。(这里B模块用到了A模块,假设我们称B为父模块,A为子模块,只要每个父模块的振荡器周期是所有它的子模块的计算耗时的倍数,即使子模块内部在中途修改了输出方格的值也没关系)

同样的,为了实现一点有趣的功能,我们还需要实现其他的基本模块:

X:16位2输入异或门,振荡器周期4个时间单位,计算耗时28个时间单位
80000ininout
2521140160
2631220240
112Aa5131032
116Aa6139040
113Ab5147048
115Ab6155056
1Aa7Ab7716308

这里要注意,X模块中的Aa和B模块中的Aa是不同的实例。

C:16位4输入或门,振荡器周期1个时间单位,计算耗时3个时间单位
800ininininout
1347115016
1562123024
127713108
P:1位全加器(依然使用16位运算),振荡器周期60个时间单位,计算耗时2040个时间单位
1600in_carryininoutout_carry
00000000
23111220240
24121300320
25131380400
1111Ba4147048
1112Ba5155056
115Ba6163064
1111Bb4171072
114Bb5179080
1113Bb6187088
113Bc4195096
1112Bc511030104
1113Bc611110112
113Bd411190120
114Bd511270128
115Bd611350136
1111Be411430144
114Be511510152
115Be611590160
113Bf411670168
1112Bf511750176
115Bf611830184
113Bg411910192
114Bg511990200
1113Bg612070208
11Ba7Ca312150216
11Bb7Ca412230224
11Bc7Ca512310232
11Bd7Ca612390240
11Be7Cb312470248
11Bf7Cb412550256
11Bg7Cb512630264
11Bd7Cb612710272
11Ca7612790280
11Cb771287016
Q:16位加法器,振荡器周期2040个时间单位,计算耗时99960个时间单位
80000ininout
31*161*16Pa3*16115016
31*165*16Pa4*16123024
31*166*16Pa5*16131032
31*16Pa7*16Pa3*16+1139040
31*165*16+1Pa4*16+1147048
31*166*16+1Pa5*16+1155056
31*16Pa7*16+1Pa3*16+2163064
31*165*16+2Pa4*16+2171072
31*166*16+2Pa5*16+2179080
31*16Pa7*16+2Pa3*16+3187088
31*165*16+3Pa4*16+3195096
31*166*16+3Pa5*16+311030104
31*16Pa7*16+3Pa3*16+411110112
31*165*16+4Pa4*16+411190120
31*166*16+4Pa5*16+411270128
31*16Pa7*16+4Pa3*16+511350136
31*165*16+5Pa4*16+511430144
31*166*16+5Pa5*16+511510152
31*16Pa7*16+5Pa3*16+611590160
31*165*16+6Pa4*16+611670168
31*166*16+6Pa5*16+611750176
31*16Pa7*16+6Pa3*16+711830184
31*165*16+7Pa4*16+711910192
31*166*16+7Pa5*16+711990200
31*16Pa7*16+7Pa3*16+812070208
31*165*16+8Pa4*16+812150216
31*166*16+8Pa5*16+812230224
31*16Pa7*16+8Pa3*16+912310232
31*165*16+9Pa4*16+912390240
31*166*16+9Pa5*16+912470248
31*16Pa7*16+9Pa3*16+1012550256
31*165*16+10Pa4*16+1012630264
31*166*16+10Pa5*16+1012710272
31*16Pa7*16+10Pa3*16+1112790280
31*165*16+11Pa4*16+1112870288
31*166*16+11Pa5*16+1112950296
31*16Pa7*16+11Pa3*16+1213030304
31*165*16+12Pa4*16+1213110312
31*166*16+12Pa5*16+1213190320
31*16Pa7*16+12Pa3*16+1313270328
31*165*16+13Pa4*16+1313350336
31*166*16+13Pa5*16+1313430344
31*16Pa7*16+13Pa3*16+1413510352
31*165*16+14Pa4*16+1413590360
31*166*16+14Pa5*16+1413670368
31*16Pa7*16+14Pa3*16+1513750376
31*165*16+15Pa4*16+1513830384
31*166*16+15Pa5*16+1513910392
11Pa67139908

这里用到了第三种指令按位或运算,所以表格中的1 * 16即16,Pa3 * 16即Pa48,按位寻址。另外进行位运算前无需清零,因为存储器在一开始已经全部清零。

M:5位比较器(存储单元依然为16位),相等输出0,不相等输出1,振荡器周期84个时间单位,计算耗时672个时间单位
80000ininout
115Xa5115016
116Xa6123024
31*16Xa7*16Ca3*16131032
31*16Xa7*16+1Ca4*16139040
31*16Xa7*16+2Ca5*16147048
31*16Xa7*16+3Ca6*16155056
31*16Ca7*167*16163064
3Ca7*16Xa7*16+4Ca7*1617108
S:16位比较选择器,比较前2个输入的数字(最大5位),相等则输出后2个输入的数字的前者,不相等则输出后2个输入的数字的后者,振荡器周期672个时间单位,计算耗时4704个时间单位
1608in_numin_numin_addrin_addrout_addr
00000000
113Ma5123024
114Ma6131032
12Ma7Ma7139040
1158147048
1169155056
11Ma766163064
1107171016

这里需要一点特殊的技巧,将5号方格的in_addr和6号方格的in_addr分别写入8号和9号方格,然后因此M模块的输出是0或者1,所以第五行将2号方格的8与Ma7进行或运算相当于对Ma7加8,即0+8=8或者1+8=9。另外66号方格最开始是0,但会被倒数第二行的指令修改,即自修改,只有这种自修改的方法才能实现条件选择。

Z:循环求和模块,计算1+2+3+4+5,振荡器周期399840个时间单位,计算耗时21991200个时间单位
808881160
115Qa5115016
117Qa6123024
11Qa77131032
114Qa5139040
115Qa6147048
11Qa75155056
115Sa3163064
116Sa4171072
113Sa5179080
112Sa6187088
11Sa79519500

2号方格的8是第二行的起始编号,3号方格的88是最后一行的起始编号,4号方格用来每次加1,5号方格是循环变量,初始化为1,6号方格的6是循环变量的终止值。注意最后一个方格即95号方格,它的初始值是0,但它会被最后一行的指令根据S模块的输出值修改(又是自修改),也就是如果Sa3等于Sa4,则把Sa5(88)放入95号方格,如果Sa3不等于Sa4,则把Sa6(8)放入95号方格。所以最后一条指令相当于实现了条件跳转,根据情况选择跳转到8或者88。

经过21991200个时间单位后,我们在右上角(即7号方格)看到了1+2+3+4+5的计算结果15,最终该模块陷入了执行最后一行指令的无限循环。

但是无限循环的逼格始终不够高,相信看过《论可计算数及其在判定问题上的应用》和《计算机程序的构造和解释》的朋友应该对里面的元解释器印象深刻,下面我们也来实现一个元解释器:

I:元解释器,振荡器周期399840个时间单位,计算耗时取决于装入的程序
160882804405600
10241234567
181024Sa3123024
1110Sa4131032
113Sa5139040
115Sa6147048
11Sa75515500
1111Sa4163064
114Sa5171072
112Sa6179080
11Sa78718700
119Qa5195096
181024Qa611030104
11Qa711411110112
110611190120
1110Qa511270128
181024Qa611350136
11Qa714611430144
110711510152
1111Qa511590160
181024Qa611670168
11Qa717911750176
167011830184
1112Qa511910192
181024Qa611990200
11Qa721012070208
110612150216
1113Qa512230224
181024Qa612310232
11Qa724212390240
110712470248
1114Qa512550256
181024Qa612630264
11Qa727512710272
16701279016
119Qa512870288
181024Qa612950296
11Qa730613030304
110613110312
1110Qa513190320
181024Qa613270328
11Qa733813350336
260134203440
1111Qa513510352
181024Qa613590360
11Qa737013670368
110613750376
1112Qa513830384
181024Qa613910392
11Qa740213990400
110714070408
1113Qa514150416
121024Qa614230424
11Qa743514310432
16701439016
119Qa514470448
181024Qa614550456
11Qa746614630464
110614710472
1110Qa514790480
181024Qa614870488
11Qa749814950496
110615030504
1111Qa515110512
181024Qa615190520
11Qa753115270528
367015430544
1112Qa515510552
181024Qa615590560
11Qa757015670568
110615750576
1113Qa515830584
181024Qa615910592
11Qa760215990600
110716070608
1114Qa516150616
181024Qa616230624
11Qa763516310632
16701639016

只要把上面的循环求和模块Z装入1024开始的一堆方格中,等待一段时间即可看到1+2+3+4+5的计算结果15。注意只有最外层的模块是跑在解释器里的,其他子模块还是跑在真机里。

如果要模拟图灵机也可以用类似的方法,能模拟图灵机即可证明它是图灵完备的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值