/* Auto ads 与同意门控都依赖这里的 client meta —— 缺了它 consent.js 的 loadAds() 会直接 return,本页永远不加载 AdSense(第 30 轮实测发现中文 324 页全缺) */
aibox

随机数生成器:指定范围、不重复抽取与掷骰子

从任意范围抽随机整数——1 到 100、1 到 49,或者你自己给的两个端点——并且带着大多数随机数工具悄悄缺掉的那条性质:每个数的机会严格相等。 随机来自浏览器自带的密码学随机源,而多数网站走的捷径(拿随机字对范围取模)在这里是被明确排除的:范围除不尽源空间时它就是有偏的。不重复抽取用部分 Fisher–Yates 洗牌,骰子和硬币走同一个无偏核心;每一种拒绝输入都给一句人话,而不是悄悄替你改。

四种模式,同一个无偏核心。「不重复」是抽奖抽人用的:出来的数互不相同;「可重复」是「给我五个数,允许撞车」用的。

两端都包含在内。允许负数端点——抽一个随机偏移量是正当需求。这里「1-100」和「1 到 100」是同一件事:两个框就是两个端点。

小端写在大端之上会报错,不会悄悄对调——替你换范围等于替你猜你想要什么。

有放回:同一个数可能出现多次。

你在上面输入的内容不会发给我们:抽取全程在你已经打开的这个标签页里完成, 也正因为如此它不限次数。详见隐私说明

怎么用

  1. 先选模式,再填数字。四种模式回答的是四个不同的问题,不可互换:「可重复」允许撞车(适合「从一批编号里随机取五个,重复也行」),「不重复」保证互不相同(适合「48 张券抽 3 个获奖者」)。骰子和硬币是同一个无偏核心换了个小范围。
  2. 每按一次「抽取」出一批新数。和本站的换算类工具不同,这一页不随敲键实时重抽——随机数是要你主动要的东西,正在看的这一批不该因为你碰了下输入框就消失。回车、Ctrl/⌘ + Enter 和按钮是同一个动作。
  3. 先看结果下面那两行证明。「拒绝上界」是让这次抽取公平的那个数;「消耗的 32 位字」是这次实际用掉的。想核对就对着上界自己算——它每次都按你的范围重新打印出来。
  4. 顺序不重要时看「排序后」那一行。开奖式的抽取通常按从小到大读;抽取本身保持抽取顺序(那才是源吐出来的真实顺序),排序版放在旁边对照。
  5. 出现连号是正常的。公平的随机源本来就会产生连号和空段——小样本里的均匀就是这个样子。一个「看起来分布得很匀称」的生成器,通常是做过洗牌或分层处理的另一种工具,承诺的东西并不一样。

随机数到底是怎么抽出来的

一共三步,每一步都对应一类随机数工具会踩的坑。

  1. 从密码学随机源取字。页面调用浏览器自带的 crypto.getRandomValues——平台为密码学场景维护的那个随机源——按 32 位一个字(4 字节)读,成批取。刻意不用 Math.random():它是为速度设计的伪随机生成器,不承诺不可预测,质量还随引擎和版本而变。做模拟数据无所谓;做「必须说得清怎么抽的」的抽取(抽奖、抽人、抽签),差别就是本质性的——这一页按后者对待每一次抽取。
  2. 先拒绝,再映射。偷懒的映射是 字 % 范围。只要范围除不尽 2^32,它就是有偏的,而且不是四舍五入那种小误差,是结构性的。这一页对 1 到 100 的抽取只接受 4294967200 以下的字——100 在 2^32 以内的最大倍数——其余的丢弃重取。
  3. 把接受的字映射到你的范围。接受域 [0, 上界) 里恰好装着「上界 ÷ 范围」个完整周期,每个数分到的字数严格相同。这个「相同」就是公平的全部承诺,而且是构造出来的,不是碰运气碰出来的。

具体例子:取模为什么有偏

2^32 = 4294967296。除以 6(掷骰子):4294967296 = 6 × 715827882 + 4。多出来的 4 个字意味着:在全部 2^32 个可能的源字里,映射到 1、2、3、4 的各有 715827883 个,映射到 5、6 的只有 715827882 个——七亿分之一量级的偏向,掷一百次看不出来,但它永远在数学里,而且恰好出现在大家最需要「公平」的场景:有奖品的抽取。把源换窄,同一笔账立刻变响:8 位源(256 个字)掷 d6,两边是 43 对 42——2.4% 的偏斜,几千次加一张表格就能验出来。

拒绝采样把余数扔掉而不是忍着它。代价是按 (2^32 mod 范围) / 2^32 的概率重取一次——对 d6 是 43 亿分之 4;对任何范围,期望尝试次数都到不了 2。当范围是 2 的幂(硬币、4 面骰、8 面骰、32 面骰),余数正好是零,一个字都不用扔——这也解释了为什么这个坑能藏在只掷 2 的幂面数的骰子工具里那么久。

页面给你看什么

