随机 | 带种子 | 可重现

> 随机数生成器 <

// 生成加密级随机的整数和小数 —— 或为其设置种子以实现可重现的运行

[CRYPTO]

加密级随机性

默认使用带拒绝采样的 crypto.getRandomValues(),使范围内的每个整数概率均等 —— 零模偏差。

[SEEDED]

可重现的种子模式

提供任意种子字符串即可切换到确定性的 mulberry32 PRNG。相同种子,相同数字 —— 非常适合测试和模拟。

[NO-DUP]

无重复模式

通过 Fisher-Yates 风格的采样生成唯一整数。非常适合抽奖、摸彩或无放回抽样。

[DECIMALS]

小数精度控制

切换到小数模式并选择 1–10 位小数。数字在 [最小值, 最大值) 范围内均匀分布,并四舍五入到所选精度。

// 关于随机数生成

工作原理:

当种子字段为空时,此工具会调用 crypto.getRandomValues(new Uint32Array(1)),这是一个由操作系统熵池支持的 CSPRNG(加密安全的 PRNG)。为了将 32 位无符号整数无模偏差地映射到任意 [min, max] 范围,生成器使用拒绝采样:任何超过适配于 2^32 的范围最大倍数的抽取都会被丢弃并重新抽取。当你提供种子时,字符串会用 cyrb53 哈希为 32 位整数并输入到 mulberry32,这是一个周期为 2^32 的快速 32 位 PRNG —— 确定性且可重现。小数模式将均匀的 [0,1) 样本缩放到 [min, max),然后用 toFixed(places) 格式化。

示例:

min=1, max=100, count=5, seed="hello" -> 39, 98, 64, 12, 71 (始终)

标准与参考:

  • >Web Crypto API (W3C) —— 面向 CSPRNG 的 crypto.getRandomValues() 契约
  • >RFC 4086 —— Randomness Requirements for Security
  • >mulberry32 —— Tommy Ettinger 编写的 32 位 PRNG,公有领域
  • >cyrb53 —— 用于生成种子的快速 53 位字符串哈希(bryc,公有领域)

常见使用场景:

  • >具有可证明唯一选取的抽奖和摸彩
  • >用于调查和审计的无放回随机抽样
  • >蒙特卡洛模拟和统计实验
  • >采用可重现的种子分配进行 A/B 测试分桶
  • >游戏机制:骰子替代、战利品表、关卡种子
  • >QA 测试数据和模糊测试输入生成
  • >在会议中挑选获胜者、评委或首位发言人
  • >为原型和演示生成合成数据

相关工具:

  • >UUID 生成器 —— 当你需要唯一 ID 而非数字时使用的随机 v4 标识符
  • >密码生成器 —— 可控制字符池的加密级随机字符串
  • >BIP39 生成器 —— 用于加密钱包的随机助记种子短语
  • >randompickerwheel.app —— 当你想为现场观众或孩子的教室提供可视化随机选取器时使用的转盘界面

// 输出示例

抽奖 —— 从 1-49 中抽取 6 个唯一数字

config: min=1, max=49, count=6, no-duplicates=on, sort=ascending

output:

7, 13, 22, 28, 35, 41

骰子替代 —— 投掷 d20 十次(允许重复)

config: min=1, max=20, count=10, no-duplicates=off, sort=none

output:

14, 3, 20, 11, 7, 14, 2, 19, 8, 11

小数采样 —— [0, 1) 中的 5 个浮点数,保留 4 位小数

config: type=decimal, min=0, max=1, count=5, places=4

output:

0.4172, 0.8635, 0.0291, 0.5508, 0.7723

种子可重现 —— 相同种子产生相同序列

config: min=1, max=100, count=5, seed="hello" (运行两次)

output:

运行 1: 39, 98, 64, 12, 71 / 运行 2: 39, 98, 64, 12, 71

分桶 —— 通过整数取模进行 100 次加权 A/B 选取

config: min=0, max=99, count=100, seed="experiment-42", sort=ascending

output:

0, 1, 3, 4, 7, 9, 12, ... 95, 97, 98, 99 (确定性)

