冯·诺依曼——现代计算机架构的设计者——1951 年留下一句名言:「任何以为用算术方法能产生随机数字的人,当然是生活在罪恶之中。」他不是在骂谁,是在陈述一个结构性事实:计算机是一台彻底的确定性机器。同一个程序、同一个输入,永远给同一个输出。让它产生「随机」,等于让它做自己最做不到的事。
但你的代码里 random() 每天都在跑。它在干什么?答案是:假装。
线性同余:一个除余数的轮回
最经典的假装方案叫线性同余发生器(LCG)——大白话讲,就是一条递推式:
下一个数 = (a × 当前数 + c) mod m
从某个「种子」(初始值)出发,反复套这条式子:乘一乘、加一加、除一除取余数。余数的序列看起来忽大忽小、毫无规律——拿它当随机数用。所谓 random(),多数时候就是这么一条式子在空转。
注意三个直接推论:
- 种子决定一切。同一个种子,序列一模一样,分毫不差。所以调试模拟程序时固定种子,bug 就能复现;两个玩家用同一个种子,就能生成同一张游戏地图(《我的世界》的世界种子就是这么来的)。
- 必然循环。状态只有 m 种取值,最多走 m 步必然撞回旧值,然后整个序列原样重播。这个长度叫周期——大白话讲,这盒「随机磁带」有多长,播完就倒带。
- 它不是随机,是「像」。序列从头到尾被三个常数和一个种子钉死。第一章的压缩语言说:它的柯尔莫哥洛夫复杂度极低——整条「随机」长河,用一行式子就能完整描述。
假装会露馅:RANDU 的灾难
「看起来像」和「经得起看」之间隔着深渊。1960 年代,IBM 在大型机上标配了一个 LCG 叫 RANDU,参数没选好,留下了计算机史上最著名的随机数事故:把 RANDU 连续生成的三个数当成三维坐标画点,所有点竟然整整齐齐躺在 15 个平面上——三维空间里本该均匀撒开的点,实际是一摞平行的薄片。
后果很实际:当年大量用 RANDU 做的蒙特卡洛模拟(第十章会讲这种方法),结果都有系统性偏差——你以为你在整个空间里均匀采样,其实只采了 15 片「夹层」。无数论文的结论建立在这种「假均匀」上。 Marsaglia 后来那句刻薄的总结成了行规:「随机数发生器不该是随便选的参数凑出来的。」
大白话:伪随机数的可怕之处不是它假,而是它假得很有结构——肉眼看是噪声,放进正确的维度里看,是整齐的栅栏。用它做模拟,等于在一把弯尺上量布。
现代的假装:又好又长的磁带
今天的默认选择(Python 的 random、Excel 的 RAND)是梅森旋转(Mersenne Twister)——大白话讲,一个把内部状态搅得极其充分的伪随机发生器。它的周期是 2^19937 − 1,长到把宇宙里每个原子每纳秒取一个数、取到宇宙热寂也播不完;统计性质通过了几乎所有常规检验;而且快。
但它依然是「磁带」:知道种子就能重播全部,观察到足够多的输出(624 个连续输出)就能反推出内部状态、预测未来所有输出。所以它从头到尾都在文档里印着警告:不可用于密码学。模拟、游戏、洗牌尽管用;密钥、令牌、抽奖——碰都别碰。这个分水岭第十一章会正式接管。
假装也是一种美德
别急着瞧不起伪随机。「确定地假」恰恰是它的核心价值:
- 可复现:科学模拟里,「同样的种子,同样的结果」是别人能验证你工作的前提。真随机反而没法写论文。
- 可测试:软件测试里,固定种子的伪随机让「随机输入的测试」也能稳定复现 bug。
- 可共享:一个 64 位种子顶一部世界的说明书——分布式系统不用传输整张地图,传一个种子就够。
伪随机的定位想清楚了其实很优雅:它提供的不是随机性,是「按方抓药的复杂性」——一种低成本、可复现、统计上足够好的「乱」。它欠的那一味药——真正的不可预测——得去物理世界里找。下一章,我们去看看真随机从哪里来,以及怎么验货。