每次结果都会把它用的上界和它消耗的字数打印出来,公平性是可核对的,不是要你记的一句话。抽「1 到 49 里 6 个互不相同的数」,你会看到上界是 4294967292 / 4294967296——2^32 比 49 的倍数多出 4——以及这一抽实际消耗了多少个字。这一页没有任何一个地方要你相信一个形容词。

不放回抽取:决定抽奖对错的那一步

「抽 5 个随机数」和「抽 5 个互不相同的随机数」是两道题,把它们弄混的方式正是抽奖抽出一人双奖的方式。有放回:每次都从完整范围里抽,重复合法;不放回:抽走的数离开池子,剩下的数重新等机会。抽奖、抽人、抽签要的几乎都是后者。

老实的实现是部分 Fisher–Yates 洗牌:把范围想成一排格子,从还没抽走的格子里等概率取一格,拿走它的数,把最后没动过的那个值补进空位。每个有序的 k 个不同数的组合,出现概率都精确等于 1/(范围×(范围−1)×…×(范围−k+1))——均匀性可以写成公式,因为算法从不重试任何东西。

更流行的另一种做法——抽到重复的就重抽——恰恰是用户能直接感到的那种错。抽的个数越接近范围,下一抽撞上旧数的概率越接近必然,代价无上界地涨;个数超过范围时它根本无法结束,也就是「抽 11 个 1 到 10,标签页卡死」那个事故。这一页把这种输入明确拒绝:要的个数超过范围能给的,是一句报错,不是悄悄少给你几个。

顺带一笔成本账,这就是用稀疏表的原因:范围可以宽到 2^32,真把 43 亿个格子铺成数组是荒唐的。只有洗牌实际碰到的格子才存进一个稀疏 Map——代价与你抽几个成正比,与范围多宽无关。从十亿里抽 20 个,和从十里抽 20 个一样快。

骰子与硬币:同一个核心的小范围

一个 6 面骰就是「从 1 到 6 抽一个均匀整数」,和「从 1 到 100 抽一个」是同一个操作——所以骰子走的正是同一套拒绝采样,只是上界按 6 重算。硬币是最小的有意思的情形:范围是 2,正好整除 2^32,上界就是整个源空间,永远不需要拒绝——唯一一个连取模都不会出偏的范围,而这一页仍然把上界打出来,让你看见它为什么公平。

有两个约定值得说破,因为它们是选择而不是定律。骰子按 1 到面数编号,不按 0 到面数−1——你从实体骰子上读到的就是这种数。硬币的两个结果在页面上写作「正面 / 反面」,核心里抽的是 0 和 1;哪个词对应哪一面是展示层的事,各一半的概率才是数学。

自定义面数是一等公民,不是附赠:7 面(星期几)、12 面(月份)、30 面、100 面全部走同一条路径,上界各自打印。这片唯一被拒绝的是一面骰:范围是 1 的抽取只有一个可能结果,把一个常数包装成随机数,是带着多余步骤的谎话。

每个上限,以及它们各自的原因

这一页上的每一条边界都是写明理由的决定,不是配置文件里冒出来的魔法数。同样的数字由核心模块强制执行——页面与测试套件共用同一份文件,所以页面不可能说一套、执行另一套。

上限数值为什么是这个数而不是别的
范围宽度≤ 4294967296(2^32)一次抽取消耗一个 32 位字,它最多能命名 2^32 个不同的值;更宽的范围需要把多个字拼起来用,而真实需求——抽奖、点名、骰子、测试编号——没有一个接近它。在上限以内,每一步都是精确的整数运算。
两个端点±1000000000000000(1e15)双精度浮点从 2^53−1 ≈ 9007199254740991 往上就开始存不准整数。端点压在 1e15——比它小的最大整十进制幂——留出九倍余量,于是「小端 + 偏移」永远落在精确整数上。(这个模块也刻意不用大整数类型,部署管线打包阶段会因为它直接失败;上限把一切留在普通数字里。)
一次抽几个≤ 10000页面上每个数都会画出来,过万之后标签页的时间花在画而不是抽。真实的抽取——获奖名单、抽人、样例编号——离这条线都差着数量级。
不重复的个数个数 ≤ 范围不放回抽样抽不出比池子里更多的数。这里按报错处理——绝不悄悄截短,因为悄悄改答案比不答更糟。要么放宽范围,要么减少个数。
骰子面数2 … 4294967296小于 2 没有可随机的对象;大于 2^32 一个字命名不了那么多面。中间的一切都是同一种无偏抽取。

这个工具不是什么

  • 不是转盘。转盘抽的是名单,这里抽的是数字;有名单就把名单编号 1 到 N,再用「不重复」抽——结果一样,但范围和「不重复」都摆在明处,而不是藏在一段动画里。
  • 不是密码生成器。按范围抽的随机整数不是密钥材料:6 位数的一次抽取只带约 20 比特熵,密码需要的是字符集策略和你选的长度,不是一个范围。真需要密码工具时,它会有自己的一页、写自己的方法。
  • 不是统计引擎。均匀不等于「分布得匀称」:连号、重复、空段正是公平的源会产出的东西;把它们抹平只会让抽取更不随机,不会更随机。
  • 不是开奖预测器。它抽数字;它对哪些数字会中没有任何意见,本站也不会有任何一页假装有。

