“这是随机的”是一种断言,不是事实。一个选择要公平,必须满足两件事:数字的来源不可预测,而且每个选项都同等可能。
两者都可以实现,两者都常被搞错,而且它们会各自独立地失败。一个工具可能有出色的随机来源却有偏倚的映射,或者有完全均匀的映射却由可预测的数字喂入。
随机性从何而来
真正的随机性来自不由先前状态决定的物理过程:
- 放射性衰变——原子衰变的时刻无法预测
- 热噪声——电阻中随机的电压波动
- 大气噪声——闪电活动产生的背景静电
- 量子测量——真正不确定的结果
计算机无法直接观测这些。它所做的,是采样一个硬件生成器——/dev/urandom 及其同类——而后者本身由物理过程播种。这才是真随机数生成器。
更常见得多的情况是伪随机数生成器:一种确定性算法,产生的序列只是看起来随机。
next = (previous × multiplier + increment) mod modulus
关键性质并不微妙。如果你知道算法和种子,你就能算出这个序列将来会产生每一个值。不是近似——是精确。这就是为什么大多数网站使用的 Math.random() 不适用于任何要紧的事:它的输出由内部状态决定,而从观测到的输出中恢复该状态,就能预测每一个未来的结果。
决定问题的中间地带
现代平台每次调用并不使用原始的硬件熵。它们使用加密安全伪随机数生成器——确定性的,但由硬件熵播种,并被设计成观测输出不会暴露状态。
| 硬件真随机数生成器 | CSPRNG | 普通伪随机数生成器 | |
|---|---|---|---|
| 基于物理熵 | 是 | 是 | 否 |
| 给定状态后是确定性的 | 否 | 是 | 是 |
| 输出会暴露状态 | 否 | 否 | 是 |
| 适合用于抽奖 | 是 | 是 | 否 |
CSPRNG 是确定性的,其含义与你使用的每一款软件都是确定性的完全一样。让它可接受的原因在于,它的状态无法从它已经输出的内容中恢复。
浏览器通过 crypto.getRandomValues() 暴露这一点。本站的抽签用的就是这个来源。
均匀性是一个独立的问题
有一个好的来源还不够。从一个随机数到某个选项的映射仍可能有偏倚,而这正是大多数工具犯的错误。

朴素的做法——把 0 到 1 的随机浮点数缩放到区间上——会让低端产生偏倚。有 40 个名字时,一些随机值会落在最后一个桶的末尾之外,如果它们被截断而不是丢弃,第一个名字出现的频率就会比最后一个略高。
这种偏倚小到没人会注意到,而这恰恰是它能在大多数实现中存活下来的原因。
正确的做法是拒绝采样:抽一次,如果结果落在不完整的最后一个桶里,就丢弃它再抽一次。分布会变得完全均匀,于是每个条目都有 1/N 的概率——包括在 40 个名字的抽签中,缩放浮点数的实现会给第一个名字略高一点的概率。
同样的技术也出现在本站的短 ID 生成器里,其接受率是 252/256,由此产生的偏倚约为千万分之二。小到无关紧要,也小到一个粗心的实现会不被察觉——而这恰恰是它值得被测量的原因。
三种不是工具责任的人为失误
赌徒谬误。 连续五次正面之后,反面感觉该出现了。每一次抽签都是独立的。硬币不记得自己是否公平,五次正面之后正确的概率仍然恰好是每面 1/6。
聚类错觉。 随机数据中的连续相同结果让人觉得有规律,因为你在找规律。连续三次相同结果的发生频率远高于直觉。而真正随机的数据在短序列里看起来是凹凸不平的——一个看起来完全均匀的序列,更可能是编造出来的,而不是真实的。
生日悖论。 23 个人里,很可能有两人同一天生日。这是算术,不是心理学,但它说明了为什么人们如此不擅长估计随机性:单个概率很小,而它们会相乘。
共同的线索是,一次观测什么都证明不了,无论朝哪个方向。一次看起来奇怪的抽签,和一次看起来整齐的抽签一样说明不了问题。
实践中最重要的两种失误
追溯了机制之后,值得点明当一次选择引发争议时实际出错的地方——而这两种都与来源无关。
操作者改写了结果。 重新抽取、手动修正、在抽签中途跳过缺席的名字。一个完美的生成器加上被修改的结果,仍然是被修改的结果,而且任何看着的人都看得出来。
候选池不是公布的候选池。 增加一个名字,或悄悄排除一个,无论算法如何都会改变选择。
两者都是流程失误,而两者都是录制抽签所能防止的。这就是为什么把参与者名单拍进画面的录制,比任何技术属性都更有价值:它同时堵死了这两者。
这种严谨程度在何处是必要的
为了选午餐,这些都不重要。任何生成器都能产生一顿可以接受的午餐。
当涉及利害、声誉或法律层面时,它就变得至关重要:
- 抽奖和比赛。 可验证的结果才是重点
- 员工或任务分配。 人们会审视流程
- 科学与临床随机化。 该领域的标准是有记录、有种子、可复现的随机性
- 监管与审计场景。 有记录的随机性常常是要求,而不是偏好
这些场景的要求都是同样的四条,而且很短:候选池固定且公布,如果有权重则公布权重,来源不可预测,结果被记录下来且候选池可见。
如何自己检查
你无法在浏览器标签页里审计一个 CSPRNG,但你可以推理机制,而这就够了。
问来源是什么。 如果答案是“一个随机函数”,就问它是不是那个可预测的函数。crypto.getRandomValues() 是一个具体、可检查的答案。
问均匀性是如何实现的。 如果答案涉及把一个浮点数缩放到区间上,却只字未提拒绝采样,那映射很可能是偏倚的。偏倚很小,但可以测量。
问操作者本可以改变什么。 如果什么都改不了,结果就是站得住脚的。如果一个名字可以被跳过,或者抽签可以被重跑,那就站不住脚——无论来源如何。
公平性页面更详细地介绍了本站的实现。如果你要为一次会被质疑的决定挑选工具,值得读一遍。