下一章 上一章 目录 设置
21、第21章 噬嗑•咬合 数学与代码的啮合 悦儿和墨子 ...
-
回到剑桥市的第三天,悦儿收到了墨子的一封长邮件。邮件的主题只有两个字母:"Viz"。正文没有多余的话,直接贴了一段描述和一个附件。描述写的是:"我在想能不能把挂谷猜想的证明过程变成一个可以交互的模型。不是静态的图解,是动态的、可以缩放和旋转的。每一个引理对应一个视觉层级,用户从顶层往下钻,钻到管道几何的细节层。你愿意一起做吗?"
悦儿在书桌前读完了那封邮件。她喝了口茶,然后打开附件。附件里是一份草图,大概七八页的粗糙原型设计——用简笔画的界面布局、滑动条的位置、视角切换的示意箭头。墨子的草图风格跟他写代码的注释一样干净而直接:没有多余的装饰,每一个元素都有明确的标注,相邻元素之间的逻辑关系被箭头和短句写清楚了。她看着那份草图,注意到他在每一页的边缘都留下了空白的边栏,像是留给她填注释的位置。
她回复了三个字:"怎么做?"
墨子在两小时后发来第二条消息:"先做管道层。把你的多尺度归纳拆成'尺度'这个独立变量。每一层尺度对应一组管道。然后用户在界面上拨动尺度滑动条,从最粗的尺度一路缩到最细的尺度。中间所有的过渡是连续的。"
悦儿看着那行字,在脑子里想象他描述的那个画面。从宏观到微观。管道在每一个尺度上呈现出不同的密度、不同的重叠指数、不同的交叉模式。她用笔在草稿纸上画了一条线,从左到右标了"宏观"到"微观",然后在线的中间位置画了几个小圆圈,代表几个关键尺度。那些圆圈的大小不等,间距不等,但连接它们的线是连续的。
她拍了那张草稿的照片发给墨子。附了一句:"线是连续的,但中间的关键节点需要可标记。"
墨子回了一张截图——他在她的草图基础上用代码生成了一条带节点标记的连续曲线,每一个节点都对应一个可交互的按钮。节点之间的过渡是渐变式的,颜色从浅蓝渐变到深蓝再渐变到浅绿。悦儿看着那张截图,把两个显示器并排放好,一个显示墨子的代码界面,一个显示她自己的数学文档。她第一次感觉到这两个屏幕之间有一种可以被翻译的语法。
第一次碰头是在悦儿的公寓里。墨子带了两台笔记本电脑,他用自己的电脑开了三个窗口——代码编辑器、模型预览界面、版本控制终端。悦儿用她的电脑打开了一篇她新写的文档,里面列出了三维挂谷猜想证明的核心结构,按照层级分类:地基层(粘性情形)、中间层(阿苏阿德维数)、顶层(归约框架)。她把这三个层级画成了一个纵向的结构图,每一条引理都标注了编号和依赖关系。
"你的数学是线性的,"墨子看着那个结构图,手指在触摸板上滑动,把纵向的图旋转了九十度变成了横向的,"一行接一行。引理1依赖定义1,引理2依赖引理1,定理1依赖引理1到引理13。整篇论文是一条链。"
"对。"悦儿说。
"但在视觉里,不能一条链一条链地放。用户看到链的时候,记不住前面的节点。"墨子把横向图又旋转了一下,变成了一种放射状的结构——中心是最终结论"三维挂谷猜想得证",向外辐射出三个大圈,大圈里面又分成若干个小圈,每一个小圈对应一个引理。"你需要一种结构,能让用户从任何一个点进入,然后从内部向外走,或者从外部向里走。不能只有一种方向。"
悦儿看着那个放射状结构,沉默了一会儿。"你的意思是——同一组信息,需要多种入口。"
"对。数学论文只有一个入口:第一页第一行。但可视化需要有无数个入口。用户从任意一个引理点进去,向前走能看到它证明了什么,向后走能看到它依赖什么,向上走能看到它在整个证明里扮演什么角色。"
悦儿把那个放射状结构截图保存了下来。她重新打开自己的文档,把原本纵向排列的结构图改成了横向排列。每一层的引理被她重新编号,按照"依赖深度"分了五组。她写完之后把新的结构图发给了墨子。
墨子花了大概四十分钟把那张新的结构图翻译成了代码里的数据结构。他用一个嵌套的字典来存储每一层引理的信息,键是引理编号,值是依赖列表和被依赖列表。悦儿坐在他旁边,看着他敲代码的时候手指在键盘上移动的速度比她写字快得多。有些行她需要读两遍才看得懂,他只是敲过去,每敲完一段就缩进、分行、加注释。
"你写代码的时候在想什么?"悦儿问。
墨子的手指没有停。"在想怎么把你说的话翻译成机器能读的结构。"
"数学论文也是结构。但它是一种用来被人类读的结构。"
"对。"墨子的手指在回车键上敲了一下,新起了一行,"代码是另一种结构。机器读它的时候不关心美不美。它只关心行不行得通。"
悦儿看着屏幕上那些被缩进和括号组织起来的代码行。那些行跟她在纸上写的公式排布方式完全不同。公式是横向铺开的,从左到右,像河流在平原上流动。代码是纵向叠放的,一层嵌一层,像一座被挖开的地层剖面图。但它描述的东西跟她的公式描述的是同一个东西——依赖关系、层级结构、从宏观到微观的连续过渡。
"行得通吗?"她问。
墨子停下手指,转过屏幕给她看预览界面的第一次渲染。画面上出现了一个浅灰色的三维空间,空间里悬浮着几十根细长的彩色管道。管道从画面中心向外延展,像一朵被冻结在绽放过程中的金属花。颜色从中心的深红色渐变到边缘的浅蓝色,对应于从低尺度到高尺度的重叠指数。
悦儿凑近屏幕看了看。"方向数少了。"
"这是一个测试样本。实际数据集有几千根管道。渲染引擎要优化才能同时显示几千根还不卡顿。"
"那先测试一千根。"
墨子把代码里的数组长度从"50"改成了"1000",然后重新运行。画面卡了大约两秒,然后管道网络重新出现——比刚才密了二十倍,所有的管道交错、穿插、重叠,像一团被压缩过的彩色纤维在三维空间中展开。悦儿伸手用鼠标旋转视角,那团纤维在她的操控下缓慢转动,从各个角度展示着它的内部结构。
"你看到了什么?"墨子问。
悦儿把视角停在一个特定的角度上。在那个角度下,管道网络中出现了一条极细的空隙带——一根细线般的透明区域在所有管道的交错中贯穿了整个结构。"这个位置,"她用手指指屏幕,"在三维的证明里,这是我花了最长的时间去处理的那个坏管逃逸路径。它在视觉上是一条通道。"
墨子在那个位置加了一个标记点。标记点的颜色是金色的,在深红和浅蓝的底色中极为醒目。"好。那我把它单独摘出来,让用户可以点击这个标记点直接跳转到对应的引理。"
悦儿看了他一眼。"你不需要我解释那个引理的内容?"
"我需要。但解释可以放在弹出窗口里,用户点击的时候才显示。不要一开始就把所有信息堆在屏幕上。"
接下来的几周里,悦儿和墨子每周碰三次面。每次三到四个小时。悦儿负责把证明中的每一个引理用最简短的文字概括成一个标签——一个标签的长度不超过一句话。墨子负责把那些标签嵌入到可视化结构的对应节点上。他们之间的对话变成了一种持续不断地翻译和回溯的过程——悦儿说"这一段证明本质上是在证明管道束的直径不会随尺度缩减而无限分裂",墨子说"那我把它翻译成一个宽度约束,用户拖动尺度滑动条的时候管道的直径被这个约束夹住"。悦儿看他的实现,有时候说"你的约束在极小尺度上放宽了百分之十",墨子就回去改参数,调完再给她看。
第三个星期他们遇到了第一个真正的障碍。悦儿发现可视化中的管道重叠指数跟数学定义中的重叠指数之间有一个精度误差——在最小尺度上,可视化渲染出来的重叠指数比理论值低了大约百分之八。那个误差肉眼几乎看不出,但在她检查数据输出的时候发现了。
"这是渲染的近似导致的,"墨子说,"离散化采样的时候,最小尺度的管道数量过多,计算机的浮点数精度不够。"
"但百分之八的误差会让用户在最细尺度上看到一种'虚假的空隙'。那个空隙在数学上不存在。"
墨子调出代码的核心渲染函数,把浮点数精度从单精度切换成了双精度。重新运行后误差从百分之八降到了百分之零点三。悦儿说"还是有一点",墨子又把采样密度提高了三倍,渲染速度下降了一半,但误差降到了百分之零点零三。悦儿看着屏幕上新渲染出来的管道网络,最小尺度的颜色从浅蓝色变成了近乎饱和的深蓝色——"这就对了。这个颜色对应了理论值的最优估计。"
墨子保存了那一版。然后在代码的顶部加了一行注释:"精度修正。双精度+三倍采样。原因:数学不容忍百分之零点三的误差。"
悦儿看到那行注释的时候,在注释的下面用键盘打了一行字:"数学也不容忍百分之零点零三。但视觉可以。视觉有容忍度。数学没有。"
墨子看了那行字,在下面又加了一行:"那就在视觉中保留百分之零点零三的误差但标出来。用一条警告线标示近似区域的边界。"
悦儿看着屏幕上那条新增的金色虚线——它在最小尺度的管道网络外围画了一个极细的圈,圈内的颜色是深蓝色的理论最优估计,圈外的颜色是稍淡一些的近似值。"这条线我可以接受。"她说。
那是他们第一次在"精度"和"可读性"之间找到一个平衡点。那根金色的虚线像一道缝合线,把数学的严格和代码的近似缝在了一起。两边都有让步——数学让出了一小片可以被标注为"近似区域"的边界,代码让出了双倍的计算资源去逼近那个不可达的精确值。
五月末的时候,他们做出了第一个可用的原型版本。用户打开程序后进入一个三维空间,空间中悬浮着挂谷猜想的完整管道网络。屏幕左侧是一列滑动条,控制着尺度、视角旋转角度、管道的透明度。右侧是一个信息面板,实时显示当前视角下的重叠指数、管道束数量、尺度层级编号。用户可以从任何一个尺度进入,用滚轮放大到管道内部的细节,也可以用鼠标拖动旋转从外部观察整体结构。
悦儿坐在电脑前面操作了大约四十分钟。她从一个最小的尺度开始,慢慢把滑动条推上去,看着管道网络在她的操控下从稠密的核心区域逐渐疏散、膨胀、重新排列。每推到一个节点,信息面板就会显示对应的引理编号和简要解释。她滑到坏管分类的位置,信息面板弹出了一行字——"坏管无穷逃逸路径"后面跟着一个链接,点开之后是一段她自己写的文字解释,不到两百字,写着坏管为什么最终会被粘性结构覆盖。
她看完那段文字,把滑动条推到了最顶部。整个管道网络缩成了一个极小的、完整的三维球体。球体表面光滑,颜色均匀,所有的方向都被压缩在了里面。她看着那个球体转了几圈,然后关掉了程序。
"做得很好。"她说。
墨子坐在她旁边。"什么叫'做得很好'?你之前从来不说'很好'。"
"就是做得很好。你让我看到了我写了五年但从来没看见过的东西。在证明里,管道是抽象的符号和不等式。在这个模型里,管道是真的管子,三维的,我可以转着看它。"
"那你看到什么了?"
悦儿想了想。"我看到那个证明是一棵树。根在粘性情形,树干是多尺度归纳,树枝是坏管分类和逃逸路径,叶子是最终结论。我在写证明的时候只能在一条枝上走,走完了换另一条。但在这个模型里,我同时看到了所有的枝。"
墨子把程序界面最小化,把代码编辑器重新拉了出来。他在代码文件的末尾加了一个新的注释行——"版本1.0。数学:悦儿。代码:墨衍。结构:同一棵树。"
悦儿看到那行注释,没有说话。她把那份注释截了图,存在了自己的文件夹里。文件夹的名称是"Viz",放在她所有的论文草稿文件夹旁边,像一个刚刚被开辟出来的、相邻的新地块。
噬嗑。亨,利用狱。咬合的过程需要咀嚼和克服。数学和代码之间的障碍不是在第一次尝试中就被碾碎了的,它们是被一口一口咬碎的。每一次精度误差的发现都是一次"咬",每一次参数调整都是一次"嚼"。咬合不是融合,是两种不同的硬度在接触面上彼此磨掉一些边角,然后严丝合缝地嵌在一起。悦儿和墨子的工作方式也是这样——她的数学要求极致的精确,他的代码需要可计算的逼近。两种要求在同一个问题上的相遇产生了摩擦,摩擦产生了磨损,磨损产生了新的形状。
那天墨子离开之后,悦儿重新打开了那个可视化程序。她这次没有操作滑动条,只是让它停留在初始状态——一个完整的、缓慢自转的三维管道网络。她看着那些管道在屏幕上转过一圈又一圈,颜色随着旋转角度的变化而微微改变。她看了很久,久到窗外的天色从傍晚变成了深蓝。
然后她关了程序,翻开第五十九页。她在页面底端写了一行新的笔记:"可视为树状结构。根在粘性。枝在多尺度。叶在结论。可视化证明可用。"
她合上草稿本,靠在椅背上。窗台上绿萝的藤蔓又长了一截,在夜风里轻轻晃动着。她看着它,在想明天她要把第五十九页的内容继续往前推一页。明天墨子会带着代码的新版本来,他们要在管道网络的旋转控制上再加一个惯性阻尼层。他们的合作还在继续,像一台被同时驱动的机器,两个齿轮咬在一起,转速一致,方向一致,摩擦的声音逐渐变成了一种均匀的低频嗡鸣。
噬嗑。咬合之后,障碍变成了动力。