// 自行实现

如果你更愿意将该算法直接放入自己的项目中,这里是本工具所基于的三个基本组件。它们共同涵盖了无偏的加密整数、确定性的带种子流,以及将任意字符串种子转换为 32 位 PRNG 状态。全部为公有领域或等同于 MIT。

[JavaScript]

[min, max] 范围内的加密安全整数(无模偏差)

function secureRandomInt(min, max) {
  const range = max - min + 1;
  const buf = new Uint32Array(1);
  const max32 = 0xFFFFFFFF;
  const limit = Math.floor((max32 + 1) / range) * range;
  let v;
  do {
    crypto.getRandomValues(buf);
    v = buf[0];
  } while (v >= limit);
  return min + (v % range);
}
[JavaScript]

mulberry32 —— 确定性的带种子 PRNG(32 位状态,约 2^32 周期)

function mulberry32(seed) {
  let a = seed >>> 0;
  return function () {
    a = (a + 0x6D2B79F5) | 0;
    let t = a;
    t = Math.imul(t ^ (t >>> 15), t | 1);
    t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
  };
}

// usage: const rng = mulberry32(0xC0FFEE);
//        rng(); // 0.6743..., reproducible across runs
[JavaScript]

cyrb53 —— 快速的字符串到 32 位哈希(用于为 mulberry32 提供种子)

function cyrb53(str, seed) {
  seed = seed || 0;
  let h1 = 0xdeadbeef ^ seed;
  let h2 = 0x41c6ce57 ^ seed;
  for (let i = 0; i < str.length; i++) {
    const ch = str.charCodeAt(i);
    h1 = Math.imul(h1 ^ ch, 2654435761);
    h2 = Math.imul(h2 ^ ch, 1597334677);
  }
  h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^
       Math.imul(h2 ^ (h2 >>> 13), 3266489909);
  h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^
       Math.imul(h1 ^ (h1 >>> 13), 3266489909);
  return (h1 >>> 0);
}

// const rng = mulberry32(cyrb53('my-seed-string'));

// 常见陷阱

> 模偏差

当范围不是 2 的幂时,简单的 Math.floor(Math.random() * range) 会不均匀地分配高位 —— 某些输出出现的频率会略高于其他输出。使用拒绝采样(上面的代码片段)丢弃超出范围的抽取,使分布保持均匀。

> Math.random() 并非加密安全

Math.random() 在 V8/Spidermonkey 中使用 xorshift128+,它快速但可预测:观察到少数输出的攻击者可以恢复内部状态并预测未来值。对于令牌、盐值、对公平性至关重要的抽奖,或任何对手在意的内容,请始终使用 crypto.getRandomValues()

> 带种子的 PRNG 是确定性的 —— 切勿用于安全用途

Mulberry32 及同类算法的设计目的是重复。这正是你在测试、模拟和分桶中想要的 —— 也正是你在密码重置令牌或会话 ID 中不想要的。如果你发现自己将种子传入任何面向用户或与认证相关的内容,请立即切换回 CSPRNG。

> 高数量下的重复拒绝会耗尽容量

在大小为 n 的范围中请求 k 个唯一整数,当 k 接近 n 时,会变成赠券收集问题:最后几次抽取的代价呈指数级增长。本工具上限为 10,000,并使用部分 Fisher-Yates 采样,因此容量是精确的,但使用 seen.has(x) 的简单 while 循环在 k > n 时会无声地挂起。请始终预先检查容量。

>> 常见问题

问:这个随机数生成器在加密上安全吗?

答:是的,默认情况下是安全的。当你将 SEED 字段留空时,工具会使用 Web Crypto API 的 CSPRNG crypto.getRandomValues(),它在 Linux/macOS 上从 /dev/urandom 读取,在 Windows 上从 BCryptGenRandom 读取。我们还应用了拒绝采样,因此从 32 位整数到你的自定义范围的映射是无偏的。其输出适用于与安全相关的任务,如生成令牌、选择盐值或举办公平的抽奖。请注意,提供种子会切换到确定性 PRNG(mulberry32),它在加密上并不安全 —— 仅当可重现性比保密性更重要时才使用它。

