随机抽奖工具
公平公正的随机抽取工具,支持名单抽奖、随机抽签
什么是随机抽奖?
随机抽取工具会从候选列表中随机选出一个结果,避免由某个人主观决定。它适合课堂点名、会议发言顺序、轻量抽奖、任务分配、午餐选择、团队热身,以及任何需要用中立方式做选择的场景。本页面支持一行一个候选项、滚动动画、本地抽取历史,也可以关闭重复抽取,让已抽中的项目从剩余池中移除。随机性可以在统计意义上减少偏向,但它不会验证身份、不会自动去重输入里的同名项,也不能提供正式审计证据。用于正式抽奖、合规活动、资格核验或奖品发放时,应使用有记录、有规则、有责任人的流程。
使用方法
操作步骤
- 在文本框中输入选项,每行一个
- 设置要抽取的项目数量
- 选择是否允许重复
- 点击'开始抽取'按钮
- 在右侧查看结果
公平抽取技巧
- 每个候选人占一行,移除意外的空白或重复项,除非有意允许重复。
- 对于公开抽取,在开始前决定种子、重复规则和重抽规则,以便结果更容易解释。
使用场景
技术原理
“加密级”的区分很重要。V8 中的 Math.random() 使用 xorshift128+(一种快速、低熵的算法),返回 [0, 1) 范围内的 64 位浮点数;恶意行为者观察到少量输出原则上可以重建 128 位状态并预测所有未来的值。SpiderMonkey 和 JavaScriptCore 也是如此。这使得 Math.random() 不适用于任何结果必须不可猜测的场景——公平抽奖、令牌、一次性密码、纸牌游戏发牌、审计抽样。crypto.getRandomValues(typedArray) 是这些场景的唯一选择。
将均匀的 32 位整数映射到 [0, N) 范围内的均匀索引且不产生模偏差,是区分正确与错误实现的第二个工程细节。简单地 idx = randomUint32 % N 是错误的:如果 N 不能整除 2³²,前 2³² mod N 个值的概率略高。正确的算法是拒绝采样:计算 limit = 2³² - (2³² mod N);抽取 32 位值 r;如果 r >= limit,重新抽取;否则返回 r % N。偏差从 O(N / 2³²) 降至零。当 N = 2 或 N 是 2 的幂时模运算是精确的,但拒绝采样是安全的默认方案,平均最多一次重抽(且总是终止)。
抽奖结果使用版本化键存储在 localStorage 中,因此会话可以承受页面重载,用户可以查看抽奖历史。历史记录未加密或签名——它是本地的、非敏感的且易于清除。无重复模式下,算法使用 Fisher-Yates 部分洗牌进行无放回抽取:在 N 个中的第 k 步,将元素 k 与 [k, N) 范围内的均匀随机元素交换,并锁定元素 k 作为已抽取。这是 O(N) 时间、O(1) 额外空间,且每个排列等概率,这是正确的组合保证。(简单的“抽取 N 次并拒绝重复”也是正确的,但 O(N²) 且在长会话中可能卡住。)
对于加权抽取(每个选项有自己的概率),标准算法是带前缀和数组的逆 CDF 采样:构建权重的前缀和,抽取 [0, total) 范围内的均匀实数,二分查找使 prefix[i] >= u 的最小 i。每次抽取 O(log N),前缀和在每次变更时构建一次。别名方法(Walker-Vose)以 O(N) 建立时间为代价实现 O(1) 每次抽取,仅在 N 较大且分布固定时才有价值。
- 随机源:crypto.getRandomValues(typedArray)——浏览器中唯一的加密安全 RNG,由操作系统级 CSPRNG 支持(Windows 10+ 上的 AES-256 CTR-DRBG、Linux/macOS/iOS/Android 上的 ChaCha20)
- Math.random() 在 V8 上使用 xorshift128+(其他引擎为变体):快速但可预测。永远不要用于公平抽奖、令牌、OTP 或审计抽样——仅用于视觉噪声、动画或非安全采样
- 无偏索引映射:使用拒绝采样而非取模。计算 limit = 2³² - (2³² mod N);如果随机 uint32 >= limit,重新抽取;否则返回 r % N。避免经典“模偏差”——短区间索引出现更频繁
- 无重复抽取使用 Fisher-Yates 部分洗牌,O(N) 时间、O(1) 空间:第 k 步将元素 k 与 [k, N) 范围内的均匀随机元素交换,然后锁定 k。每个排列等概率
- 加权抽取使用逆 CDF 采样:构建权重前缀和一次,抽取 [0, total) 范围内的均匀实数,二分查找使 prefix[i] >= u 的最小值。每次抽取 O(log N)
- 抽取历史通过版本化键持久化到 localStorage,会话可在页面重载后恢复。历史仅在本地且非安全敏感——完成后从界面清除
- Fisher-Yates 与“拒绝法抽取 N 次”对比:两者分布正确,但 Fisher-Yates 总计 O(N)、O(1) 内存,而简单拒绝法最坏情况 O(N²)(长列表末尾大量重复)
- 输出范围和类型:getRandomValues 接受 Int8Array、Uint8Array、Int16Array、Uint16Array、Int32Array、Uint32Array、BigInt64Array、BigUint64Array——页面始终使用 Uint32Array 供索引映射步骤使用
示例
从候选名单中不重复抽取
输入名单(每行一个):
Alice
Bob
Charlie
David
Eve
Frank
允许重复:关闭 抽取数量:3
结果:
#1: Charlie
#2: Alice
#3: Frank
(Bob、David、Eve 仍留在候选池)
页面会调用 crypto.getRandomValues() 在 Uint32Array 上生成 [0, n)
范围内均匀分布的整数,n 为剩余候选人数量。每个名字在每次抽取时
都有相同的 1/n 概率,且选中的下标在动画开始前就已经确定。允许重复并附带时间戳记录
输入名单(每行一个):
Head
Tails
允许重复:开启 抽取数量:5
结果:
#1 (14:23:01): Head
#2 (14:23:01): Tails
#3 (14:23:01): Head
#4 (14:23:01): Head
#5 (14:23:01): Tails
允许重复模式下,同一项可以连续被抽中,候选池也不会减少。每条记录
都会带上抽取时间,方便用同一份候选名单做多轮抽取,同时保留每次的
时间留痕。抽取记录与候选名单需分别清除——它们存放在 localStorage 的
不同键下。抽取核心逻辑的 JavaScript 片段
// 使用 Web Crypto(CSPRNG)从 [0, n) 中抽取一个下标
function pickIndex(n) {
// crypto.getRandomValues 给出均匀分布的 32 位整数;这里直接对 n 取模,
// 因为名单抽取场景下 n 最多几千,2^32 / n 很大,模偏差可以忽略。
const buf = new Uint32Array(1);
crypto.getRandomValues(buf);
return buf[0] % n;
}
// pickIndex(6) -> 例如 4 (上例中的 Charlie)
// pickIndex(2) -> 0 或 1 (Head 或 Tails)
//
// 注意:当 n 接近 2^32 这样的超大候选池时,应使用拒绝采样消除模偏差,例如:
// const limit = Math.floor(0xFFFFFFFF / n) * n;
// let r; do { crypto.getRandomValues(buf); r = buf[0]; } while (r >= limit);1,000 次抽取的公平性验证
名单: ['A', 'B', 'C', 'D'] (4 项,期望占比 25.0%)
抽取次数: 1,000 允许重复:开启
实测结果:
A: 247 (24.7%)
B: 256 (25.6%)
C: 248 (24.8%)
D: 249 (24.9%)
用 crypto.getRandomValues 跑 1,000 次抽取,每项与期望占比的偏差通常
应控制在约 +/- 3 个百分点;偏差更大(例如某一项达到 35%)多半说明
实现里用了 Math.random() 或者模运算用错了。如果用于受监管的抽奖,
应保存原始名单、随机源信息(浏览器/操作系统熵)、抽取时间戳以及
监督方签名——localStorage 不是一个可信的存证系统。常见问题
抽取结果真的是随机的吗?
是的。选择过程使用 Web Crypto API 的 crypto.getRandomValues,具备密码学级别的随机强度。除非你设置了权重,否则每个选项被抽中的概率完全相等。
同一个选项会连续被抽中两次吗?
会——这正是独立随机抽取的特性。如果有 5 个选项,连续两次抽中同一个的概率是 1/5 = 20%。如果你不希望出现重复,可以开启「抽中后移除」,这样会进行无放回抽取,直到列表清空。
「抽取 N 个」是怎么处理的?
默认采用无放回抽样——在一次 N 个的抽取中,每个项目最多被选中一次。如果允许重复(比如给一个人挑 N 项任务),可以打开「有放回」开关。无放回模式下,N 不能超过选项总数。
可以为选项添加权重以调整概率吗?
部分版本支持为每个选项设置权重(例如 A 权重 3、B 权重 1,则 A 被选中的概率是 B 的 3 倍)。页面会将权重归一化为概率。不设权重时,所有选项概率相等。
为什么这种方式比「凭感觉挑」更公平?
人类其实不擅长真正的随机——我们倾向于避开刚选过的项、过度关注醒目的选项,并不自觉地偏向熟悉的名字。由计算机来抽能消除这些偏差,对参与者来说类似抽奖的决策也会显得更加公正。
我的列表会被保存吗?
部分版本会写入 localStorage,因此在同一浏览器中列表会保留。关闭标签页或换浏览器都会丢失,除非你导出过。任何数据都不会上传。
可以用它进行合法抽奖吗?
不可以。法律意义上的抽奖、彩票和博彩开奖需要可审计的随机程序,往往依赖物理设备或经过认证的随机数生成硬件。普通的网页工具没有相应的文档和审计——只适合公司内部抽奖、朋友间抽签等非正式场合。