有什么东西离开了这个页面

没有。这一页是一个静态文件,引用一份纯函数模块:抽样、拒绝采样、洗牌、计数全部在你已经打开的这个标签页里跑。不向我们的服务器发请求、不给你的范围挂统计事件、不写 cookie、不做任何存储——刷新就没了,因为它从来没有到过别的地方。对这一页来说这比换算工具更要紧:大家输入的范围常常是某种不想宣布的东西的代理——候选名单有多长、券卖了多少张、团队有多少人。

随机源本身是浏览器的,不是我们的:那些字我们永远看不到,你屏幕上看到的数字我们也看不到。

常见问题

随机的公平性怎么保证?
靠一条可以核对的上界,而不是一句形容词。最常见的实现是拿一个随机字对范围取模(random() % 100),但 2^32 不是 100 的倍数,多出来的 96 个字会让 100 个数里有 96 个各多出约四百三十亿分之一的机会——掷骰子更明显:2^32 = 6×715827882 + 4,于是 1 到 4 各有 715827883 个源字、5 和 6 只有 715827882 个。这个工具把不均匀的那部分直接扔掉:只接受落在「不超过 2^32 的最大倍数」以下的字,其余丢弃重取,于是接受域里每个数分到的源字数完全相同。这条上界每次抽取都印在结果下面,你可以拿计算器自己验。
不重复抽 20 个会卡住吗?
不会。不重复抽取用的是部分 Fisher–Yates 洗牌:想象范围排成一排格子,每一步从还没被抽走的格子里等概率取一格,取走后把最后一格补进空位——每抽一个数,代价就多一份,跟范围多大无关,所以从 1 到 10 亿里抽 20 个和从 1 到 10 里抽 20 个一样快。会卡住的是另一种做法:「抽到重复的就重抽」——抽的个数越接近范围,撞上已抽过的数概率越高,个数超过范围时永远抽不完。所以这一页把「要抽的个数超过范围」做成明确的报错,而不是悄悄少给你几个。
转盘抽签和这个是一回事吗?
转盘抽的是名单,这个抽的是数字——但你的名单可以变成数字。把名单编号 1 到 N,用「不重复抽取」从 1 到 N 抽 N 个(或者抽几个要几个),编号落到谁就是谁。这样比转盘多两样东西:范围和不重复都是明摆着的,抽完可以按同样条件复算一遍核对;转盘动画既不给你这两样,也没法复核。
和 Excel 的随机数一样吗?
不一样,差别在随机源和可核验性。Excel 的 RAND 和 RANDBETWEEN 是伪随机生成器,为速度和统计分布设计,不承诺不可预测,具体质量随版本和平台而变——做模拟数据、快速估算完全够用;但要支撑「必须能说清怎么抽的」的场合(抽奖、抽人、抽签),更稳的是用浏览器自带的密码学随机源、并把采样方法写在页面上。这一页用的是后者,方法全在正文里。
一次抽一个和一次抽完有什么区别?
区别就是「有放回」和「不放回」,抽奖时这是要命的区别。一次抽一个、每次都从全部号码里抽(有放回),同一个人可能中两次,后面的抽取也不受前面影响;一次抽完 N 个互不相同的数(不放回),每人最多中一次。要抽多名获奖者,就用「不重复抽取」一次抽完,或者用递减的个数分几次抽——不要在「独立抽取」模式里连按 N 次当成抽 N 个获奖者。
范围能开多大?个数有上限吗?
范围最多 4294967296 个数(2^32):一次抽取消耗一个 32 位源字,它最多能命名这么多不同的值,而真实需求——抽奖、点名、骰子、造测试编号——都远低于它。两个端点各自不超过 ±1000000000000000(1e15):双精度浮点从 9007199254740991 往上就开始存不准整数,留 9 倍余量是为了保证每个抽出来的数都是精确整数。一次最多抽 10000 个:页面上每个数都要画出来,再多标签页就忙于渲染而不是抽样。三个上限都写在正文的表格里,各有各的理由。
我输入的范围会被上传或保存吗?
不会。这一页只是一个静态文件,引用一份纯函数模块:抽样、拒绝采样、不重复洗牌、计数全部在你已经打开的这个标签页里跑。不向我们的服务器发请求、不给你的范围挂统计事件、不写 cookie、不做任何存储。也正因为不经过服务器,它不限次数——刷新页面,一切都没了,因为它从来没有到过别的地方。

这一页不打包任何抽样代码。上面每一次抽取、每一条上界、每一个被丢弃的字, 都来自测试套件对着固定字节源跑过的那同一份纯函数模块 —— 所以不可能出现「页面上是对的、测试里是错的」这种偏差。