问:带种子模式和不带种子模式有什么区别?

答:不带种子模式(默认)使用浏览器的 CSPRNG,每次点击都会产生全新的、不可预测的数字 —— 同一次调用永远不会两次返回相同的序列。带种子模式用 cyrb53 将种子字符串哈希为 32 位整数,并将其输入到确定性 PRNG mulberry32。相同的种子加上相同的设置总是产生相同的序列,这对于可重现的实验、调试随机代码、共享测试夹具,或每个用户必须始终落入同一分桶的 A/B 测试分桶来说非常宝贵。

问:无重复选项如何工作?

答:对于整数,无重复使用受 Fisher-Yates 启发的部分洗牌从范围 [min..max] 中抽取,返回前 k 个元素而绝不重复。我们使用稀疏交换映射,因此内存使用量保持为 O(count) 而非 O(range),这意味着从 1 到 1,000,000 中抽取 10 个唯一数字与从 1 到 100 中抽取 10 个一样廉价。如果你请求的唯一数字多于范围所能提供的数量(例如,在 1–10 中请求 20 个唯一整数),工具会报告明确的容量错误,而不会永远循环。

问:我可以生成小数(浮点数)随机数吗?

答:可以。将 TYPE 单选按钮设置为小数,并选择 1 到 10 位之间的精度。生成器在 [0, 1) 中抽取均匀浮点数,将其缩放到 [min, max),然后用 toFixed(places) 四舍五入并解析回数字。请注意,在小数模式下 max 是排他的(这是浮点数均匀性定义方式的副作用)。对于大多数统计用途,4–6 位小数就足够了;十位小数接近典型小范围的 IEEE 754 双精度极限。

问:有最大数量限制吗?

答:数量上限为每次点击 10,000 个数字。这能让页面在手机上保持响应,并避免浏览器在处理极大文本框时的边缘情况行为。如果你需要数百万个值,请用不同的种子运行多个批次并拼接输出,或将算法复制到 Node 脚本中 —— 相同的 mulberry32 / cyrb53 组合在服务器端同样有效。出于用户体验的考虑,排序、复制和下载都在客户端进行,因此 10k 是一个合理的上限,在现代笔记本电脑上仍能在远低于一毫秒的时间内完成。

问:我的数据会被上传到任何地方吗?

答:不会。每一项操作都以纯 JavaScript 在你的浏览器中运行:数字生成、排序、统计、复制到剪贴板以及下载 .txt 文件。当你点击 GENERATE、COPY 或 DOWNLOAD 时,不会向我们的服务器发送任何请求 —— 你可以用浏览器的网络标签页进行验证。我们不记录种子、范围或输出。该页面是通过 HTTPS 从 CDN 提供的静态 HTML,因此我们永远无法观测到关于你输入的任何信息。这使得该工具对于机密的测试夹具、内部抽奖种子或任何你宁愿远离第三方服务器的内容都是安全的。

问:生成小数时我可以避免重复吗?

答:可以,无重复复选框也通过尽力而为的重试循环适用于小数。由于两个独立采样的浮点数在比特级别几乎从不冲突,除非你的范围相对于精度而言非常小(例如,min=0, max=1, places=2 只有 100 个不同的值),否则重复很罕见。如果循环在合理的尝试次数内无法找到足够的唯一值,工具会报告容量错误,以便你扩大范围或提高精度。对于严格保证唯一的小数,最好使用整数模式并自行除以相应的值。

问:我什么时候应该使用转盘而不是数字列表?

答:数字列表最适合批量任务:抽取 50 张彩票、生成测试数据或为模拟提供输入。当一个人在观众面前选取一个项目,并且你想要悬念和视觉效果时,转盘更合适 —— 教室点名、直播抽奖、孩子的家务轮值、决策会议等。如果这是你的使用场景,请参阅 randompickerwheel.app,它提供专为这一时刻打造的交互式转盘界面。两个工具都从相同的 CSPRNG 基本组件中抽取 —— 区别在于呈现方式,而非公平性。

// OTHER LANGUAGES