唐纳德·克努特:编程、算法、难题与生命游戏
Donald Knuth: Programming, Algorithms, Hard Problems & the Game of Life
计算机科学泰斗谈《计算机程序设计艺术》的写作、算法之美、攻克难题的乐趣与生命游戏。
1957 年夏天,一个还没念完大一的年轻人夜里独自坐在 IBM 650 的控制台前,用十进制数字一位一位拨进指令。他要让这台机器把开关上设好的数字分解成质因数。
那个年代几乎没人听说过汇编器。他看的是随机附带的手册,上面写着 69 表示装载,后面接上要装入的地址。一条指令是十位十进制数:两位说明做什么,四位说明对哪个地址做,剩下四位说明下一条指令去哪儿取。整台机器的内存是两千个字。
这个年轻人叫唐纳德·克努特(Donald Knuth)。
他后来讲过一句很实在的话:如果那本手册写得好,他大概不会走上计算机科学这条路。正因为写得实在太差,他一边读一边想,我一个新生都能写得比这强,看来我在这上头有点天赋。于是他跑到学校的计算中心打工,开始写手册。
前几天,计算机历史博物馆的朋友道格·斯派瑟给他发了一个刚上传到 YouTube 的链接:IBM 1956 年的进度报告。那年 IBM 把 650 捐给了斯坦福、MIT 和他所在的 Case Tech,影片里能看到装配线上第五百台下线。片子还拍到一间教室,老师在黑板上写着 69 8000,正在教学生怎么给这台十进制机器写程序。
克努特就在那样的教室里起步。
第一个程序调试了一整夜,最后代码行数比预想的多出一大截
他的第一个程序是因数分解。输入是控制台上的拨盘,输出打在穿孔卡片上。最初大概二十行,写完开始调试,行数就开始往上涨。
调试的方式很原始。晚上没人的时候,他坐在机器前,拨一个数,然后按单步执行,看机器做了什么。有四组开关可以设一个地址,机器执行到那儿就停下来。他就这样盯着它把这个数除以二,没有余数就说二是因子,有余数就换三,一直试到开方为止。
第一个晚上他大概就跑通了 30 等于 2 乘 3 乘 5。之后麻烦陆续出现:有些数的因子多到一张卡片装不下,而一张卡只有 80 列,每个因子要占 10 列,他一开始根本没考虑要输出到第二张卡;如果输入的是一个很大的质数,程序会在那儿空转十分钟,这台机器慢得让人分不清它是在计算还是卡在死循环里。
第二个程序简单得多,二进制转十进制,几乎没出什么岔子。第三个才是井字棋。
三个大脑,其中一个自己学会了不输
井字棋那件事值得细说,因为里面藏着一个十八九岁的年轻人对“学习”这件事最早的直觉。
棋盘是三乘三,每个格子有三种状态:空、叉、圈。总共三的九次方,一万九千六百八十三种局面。IBM 650 只有两万个十进制数位的存储,还得塞下程序本身、人机交互的逻辑、输出逻辑。他做了一件事:利用对称性。棋盘转四次、翻一次,八种等价的摆法算一种,存储需求一下降到八分之一。
每个见过的局面存一位数字。零表示坏棋,九表示好棋,初始值大概设成五六。赢了就把这个局面对自己调高,对对手调低。
他把程序分成三个大脑。第一个大脑完全随机,轮到它就随便找个空格打一子。第二个大脑是内置的固定策略,一套已知最优的走法。第三个大脑是真的在学。
然后他让第一个大脑和第一个大脑对打。大约六百局之后,两个什么都不知道的随机玩家,收敛到了一定和棋。
这个结果值得停一下。随机对随机,本来是乱打,最后变成了谁都不犯错的稳健局面。原因在于他设计奖励函数的方式:他惩罚的是错误,妙手反而没有额外奖励。这个学习系统学会的是不输,不是想赢。他自己也点破了这一点,说如果换一个奖励函数去奖励漂亮的走法,学出来的东西会完全不一样。
“它在学习怎么不犯错误,它没想赢,它只想别输。”(唐纳德·克努特)
他还提到两件往事。查尔斯·巴贝奇一百多年前就在琢磨给那台永远没造完的差分机编一个井字棋程序。而他自己上大学的动机之一,是小时候在芝加哥科学与工业博物馆看过贝尔实验室做的继电器井字棋装置,那时他就想着,我是不是也能写一个。
凌晨的机器与停机时间写出来的 TeX 手册
那几年他用机器只能靠夜里。全世界都在睡觉,算力才轮得到他。高中时做校报,周一晚上整夜不睡,周二早上报纸出来。大一大二之间他基本不需要睡眠。
罗德尼·布鲁克斯(Rodney Brooks)讲过一件旧事。他在 MIT 的时候对克努特有点发怵,因为机器坏了,这位个子很高的教授就站在旁边等,压力全在修机器的人身上。克努特自己的版本更平淡:斯坦福 AI 实验室的机器宕得厉害,因为那儿有一群天才程序员每天都在改操作系统,系统一天比一天好,也一天比一天容易崩。
TeX 的手册,很大一部分就是在那台机器宕机的间隙里写完的。
一张餐巾纸,一周写完一本书
1967 年,克努特在牛津第一次见到约翰·康威(John Conway)。那场演讲讲的是纽结,康威拿一堆塑料小棍在台上现场拼出各种结,拼完再拆掉。他讲的东西后来引出了成千上万篇论文,而那些内容他在高中就想过,一直没发表。
1972 年春天,两人碰巧同一天到卡尔加里,一起吃了顿午饭。康威在餐巾纸上写下了他关于“数”的那套公理,写得满满一张。克努特看完觉得极其漂亮。
那年年末,他在挪威过学术休假。十二月的一个深夜,他忽然想到:康威这套数的理论,正好可以用来教学生怎么做研究,怎么体会发现的乐趣。他之前还读过阿尔弗雷德·雷尼写的一本对话体小书,两个角色像苏格拉底那样互相追问数学。凌晨他把妻子吉尔叫醒,说我想写一本书。
一月份,他在奥斯陆市中心租了一间旅馆房间,除了写这本书什么都不干。一个星期,白天黑夜连着写。字流出来的速度几乎比他思考还快。写到第六天完成,第七天休息,然后他准备给秘书写信交代怎么打字,发现自己写不出一个完整的句子了。
“灵感完全离开了我。就好像是在通灵。”(唐纳德·克努特)
稿子寄给康威,康威回信说,唐,你有一条公理写错了。他用的是“不大于”,而应该是“小于等于”。在日常语言里这两个说法差不多,在逻辑公理系统里,选错一个会让后面每一步证明都变难。那年夏天他又抽出一周,跑到挪威山里重写了一遍。
书最后叫《超现实数》(Surreal Numbers)。一本讲数学的书,写成了两个人的对话,写的过程本身就是书里讲的那个过程:从两条公理出发,一步步发现,作者犯的每个错,书里的角色也犯一遍。
这两个人后来的交往也很有意思。克努特在蒙特利尔做七场关于稳定匹配的系列讲座,康威在第六场和第七场之间到了,两人在派对上碰面,克努特讲了自己在做什么,康威当场想了一会儿,推出了一套漂亮的理论:所有稳定匹配构成的集合是一个分配格,还给出了求两个稳定配对的下确界和上确界的方法。第二天的讲座就用上了。
稳定婚姻这个词,克努特用了六十年。他和吉尔结婚也正好六十年。他们认识的方式是他先跟吉尔的室友约会,然后跑去问吉尔该怎么追。他发现听她出主意比追她室友有意思得多。一个艺术家,一个极客,一起做设计项目,每年写圣诞卡都要互相妥协各自对美的理解。
他说了一句话,婚礼上大概没人敢讲:在遇到她之前,他其实并不喜欢接吻这件事。
费曼问了一个没人问过的问题
在加州理工,他和费曼会互相去听对方的讲座。费曼要是坐在第一排,他会走神,会漏掉几句话。
他印象最深的是费曼在巴西的那段经历。费曼说那儿的物理教育全错了,学生只是在学怎么通过考试,没在学物理。别人不信,他说你把书翻到任何一页,我告诉你这一页错在哪儿。那本教材的作者就坐在台下,还是位很好的老师。
克努特从中提炼出的东西,比故事本身更重要:教育机构很容易滑向发证书,把给知识这件事放到了一边。他自己讲过一个鲍勃·弗洛伊德喜欢的漫画:两个人站在一台大机器前面,一个说这机器一秒钟能干一百万人一百年的活,另一个问,那你怎么知道它算得对。
他和费曼之间还有一段纯粹智力上的交锋,关于他名字挂在上面的那个记号:克努特箭号。
加法重复若干次变成乘法,乘法重复若干次变成幂,幂重复若干次就是双箭号。二的二次的二次,二的二次方是四,二的四次方是十六。三箭号、四箭号可以一直叠上去,数字会迅速膨胀到人脑无法把握的程度。他用这个来举例说明什么叫“大到无法理解”。
费曼听完说,那我们别只用整数,考虑考虑复数。x 双箭号 2.5 可以插值,那 x 双箭号 i 呢?他断言不存在能做这件事的解析函数。克努特当时没弄明白他凭什么这么断定。然后费曼接着问:那箭号的个数能不能是复数?
这就是费曼式的提问方式。克努特讲这段的时候语气很平静,但你能听出来他记住这个提问已经几十年了。
他还解释过自己为什么从物理转到数学。物理考试他能拿 A+,但他完全不知道老师是怎么想出那些题的。数学不一样,他知道题目为什么这么出,还能自己编题。他擅长代数,不擅长几何。物理学家用波思考,他也能用波思考,但那感觉像一条狗用后腿走路。能走,但不像它的本能。
他相信计算机科学很大程度上是被那些大脑恰好能和某类概念共振的人推动的。量子计算就属于需要另一种大脑的东西,它正好卡在物理和计算机科学的交叉口上。
KMP 算法:三个不同的人摸到了同一头大象
字符串匹配这个问题,今天每个用搜索引擎的人都受益于它。你有一大段文本,想知道“Knuth”这个词出现在哪儿,或者某个比 Knuth 更没意思的词出现在哪儿。
朴素做法是:从头扫,先找 K,找到 M,再找 O,一路下去,但遇到“Morris”这个词,读到第五个字母发现是 E 而不是 I,只能退回下一个位置重来。加州大学伯克利分校的吉姆·莫里斯(Jim Morris)注意到一个巧妙之处:刚才那几个字母已经读过了,而且它们不可能是 M,没必要重读。
如果模式词像“abracadabra”这样,开头、中间、结尾都有重复的花纹,事情就变得很微妙。
莫里斯把这个想法写进了伯克利 Unix 的系统软件里,但他没写解释。几个月后有人翻到这段代码,看看觉得不对劲,删掉了。算法做出来了,没人看得懂,就等于没做。
沃恩·普拉特(Vaughn Pratt)一两年后独立发现了同样的东西,当时他研究的其实是回文问题,跟文本搜索八竿子打不着。克努特自己则是从另一个方向过来的,而且路径相当绕。
那会儿史蒂夫·库克在伯克利当教授,克努特说伯克利计算机系最大的失误就是没给他终身教职,后来库克去了多伦多。库克证明过一个关于栈自动机的定理:这种机器只能看栈顶,只能后进先出地存取,没有别的记忆,比图灵机弱得多;但如果它能识别某个语言,那么一个普通计算机一定能在 n log n 的时间内识别同一个语言。
这个定理听起来纯粹是理论上的漂亮话。克努特拿它试了一个自己知道栈自动机能做、但想不出快办法的问题。他自认是个不错的程序员,可就是想不出来。于是他把库克的构造一步步写在黑板上,填满一整块,然后盯着那堆东西找规律。最后他看明白了。
“我之所以能写出那个高效程序,完全是因为手里有那个纯粹抽象的定理。”(唐纳德·克努特)
他写过一篇小文章,标题叫《自动机理论可以是有用的》。在那之前他读过大量自动机理论的论文,但它们从没改善过他日常编程的水平,那些东西只是发表在期刊上、看起来有趣的内容。这一次不一样:他想不出怎么写程序,手上有一个自动机理论的定理,然后他就会写了。他说这是他人生中的一个转折点。
他把这份笔记给普拉特看,普拉特说这跟我做的东西很像。三个人各自独立发现了同一件事,每个人摸到的是大象的不同部位,拼起来就完整了。论文的署名按字母顺序排,所以叫 KMP,其实应该叫 MPK。
他这辈子解过的最难的题:把大爆炸放慢
问到他遇到过最难的问题,他没怎么犹豫,答案很明确:巨组元的诞生(the birth of the giant component)。
先讲背景。有 N 个点,彼此完全平等,没有距离,没有几何。取出 π 的十进制数字,两位一组:31、41、59、26,拿第一组和第二组连一条边,拿第三组和第四组连一条边。或者干脆掷骰子,每次随机挑两个端点连起来。π 在这里没有任何特殊性,只是没人知道它的规律,拿来当随机源用。
边一条一条加上去。埃尔德什和雷尼研究过这个过程,叫随机图的演化。
神奇的地方在这儿:当边数是 0.49N 的时候,比如一百万个点、四十九万条边,图几乎全是一棵一棵孤立的树,连一个环都没有。而边数变成 0.51N,五十一万条边,就会出现一个比其它所有连通块大得多的块。这就是巨组元。
几乎就是相变。但它是个双重相变,两件事同时发生,这才是真正引人注目的地方。
伯克利的迪克·卡普有一批学生在模拟这个过程,每过一阵子给图拍一张快照。然后传出一个小道消息:几乎每一次观察,都只有一个连通块里带环。他们做了上百万次实验,只有三四次看到有一个以上的块带环。
斯坦福这边听到这个消息,克努特他们的反应是:如果这是真的,那一定还有更多东西是真的,有一整套理论等着被发现。仔细一看,消息不完全对,但几乎对。确实存在两个或三个带环的块同时存在的时刻,但那个时间窗口极短,快门速度不够就拍不到。
于是问题变成:怎么把这场大爆炸放慢,看清楚中间发生了什么。
他定义了一个叫“复杂度”的量:边数减去顶点数。一棵树有十个点九个边,复杂度是负一。一个单环连通块,环上挂着一堆树,边数等于点数,复杂度是零。多一个环就多一条边,复杂度是一,那叫双环。复杂度加一就是环的个数。
演化过程是这样的:先出现一个环;下一步,按那个小道消息,应该变成双环;但实际上有相当的概率,大约二十四分之五,分裂成两个各自带环的块。再下一步,可能变成三个,也可能是一个双环加一个单环,也可能是三个单环。
他把前五次跃迁的概率全算了出来。算到凌晨三点,数字出来了,分母是 23023。他盯着这个数看:1001 等于 7 乘 11 乘 13,这是他人生第一个程序教会他的事。23023 等于 7 乘 11 乘 13 乘 23。
一个随便的分数不会这样分解。分母里出现这些小质数,说明背后有个理由。他当即换个角度,取对数,公式果然简化了。
第二天,比尔·盖茨来斯坦福,学校想说服他给新计算机系大楼捐钱。他们安排克努特去见盖茨。他就把黑板上那张从一变到二、二十四分之五的演化图讲了一遍。当天结束时盖茨跟发展办公室的人说,那位克努特教授讲巨组元的那段,我印象太深了。
克努特喜欢讲这个故事,因为它说明理论计算机科学真的有用。
后来他和两位合著者把整个问题做完了:那条小道消息所说的“任何时刻都只有一个带环的块”,真实概率不是百分之百,大约是百分之八十七,而且能用 π 表示出来,形式像 12/π² 或者 8/π 那样的东西。那是他这辈子证明过的最难的定理,花了很大力气,也给随机图理论里一大堆其它问题照了亮。
顺带说一句,这套东西和物理是通的:玻色爱因斯坦统计研究的正是分子如何在完全没有几何的情况下结合到一起。
为什么 TeX 是开源的:收入是个阈值函数
TeX 发布的时候,“开源”这个词还不存在。他不要所有权。
因为他亲眼见过所有权怎么拖住一件事。五十年代末,IBM 的人做出了 Fortran,他们本可以规定只有 IBM 的机器能用这个语言,但他们没有,他们说,谁能把 Fortran 翻译到自己机器上,谁就可以做编译器。反观排版行业,每家厂商都有自己的页面描述语言,用户被绑死在某一家设备上,一年后新机器出来,可印刷设备的成本要摊二三十年。
他也不需要这笔钱。他有稳定的教职,书卖得够多,孩子的教育费用不愁。
“收入是个阈值函数。不够就是挨饿,过了那条线,你要么开始想慈善,要么开始想着怎么把它带走。”(唐纳德·克努特)
他有一套自己的判断标准:非平凡的软件应该收费,平凡的软件不该收费。他举 Adobe Photoshop 的例子,说里面有些真正诺贝尔奖级别的知识产权,那个撤销功能可以在上千步复杂操作之后回到任意一个中间状态,背后一定有非常漂亮的算法。专利的意义在于公开,让人不能把它当商业秘密藏起来。
而他自己那台笔记本跑 Ubuntu,不联网,只用 U 盘在自己的 Mac 和它之间搬东西。原话是:我只把最重要的家当托付给 Linux。
你喜欢的字母、数字或符号是什么
他翻到书的索引最后一页,指着一个符号给采访者看。那个符号来自苏斯博士(Dr. Seuss)的《On Beyond Zebra》,书里给这个符号起了名字。他说他小时候能把《Bartholomew Cubbins》背下来。苏斯博士五十年代初还拍过一部电影,里面有几十架钢琴同时演奏,整个布景就是他绘本里那种奇想风格。
说到自己偏爱的符号,是 π。
理由很实用:需要一个随机例子的时候就用 π,因为它没有已知的规律。他拿出一个叫 Masyu 的谜题来举例。盘面上有黑石和白石,像围棋,要求画一条闭合路径,穿过白子时直行,经过黑子时必须直角转弯。这个谜题好玩的地方在于不需要数字,纯视觉,但比数独更上瘾。
他想把它写进《计算机程序设计艺术》当例题,还写了习题教人怎么设计漂亮的 Masyu 题。于是他给自己出了个难题:拿 π 这个希腊字母的像素图,在每个该有石子的位置放一颗石子,然后给其中一些染成黑色、一些留白,要求整个谜题有唯一解。
结果真的存在唯一一种染色方式。
“π 是我用来举例子时能证明自己没有抄现成东西的来源。”(唐纳德·克努特)
把美国地图做成优雅标号,有人顺手把 π 嵌了进去
他最近在写优雅图(graceful graph)。规则是:给每个顶点一个编号,每条边连接的两个顶点编号之差,恰好构成从 1 到边数的一个排列,每条边占一个差值,一个不重复。
他拿了一张完全不对称的真实地图来试:美国四十九个州,加上哥伦比亚特区,阿拉斯加和夏威夷不算,因为得能开车过去。加州和俄勒冈、内华达、亚利桑那相邻,每条相邻关系是一条边。问题是,能不能给每个州一个编号,让所有边对应的差值不重不漏?
他原本想搞个竞赛,因为他自己只做到把所有边的差值控制在一到一百一十七之间,中间还有空缺,实际上应该是一到一百零五左右。他的朋友汤姆·罗基茨一个下午就做出了完美解,竞赛还没开始就结束了。
更妙的是两天后罗基茨回来说,他不仅找到了优雅标号,还让华盛顿州的编号是 31、爱达荷州是 41,沿着美国北边那条边界,编号正好是 π 的数字。
克努特讲这件事的时候是很高兴的。他喜欢用 π,而 π 总会在最难的问题中间某个地方冒出来。
读代码能认人:风格是技术能力,标点符号算不得什么
他说自己读程序的时候,能看出作者换人了。
他举的例子是一个卡内基梅隆团队写的编译器。读到某一段,那个人明显不知道怎么高效地在寄存器之间搬东西,本来一条指令能做完的事要三条。这就是很明显的风格切换。然后又有一些灵光一现的地方,一条指令干了两件事,因为那个人足够了解机器,知道怎么一步达成两个目标。
“我能靠技术水准认出作者,但认不出节奏意义上的风格。”(唐纳德·克努特)
那他自己的代码有什么可辨认的特征?他说现在都是文学化编程,英文和 C 混在一起。如果只看 C 那部分,别人大概会注意到他用大量全局变量,别人不这么用;他更倾向于内联展开而不是函数调用;他用的是 C 的一个子集。
文学化编程是他自己认为最重要的产出
他说,TeX 项目带来的最重要的东西是:他意识到程序是给人读的,不只是给计算机读的,而排版能极大地增强这件事。
他推荐了一本书,《Physically Based Rendering》,作者是 Matt Pharr 等人,拿过奥斯卡奖。电影里所有的图形特效都是算法做出来的,而整本书就是一个文学化程序,不只告诉你阴影怎么算、动画和纹理需要的图像怎么导入,代码还能直接跑。
他把这看成教学编程的一种延伸:把程序当成一个故事来讲。
“它作为程序能跑,同时人也能读,尤其是一周之后或者一年之后的我自己。”(唐纳德·克努特)
这是他自己设的验收标准。一周后、一个月后、一年后,你自己还能不能轻松看懂。
什么是优美的程序
他之前被问过这个问题,回避了。这次他说了为什么回避:答案有几十个,取决于你此刻在找什么。
一个层面上,能跑就美。另一个层面上,容易看懂就美。文学化编程的形态也美。还有,它让你笑出来,也是美。
他提到幽默这件事。有人问过他代码注释里该不该有幽默,他的答案是该。他给出一条很讲究的标准:理想的情况是,读者知道这儿大概有个笑话,只是还没看懂,于是有了动力去想。
《计算机程序设计艺术》第一卷的初稿,主要审稿人是鲍勃·弗洛伊德。出版社把稿子拿给他看,他还专门问,这个笑话留不留。出版社甚至组织了焦点小组,问大家对一本讲计算机编程的书里有幽默怎么看。他坚持了自己的原则,而且书里还留着一些至今没人解出来的笑话。
第二卷里有一个密文。要解开它,得先破一个比现在人类已知方法能破的还要大的 RSA 密钥。他说,如果计算机一直变快,也许一百年后会有人解出来,然后笑一下。
过早优化是万恶之源,这话是在什么语境下说的
他先澄清了“优化”这个词在当时的意思。那会儿他是编译器作者,优化指的是让翻译出来的代码在机器上跑得更快,就是给编译器设优化等级那件事。他当时翻未删节词典,看到 optimizer 这个词的解释是“以乐观的态度看待”。
他那句话针对的是另一种现象:写程序的人会以为,自己写得最费劲的那几页,计算机执行起来也最费劲。十页代码,其中一页折腾了一个星期,于是潜意识里觉得机器读到那一页也会慢下来,会“看不懂,得小心点”。这当然很荒唐。
他做过一次测试。分析一个 Fortran 编译器,发现它超过百分之八十的时间花在读注释卡上。而写编译器的人整天操心的是怎么把带多层括号的复杂表达式翻译好,那部分连百分之一都不到。如果早知道八成时间在读注释卡,十分钟就能让编译器快一倍以上。
这种认识只能来自程序写完之后的实测,来自 profiling。
但他强调这句话还有另一层意思:有时候一种优化确实提升了运行速度,代价是程序下个星期就改不动了,因为你把数据结构换了,适应性没了。所以计算机科学里有一条重要原则叫延迟绑定,能晚决定的就别早决定。他说现在已经有人量化地研究过,延迟决策在制造业、在各种领域都有它的最优性。
而这句话之所以被这么多人记住,是因为程序员心里确实有一种对优化的偏爱。一份优化得极好的代码有它的优雅,那是另一种美。
Copilot、自动化,以及“让人开心”和“正确”之间的选择
采访者给他讲了 GitHub Copilot:写几行注释,说明这段代码要干什么,它就把函数补全。用的是 OpenAI 的 Codex。采访者说,这里面有文学化编程的影子。
克努特听完先追问了一句:那你怎么知道它做得好不好?
然后他讲了一个六十年代加州理工的同事。那个人课讲得极好,听完一小时你觉得全是激动人心的东西,再过一小时你回想,他到底说了什么?那个人真的相信,计算机算得对不对不重要,重要的是它让你开心。你老板付了钱,你有了工作,能养活老婆孩子,这就行了。
“他相信快乐比真相更重要。他不相信有真相,不过他是个哲学家。”(唐纳德·克努特)
他说这种思路正在扩散。越来越多的事情被“看起来能跑就行”接管。当有利益冲突的时候,两边都不明白决定是怎么做出来的。现在大家还知道这是坏事,可五年十年之后,一层一层叠上去,就彻底脱钩了。
反面他也认:自动化程度越高,人越能去做人最擅长的事。也许程序员该专注于编程里那一小块需要天才、需要人类心智的魔法,剩下的杂乱部分交给机器生成。
但他划了线。他说自己永远不会推荐学生去那种地方工作。他要的是理解。正确和快乐都排在它后面。如果这些工具带来的是更好的医疗诊断,是帮某个科学实验找到方向,是攻克疾病,那真是妙极了。可如果它影响到人的生命,那就得非常小心。现在看着像玩闹,写个小 JavaScript 帮网站做点事。一年、两年、五年过去,你忘了底下的东西,你在上面继续盖,最后就是自主武器系统。
他也不回避最终结局,这一点他看得很开:宇宙终有热寂,他只是想把那件事往后推一推。而如果真到那一天,最好还有某种意识存在,能欣赏它。最好是人类的意识。
那费米悖论呢?为什么收不到外星信号?也许是他们也发明了这个东西。
他为什么还抱有乐观
他没有直接回答乐观还是悲观,而是让人看这个房间:铅笔、书、桌子、麦克风、衣服、食物,每一样都是某个人一件一件发明的。几百万人、几十亿件东西,我们继承下来的这一整套文明。
这么多东西,每一样都可能出问题,每一样都可能带来负面效应,而它居然大体上运转着。这非常不可思议,而这正是他乐观的来源,人工智能也不例外。
“我们开着车过桥,用着各种根本不知道怎么运作的技术。几百万聪明人各负责其中一小块,它大体上不出错,出错的时候我们能发现,然后改进,虽然常常改得不太理想。”(唐纳德·克努特)
但紧接着他补了一句,语气很轻:他能想到的改进办法,都要求人是理性的。而他对人是否理性这件事,信心正在流失。
采访者接话说,人也许不理性,但人很聪明,而且以自己的方式很美。多数人有对彼此好的意愿,只要有机会,爱最终会赢。
彭罗斯、意识,以及“超出知识范围”
罗杰·彭罗斯主张,人发现数学观念的方式不止是计算,图灵机做不到人脑能做的全部事情,他写过一本关于意识的书。克努特的女儿的孩子和彭罗斯的孩子在牛津一起玩过。
克努特说,他可以回答是或不是,但他没有理由。他反复强调自己在这上头没有专门知识。这是个无法回答的问题,这话的意思只是他现在看不清,还谈不上永远看不清。
他打了个比方,有多少天使能在针尖上跳舞。他说,这样的事我们可以推测,但他不希望别人说,克努特说过这个,所以他很聪明,所以那一定是对的。他真正想说的是:现在还没到能问清楚的时候。
采访者说他个人相信,人类心智的运作终有一天在科学可及的范围内。克努特说,这完全可能,我并不否认,只是你现在问我,我没法给出判断。他还接了句玩笑:也说不定宇宙是某个 AI 造出来的,智能设计什么的,全是一个 AI 干的。
他也提到约翰·麦卡锡对这类主张完全不以为然。还提到二十年前在美国艺术与科学院的一次研讨会,开场就说,关于意识写过的一切都是胡扯。他不太同意这个说法,但也没打算站在哪一边。
真正让他觉得有意思的是那些实验:神经学家指出,人在意识到自己做出决定之前,信号已经送到手臂上了。还有莱斯·瓦利安特给出的一个大脑架构,以及克里斯托斯·帕帕季米特里乌一年前在《美国科学院院刊》上和另外两人发表的模型,这个模型能产生与意识实验相当吻合的结果,而且有一套机器语言,可以在上面写代码、检验假设。
他自己最认可的解释是这样的:大脑内部是一场持续的适者生存竞争。他现在说话,所有可能想说的东西都在里面,像一场投票,某一句话赢了,它影响下一句。听起来像有一个清晰的叙事在发生,像他本人在掌控,可底下可能是一堆想法在激烈竞争,最后机器编出一个连贯的故事给你,让你相信。
他把这比作文学化编程。你不知道底下在跑什么程序,但你拿到了一个好故事。
生命游戏:他不觉得自己在玩弄生命,但他会犹豫要不要关机
他说生命游戏是绝妙的东西,展示了在创造者什么都不懂的情况下,事物怎么自己演化起来。
但对他而言,生命游戏最重要的意义在别处:它把自由意志这件事说清楚了。它显然是彻底决定论的。可他说,任何有过孩子的人,都很难不相信自由意志。
康威说过,当他玩到某局特别有意思的时候,会犹豫把计算机关掉是不是不道德。
从简单的初始条件、简单的规则出发,你完全知道系统在怎么运作,它是决定论的。可只要你允许自己忘掉一部分细节,退后一步去看那些涌现出来的更大的结构,它们看起来就像活的。
采访者接了一句:如果宇宙是有限的,那我们其实都活在生命游戏里,只是更快一点。
至于能不能用分析算法的方法分析生命游戏,克努特说可以,他在第四卷第六分册里有十几个习题就是干这个的。他还提到比尔·戈斯珀的算法让 Golly 跑得快了成千上万倍。
把最讨厌的事放在最前面
他的时间安排原则是:每天先做待办清单上他最讨厌的那件事。周末回头看,会很愉快,因为讨厌的事都干完了。
他妈妈教过他另一条,跟搞机器人的人说的“规划”不太一样:看见有什么需要做,就去做。别先排个一二三,看见了就捡起来。
这两条怎么并存?他的说法是,任何时刻手上都有一组任务,那么一个好的启发式是,先做那个你最不想做、而且再拖也不会做得更好的。有些事确实先做别的会更好,有些则相反,拖久了会把当初的思路忘掉,反而更难。
他刚写完书里一个很难的部分,现在在做比较有意思的部分。但写书的时候,他希望读者觉得他全程都很快乐,语气是向上的,可以有幽默,可以说这个很酷。他得把过程的痛苦藏起来。通往那种兴奋的路是痛苦的。
疫情期间他的效率掉了一半。原因是沟通全靠写字,而写字慢。他不愿意发出一句写得不好的话,所以要反复读、反复改。还有图书馆关门之类的事。
给年轻人的建议:人生是二分查找
他的建议跟以前一样:不要因为某个东西流行就去做,要做你个人觉得被召唤去做的事,而不是别人期待你做的事。
怎么知道自己被召唤了?试试看,成了或者没成,你都会更了解自己。人生就是一次二分查找。试一件事,发现自己在这上面有底子,或者发现只要再努把力就能做到。试另一件事,发现自己完全没有直觉,看起来跟自己不搭。
他也承认自己有个反向的毛病:看见所有人都在往一个方向走,他会想,竞争有点激烈。
多数时候他只是玩自己觉得有意思的东西,后来才明白,他学到的最重要的一件事是怎么对几乎任何事都产生兴趣,怎么不无聊。
“听见小孩说这个好无聊,我会很难过。一个人如果不得不承认自己没能力从一件事里找到有意思的地方,他应该为此不安。”(唐纳德·克努特)
他说这是一种技能,一种还没学会怎么享受生活的表现,非得有人来娱乐自己才行。
他提到在新冠疫情最严重的时候,很多天他见不到任何一个人,但他照样能找到别的办法,那段时间他过得相当愉快。那天他提前几分钟到了福斯特城,在附近走了走,在几个街区外的家得宝苗圃看见一些很美的花。
“生活充满这样奇妙的东西。有时候我就坐在那儿盯着树看。”(唐纳德·克努特)
生命的意义
他先拿 42 挡了一下,然后说了实话。
他信上帝存在,虽然他说自己完全不知道这句话是什么意思。他相信有某种超出人类能力的东西存在,也许是某种 AI,不管是什么。他相信自己可以去摸索怎么和那个存在想要他做的事共振。
他说自己不敢说接近过,但他在努力。他做的选择不带策略性,他只是试着想象自己在遵循某个人(那个存在)的意愿,哪怕他聪明不到能弄明白那是什么。
那个存在大概聪明到能给他线索,也能让从一个线索走到下一个线索的整段路程变得有趣。
他最近读肯·福莱特的《来自圣彼得堡的人》,讲一战前的那段。书里丘吉尔看到德国把黄金储备全砸进了军备,英国一旦被打就完了,所以想让俄国从另一侧夹击,因为德国没有足够的兵力两线作战。而俄国有个无政府主义者认为,战争是领袖发动的,死的却是普通人,所以他要去刺杀来访的俄国亲王,让沙皇不签这个同盟,这样成千上万本来不会死的俄国人就能活下来。
他读完的感受是:如果他被放在一个要为他人生命负责的位置上,大多数情况下,他对自己的任何一个决定都不会有信心。这些问题对人类来说太难了,对他更是。
采访者补了一句:对自己没把握,同时又必须做出决定并承担它,这两件事合在一起其实是件好事。
克努特没有反驳。他说他对人有信心,伟大的领导者会出现,帮助建设一个更好的世界,这就是民主的希望。采访者说,希望我们能用手上的算法增强他们的能力。
节目最后,他留给听众的是一句他自己说过很多次的话:
“科学是我们理解得足够透彻、可以讲给计算机听的东西。艺术是我们做的其它一切。”(唐纳德·克努特)
这句话可以当作整场对话的注脚。他这一辈子在做的,就是把尽可能多的东西讲清楚到能让机器执行,剩下的那些讲不清的,就留给艺术、留给幽默、留给凌晨三点因为一个分母里的小质数而突然看清的一整套理论。
- 手上有一个具体问题卡住的时候,去翻纯理论的文献。克努特写不出字符串匹配的高效程序,最后是库克一个关于栈自动机的抽象定理给了他那把钥匙。理论和实践的距离,往往比你以为的近。
- 设计任何反馈机制之前,先想清楚你要的是哪一种结果。学习系统的奖励函数决定了它学会什么:只惩罚错误,它学会不输;奖励妙手,它学会冒险。克努特的井字棋收敛成了必和,他自己说得很明白。
- 做一个东西给别人用之前,先问一句“你怎么知道它是对的”。Copilot 这类工具最好用在你能独立验证结果的地方,别用在验证成本高于重写成本的地方。
- 优化之前先测量。写代码时觉得最难的那几页,往往不是机器花时间最多的地方。Fortran 编译器八成时间在读注释卡,这个事实只有跑起来才知道。
- 给自己的代码写给人看的部分。检验标准很简单:一年后你还能不能轻松读懂。做不到,就重写。
- 每天先做清单上最不想做的那件事。拖不会让它变简单,只会让你忘掉当初的思路。
- 把“不无聊”当成一项要练的技能。觉得一件事没意思,多半是自己还没找到看它的角度。
- 试错是二分查找。不要因为某个方向流行就走进去,也不要因为竞争激烈就绕开。想清楚自己有没有那个直觉,然后动手试一次,答案会比任何规划都清楚。
本集资源与出处
- 视频在 YouTube 收看
- 音频官方 MP3 直链来自 lexfridman.com 官方播客 RSS
- 官网Lex Fridman 官网本集页面