隨機 | 帶種子 | 可重現

> 亂數產生器 <

// 產生加密級隨機的整數與小數 —— 或為其設定種子以實現可重現的執行

[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