隨機抽取工具
公平隨機的抽籤、抽取、選擇工具
什麼是隨機抽取工具?
隨機抽取工具會從候選清單中隨機選出一個結果,避免由某個人主觀決定。它適合課堂點名、會議發言順序、輕量抽獎、任務分配、午餐選擇、團隊暖身,以及任何需要用中立方式做選擇的情境。本頁支援一行一個候選項、滾動動畫、本機抽取歷史,也可以關閉重複抽取,讓已抽中的項目從剩餘池移除。隨機性可以在統計意義上減少偏向,但不會驗證身分、不會自動處理輸入中的同名項,也不能提供正式稽核證據。若用於正式抽獎、合規活動、資格核驗或獎品發放,應使用有記錄、有規則、有負責人的流程。
使用方法
操作步驟
- 在文字框中輸入選項,每行一個
- 設定要抽取的項目數量
- 選擇是否允許重複
- 點選「開始抽籤」按鈕
- 在右側查看結果
公平抽籤提示
- 每行放一位候選人,移除多餘的空白行或重複項,除非刻意允許重複。
- 公開抽籤前,請先決定種子、重複規則與重新抽籤規則,結果會更容易說明。
使用場景
技術原理
隨機抽取工具基於 Web Crypto API(W3C、WHATWG),具體使用的是 crypto.getRandomValues(typedArray),這是瀏覽器中唯一暴露的加密安全隨機數來源。底層瀏覽器會呼叫作業系統的 CSPRNG:Windows 上的 CryptGenRandom(Windows 10 起使用 AES-256 CTR-DRBG 模式)、Linux 上的 getrandom(2)(讀取與內核 /dev/urandom 相同的 ChaCha20 池)、macOS 上的 SecRandomCopyBytes,以及 iOS、Android 和 BSD 上的對應實作。其輸出適用於金鑰生成、隨機數、鹽值、初始化向量,以及任何可預測性會造成安全問題的場景。 「加密等級」的區別很重要。V8 中的 Math.random() 使用 xorshift128+(一種快速、低熵的演算法),回傳 [0, 1) 範圍內的 64 位元浮點數;惡意觀察者在理論上只需觀測少量輸出就能重建 128 位元狀態並預測所有未來值。SpiderMonkey 和 JavaScriptCore 的情況也類似。這使得 Math.random() 不適用於任何結果必須不可猜測的場景——公平的中獎者選擇、權杖、一次性密碼、紙牌遊戲中的交易、稽核抽樣。crypto.getRandomValues(typedArray) 是這些場景的唯一選擇。 將均勻分佈的 32 位元整數映射到 [0, N) 範圍內的均勻索引且無模偏差,是區分正確與錯誤實作的第二個工程細節。直接用 idx = randomUint32 % N 是錯誤的:若 N 不能整除 2 的 32 次方,前 2 的 32 次方 mod N 個值的出現機率會略高。正確的演算法是拒絕取樣:計算 limit = 2 的 32 次方 - (2 的 32 次方 mod N);抽取 32 位元值 r;若 r >= limit 則重新抽取;否則回傳 r % N。偏差從 O(N / 2 的 32 次方) 降至零。對於 N = 2 或 N 為 2 的冪次方,取模是精確的,但拒絕取樣是安全的預設方案,平均最多只需一次重抽(且保證終止)。 抽取工具將結果以帶版本號的鍵存入 localStorage,因此工作階段可在頁面重新載入後存活,使用者也能回顧抽取歷史。歷史記錄未加密或簽署——它是本機的、非敏感的,且容易清除。在不重複模式下,演算法使用 Fisher-Yates 部分洗牌進行無放回抽取:在 N 步中的第 k 步,將第 k 個元素與 [k, N) 範圍內均勻選取的元素交換,並將第 k 個元素鎖定為已抽取。這是 O(N) 時間、O(1) 額外空間,且每種排列的概率相等,這是正確的組合保證。(「抽 N 次並拒絕重複」的方式也是正確的,但最壞情況下為 O(N²),在長清單上可能卡住。) 對於加權抽取(每個選項有各自的機率),標準演算法是前綴和陣列的反 CDF 取樣:建立權重的前綴和,抽取 [0, 總和) 範圍內的均勻實數,然後二分搜尋最小的 i 使得 prefix[i] >= u。每次抽取為 O(log N),前綴和在每次變更時建立一次。Alias 方法(Walker-Vose)可在 O(1) 時間內完成每次抽取,代價是 O(N) 的初始化,僅在 N 很大且分佈固定時才有價值。
- 隨機數來源:crypto.getRandomValues(typedArray) — 瀏覽器中唯一的加密安全 RNG,由作業系統層級的 CSPRNG 支援(Windows 10+ 使用 AES-256 CTR-DRBG、Linux/macOS/iOS/Android 使用 ChaCha20)。
- Math.random() 在 V8 上是 xorshift128+(其他引擎有類似變體):速度快但可預測。永遠不要用於公平抽選、權杖、一次性密碼或稽核抽樣——僅適用於視覺噪點、動畫或非安全取樣。
- 無偏差索引映射:拒絕取樣,而非取模。計算 limit = 2³² - (2³² mod N);若隨機的 uint32 >= limit 則重新抽取;否則回傳 r % N。避免了經典的「模偏差」問題,即較小範圍的索引出現頻率更高。
- 不重複抽取使用 Fisher-Yates 部分洗牌,O(N) 時間、O(1) 空間:在第 k 步將第 k 個元素與 [k, N) 中的均勻元素交換,然後鎖定 k。每種排列概率相等。
- 加權抽取使用反 CDF 取樣:一次性建立權重的前綴和,抽取 [0, 總和) 範圍內的均勻實數,二分搜尋最小的 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 仍留在候選池中)
頁面對 Uint32Array 呼叫 crypto.getRandomValues(),為每次抽選
產生 [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,所以同一個瀏覽器中清單會被保留。關閉分頁或切換瀏覽器後就會遺失,除非你先匯出。所有資料都不會上傳。
可以拿來做合法的抽獎嗎?
不建議。合法的抽獎、樂透與博弈開獎都需要可稽核的隨機程序,常常需要實體裝置或經過認證的 RNG 硬體。一般網頁工具沒有相關文件與審計,建議只用於辦公室抽獎與非正式抽選。