什么是随机数生成器?

随机数生成器(RNG)是产生随机数或伪随机数的系统。下一个数字能否真正被预测,取决于生成器的类型。有些生成器依赖物理过程,有些用算法计算数字,还有一些算法被设计成:即使有人看过它们的输出,也无法预测接下来会出现什么。看起来随机与不可预测是两种不同的性质,本文的大部分内容讲的正是这一区别。

硬件随机数生成器

硬件随机数生成器(HRNG)也称真随机数生成器(TRNG),它的数字来自物理过程。最古老的硬件随机源就在我们眼前:抛出的硬币、掷出的骰子、轮盘。力学可以完整地描述其中每一种,但制作精良的轮盘在实践中仍然无法预测,因为每次旋转起始状态的微小差异会放大成完全不同的结果。

现代硬件生成器则测量微观现象:电子电路中的散粒噪声和热噪声、大气噪声、量子效应。它们是良好的熵源——熵即可以测量的不可预测性——但物理源本身并不完美:它可能有偏差、可能随时间漂移,也可能失效,因此必须评估和监控其质量,并在必要时对输出作进一步处理,NIST SP 800-90B 等标准描述的正是这些。硬件源用于最需要保证的地方——首先是密码学,它们为传输层安全(TLS)等协议背后的密钥提供不可预测的初始材料。

伪随机数生成器

物理设备之外的另一种选择是算法。伪随机数生成器(PRNG)产生的序列看起来随机,却完全由一个称为种子(seed)的初始值决定。把同一个种子交给同一个算法,每次都会得到同一个序列。在结果必须不可预测、而种子或内部状态又可能被猜出或重建的场合,这是弱点;在结果必须可复现的场合,这又是优点——模拟或测试可以原样重新运行。PRNG 还速度快、成本低、易于实现,因此大多数软件都依赖它们。著名的算法家族包括线性同余生成器(LCG)、xorshift 生成器和梅森旋转算法。

梅森旋转算法

梅森旋转算法(Mersenne Twister)由松本真(Makoto Matsumoto)和西村拓士(Takuji Nishimura)于 1997 年发表,是使用最广泛的伪随机数生成器之一,也是许多编程语言的默认选择。它的名字来自其周期——序列开始重复之前的长度——在标准变体 MT19937 中,这一周期是梅森素数 219937 − 1。它能通过大多数随机性统计检验,很适合用于模拟。但它并不是为保守秘密而设计的:根据 624 个连续的 32 位输出,任何人都能重建它的内部状态并预测之后的每一个值,因此不能把它用于密钥、密码或任何必须保持不可预测的东西。

密码学安全生成器与熵

许多应用同时需要两样东西:算法的速度和物理源的不可预测性。答案是密码学安全伪随机数生成器(CSPRNG)。它仍然是 PRNG,但其构造使得看到部分输出也没有切实可行的办法预测其余部分;它的种子来自真实的熵源,并定期重新播种。来自物理源的种子并不能让普通 PRNG 变得安全,算法本身必须为此而设计。热噪声或硬件事件的时间等物理源提供少量真正的随机性,CSPRNG 则以快得多的速度由此得到一长串数值。这种组合正是操作系统提供给其上运行的程序的东西,也是如今“随机数生成器”在实践中通常所指的含义。它用来生成加密密钥、会话令牌和密码。

浏览器中的随机数

JavaScript 为网页提供两种内置的获取随机值的方式,它们属于不同的类别。Math.random() 是普通的 PRNG:语言标准把算法交给各浏览器自行决定,对安全性不作任何承诺——用于动画没问题,用于可能有人质疑的抽签则不合适。另一种是 Web Crypto API。它的 crypto.getRandomValues() 方法返回密码学强度的随机值,由以操作系统的熵为种子的 CSPRNG 产生。

我们的在线随机数生成器每次抽取都使用 Web Crypto API,数字在您的浏览器中生成,而不是在服务器上。本站其他生成器也由同一随机源驱动,无论您是掷骰子、抛硬币还是生成密码。

从随机比特到指定范围内的数字

密码学安全的生成器只是公平抽取的一半。它提供原始比特,程序还要把它们转换成您所需范围内的数字——偏差正是可能在这里混进来。假设随机源以相等的机会给出 0 到 9 的值,而您需要一个 0 到 5 的数字。取除以 6 的余数看起来很自然,但这样 0、1、2、3 各有两种出现方式,而 4 和 5 只有一种,于是 0 到 3 中每个数字的机会是 20%,4 和 5 各只有 10%。消除这种偏差的一种方法是丢弃不合适的值并重新抽取;随机数资料对此有详细说明。

还有两件事常让人意外。重复是正常的:从 1 到 10 中独立抽取一个整数、每个数字概率相等时,刚抽出的数字再次出现的机会与其他任何数字一样,都是 1/10。不重复的抽取是另一种抽取,并不“更随机”。而且,仅有公平的生成器并不能让整个过程公平:参与者名单和尝试次数同样重要,我们的如何随机抽取获奖者一文对此有说明。