随机数生成器
结果
生成的随机数
- 可能的取值个数
- 100
- 至少出现一对重复数字的概率
- 9.65%
随机数生成器从一个起点出发,生成你指定区间内的一串整数。这一页收三个东西——区间、要几个数、以及作为起点的随机种子——然后原样吐出那么多个整数,而且同一个种子永远给出同一串数。这正是它相对于随手一个随机函数的用处:把一个一次性的结果变成可以交给别人复现、可以当面重跑、可以拿来当测试用例的东西。关掉重复,结果就从「序列」变成「抽取」,同一个值不会出现两次;开着重复,每个位置各自独立地取一个值,这才是骰子、摇号机、可能重复的播放列表的诚实模型。面板还会给出这个区间一共有多少个不同的值,以及允许重复时撞上同一个数的概率。
公式
next = (state + 0x6D2B79F5) 经乘法移位混合 → value = minimum + ⌊next × (maximum − minimum + 1)⌋
- seed
- 生成器的起始状态,可以取 0 到 4294967295 之间的任意整数。它本身不是随机源,只是这串数开始的地方——这正是同一个种子永远给出同一串数的原因
- state
- 运行中的状态,每生成一个数就更新一次。更新用一个固定常数和几步乘法移位把状态搅乱;让它看起来无关的就是这几步,要把它简化掉,第一个坏掉的就是它
- minimum, maximum
- 区间的两个端点,都包含在内。区间的宽度就是这个小数被缩放的倍数,所以区间里每个整数被取到的机会一样
- ⌊ ⌋
- 向下取整。生成器给出的是 [0, 1) 上的小数,取整把它落到区间里的某一个整数上
- count
- 要生成几个数,最多 200 个。关掉重复时它不能超过区间里的取值个数——抽出来的子集不可能比池子还大
需要一串数而不是一个数的时候用它:手工核对用的抽样、每次跑都必须一样的测试数据、需要当众重跑一次的抽签、或者占位数据。种子能固定下来,正是它比随手一个随机函数更合适的地方——种子把一个一次性的结果变成可以共享、可以复查、可以拿出来讲道理的东西。同一个值不允许出现两次时(抽签、从名单里抽样、洗牌)就把重复关掉。两件事不要用它做:它不是不可预测的来源,凡是需要「谁都猜不到」的场合都不该找它;它也不等于对真实总体的随机抽样——它只知道区间,对总体一无所知。
算例
1 到 100 里取 5 个数,再来一次还是这 5 个
- 1 到 100 一共有 100 个不同的值,所以每次取数都是 100 里挑一个
- 种子 1 确定了起始状态,五个数各自把它更新一次
- 把每次得到的小数向下取整落到 1 到 100 里,依次得到 63、1、53、99、97
这五个数不是巧合,也不是事先存好的表:它就是种子 1 的结果,在种子那一栏重新填 1 会一模一样地再给一次。注意 9.65% 说的是区间与个数,不是这一次抽取——从 100 个值里抽 5 个,撞上一对的概率略低于十分之一,至于这一串里有没有撞上,那是另一回事。把种子改成 7 会换一整串数,而这个百分比纹丝不动,这是看清「面板上哪一半是随机的、哪一半是算出来的」最干净的办法。
1 到 49 里取 6 个数,不许重复
- 池子是 49 个值,要从里面不放回地取 6 个
- 每取一个新数,如果池子已经给过它就重取,所以一个值不可能出现两次
- 按抽取顺序读出来就是 23、16、33、31、8、11
把重复关掉并不会改变 27.26%,而这正是把它印在这条算例旁边的意义:它是「6 次抽取如果允许重复,撞上的机会有多大」,与你有没有禁止重复无关。它也说明了为什么大数目下的不重复抽取会越来越贵——49 个池子取 6 个,已经超过四分之一的无限制抽法会撞号;而 365 个池子取到 23 个,这个数就过半了。列表按抽取顺序给出而不排序,就是为了同一个种子加同一组设置永远复现同一串数。
区间里只有一个值
- 区间里只有一个值,所以每次取到的都是它
- 撞号概率问的是 4 次抽取撞上的机会,池子只有一个值时必须撞上
- 结果是 7、7、7、7
这是随机数生成器不再随机、退化成常数的边界,值得记住,因为这是唯一一种「还没按就已经能全说准」的情况。它同时也是不重复模式的边界:同一个区间下要取 2 个不重复的数,那不是一个短一点的列表,而是一个做不到的请求,这一页会直接说出来,而不是悄悄少给一个数。
局限
这些数不能用在任何需要「谁都猜不到」的地方。它用的是普通软件里那种又快又小的种子式算术序列:知道种子就能复现整串数,知道其中几个值就能推出其余的。所以密码、密钥、令牌、一次性验证码、带钱的抽奖,以及任何参与者不该能预测或还原结果的场合,都不该用它——那些场合只有密码学随机源才是对的答案。撞号概率也不是对「你眼前这一串」的判断:它是在「各次抽取独立且均匀」的前提下,算这一组抽取里至少出现一对相同值的概率,而且无论你有没有禁止重复都会给出来。它的实际含义恰好是容易反直觉的那一面——池子大小的平方根量级就足以让撞号变得很可能,所以 365 天里取 23 个生日就已经过半。单次最多 200 个数,且个数超过池子大小时不重复模式无解。
常见问题
- 这些数能用在抽奖、密码或者安全令牌上吗?
- 不能。它是普通软件里那种种子式算术序列,不是密码学随机源:知道种子就能复现整串数,知道其中几个值就能推出其余的。凡是参与者不该能预测或还原结果的场合——带钱的抽奖、密码、密钥、令牌、一次性验证码——都要换成密码学随机源。而抽签、抽样、游戏、测试数据这些「可复现是优点而不是漏洞」的场合,它才正好合适。
- 种子是干什么的?为什么每次给我的数都一样?
- 种子就是这串数开始的状态,所以同一个种子永远给出同一串数,这是有意的:它让结果可复现,而这正是你要把一份抽样交给别人、当众重跑一次抽签、或者不让测试用例漂移时需要的。换一个种子,同一组设置就换一串数。想让每次都不一样,就在两次之间改种子——你填进去的值会跟着结果一起给出,所以喜欢的那一串随时能找回来。
- 允许重复的时候,为什么还会真的撞上?
- 因为每次抽取互相独立。允许重复时,每个位置各自从区间里挑一个值,不管别的位置挑了什么,而这正是骰子、或者把球放回去的摇号机的诚实模型。面板上的撞号概率就是「至少有一对相同」的概率——100 个值里取 5 个约 9.65%,从 365 个值里取 23 个就过半了。同一个值不允许出现两次时(抽签、从名单里抽样、洗牌)就把重复关掉。
- 禁止重复以后,撞号概率会变小吗?
- 不会,而且面板是故意照旧显示的。它回答的正是你拨动那个开关时真正关心的问题:如果不禁止重复,本来有多大机会撞上。1 到 49 里取 6 个数是 27.26%,所以禁止重复挡掉的是一份真实存在的风险,不是什么理论上的可能性。它也解释了大数目下的不重复抽取为什么会变慢乃至无解——个数一旦超过区间里的取值个数就根本没有合法答案,这一页会直接说明,而不是返回一个短一点的列表。
- 一次能生成多少个数?区间有什么限制?
- 一次最多 200 个数;区间端点必须是整数,且最小值不能大于最大值。取值本身可以是负数,所以 −50 到 50 这样的区间是可以的。端点是非整数时会直接报错而不是四舍五入:在不重复模式下,从一个含小数的区间里抽取会让「不重复」这条规则失去意义,而一个看起来合理、实际却违反了自身设置的列表,比一条错误信息糟得多。
- 生成的数会排序吗?
- 不会——它按抽取顺序打印,而这个顺序是结果的一部分。排序会丢掉「同一个种子要复现」的那份信息,并且在位置本身有意义的地方(比如排一个乱序、发一手牌)会直接改变含义。想要有序的抽取结果,复制出去自己排一遍;抽取本身保持种子给出的那个顺序。
参考资料
- GB/T 3358.1-2009《统计学词汇及符号 第1部分:一般统计术语与用于概率的术语》(推荐性国家标准,现行:2009-10-15 发布 / 2010-02-01 实施,采标;该站注明因涉及国际版权不提供在线阅读,全文需另寻) — 国家市场监督管理总局 国家标准全文公开系统
- GB/T 32915-2016《信息安全技术 二元序列随机性检测方法》(推荐性国家标准,现行:2016-08-29 发布 / 2017-03-01 实施;用来对照「随机性是被检测出来的性质,不是生成器自称的」这一点的官方口径) — 国家市场监督管理总局 国家标准全文公开系统
- SP 800-90A Rev. 1, Recommendation for Random Number Generation Using Deterministic Random Bit Generators(英文原版;确定性随机位生成器的官方建议,「种子决定整串输出」的权威依据) — 美国国家标准与技术研究院(NIST)计算机安全资源中心
- Birthday Problem — from Wolfram MathWorld(英文原版;撞号概率的闭式写法:1 减去池子大小上的下降阶乘连乘,本页 repeatChance 的依据) — Wolfram MathWorld