轉盤真的是隨機的嗎?
是的。每次轉動在動畫開始之前,就已經用瀏覽器內建的密碼學亂數產生器決定好結果。伺服器不參與也不干預;除非你自己設定機率,否則每一格的機會完全相同。
const MAX = 2 ** 32
function randomInt(n) {
// 捨棄無法整除的尾段,讓 n 個結果的機會完全相等
const limit = MAX - (MAX % n)
let value
do {
value = crypto.getRandomValues(new Uint32Array(1))[0]
} while (value >= limit)
// 這時取餘數就不再有偏差
return value % n
}這是本站共用亂數模組裡的 randomInt 函式。你可以在瀏覽器開發人員工具的「來源」面板中查看完整程式。
自己試試看:模擬轉 10,000 次
8 個一樣大的格子。公平的轉盤每格大約會中 1,250 次,並帶有正常的小幅波動。
虛線:完全公平的轉盤在該格的理論期望次數
均等模式每轉一次就呼叫左邊展示的 randomInt 函式,全部在你的瀏覽器裡完成。 卡方檢定會把每根長條和期望值的差距加總。就算是公平的亂數,大約每 20 次也會有 1 次超過 5% 的臨界值,所以偶爾一次「偏離」很正常;如果每次都不通過,才代表有問題。
- 直播與 LINE 群組抽獎開獎前把這頁分享給觀眾,說明得獎者怎麼抽出來,減少「內定」的質疑。
- 課堂抽籤點名學生知道老師也控制不了轉盤,被抽到時就不會覺得是在針對自己。
- 朋友聚會做決定吃什麼、誰請客、誰洗碗,有人說轉盤有鬼的時候,把這頁丟給他看。
- 檢查自訂機率看懂為什麼 30% 的格子長期大約每 10 次中 3 次,短期卻會上下波動。
按下「轉動」之後發生了什麼事
你按下轉動
頁面向瀏覽器的 crypto.getRandomValues 要求新的 32 位元亂數,種子來自作業系統的安全亂數來源。
停止位置當下決定
這些亂數決定轉盤多轉幾圈、最後停在哪個角度。這一刻結果就已經固定:停下時指針下方的那一格就是得獎項目。
接著才播放動畫
旋轉、減速和滴答聲都只是視覺效果。看得再久、手機再快,也改變不了已經決定好的停止角度。
資料不會離開你的裝置
抽選在你的瀏覽器中完成,名單不會上傳到我們的伺服器來決定結果,所以不存在後台「開關」可以操控。
確保每次抽選公平的六個設計
為什麼直接取餘數會有一點不公平,拒絕取樣又怎麼解決
亂數產生器給的是固定範圍內的整數。瀏覽器給的是 32 位元整數,一共 4,294,967,296 種可能。最直覺的做法是用 value % n,也就是除以 n 取餘數,從 n 個選項裡挑一個。問題是:除非 2 的 32 次方剛好能被 n 整除,否則總有幾個餘數會多出現一次。
用一個只會產生 0 到 7 的迷你產生器、從 3 個選項裡抽,最容易看懂。
| 方法 | 選項 1 | 選項 2 | 選項 3 |
|---|---|---|---|
| 直接取餘數,數值 0 到 7 | 0、3、6:8 分之 3 | 1、4、7:8 分之 3 | 2、5:只有 8 分之 2 |
| 拒絕取樣,丟掉 6 和 7 重抽 | 0、3:6 分之 2 | 1、4:6 分之 2 | 2、5:6 分之 2 |
本站的 randomInt 會先算出 limit,也就是 2 的 32 次方減去餘數,凡是大於或等於 limit 的值都丟掉重抽。在 32 位元亂數下,這個偏差本來就極小,一個班級人數的名單不到一億分之一,也幾乎不會真的需要重抽,但結果從「幾乎準確」變成「完全準確」。
轉盤怎麼把亂數變成得獎結果
轉盤並不是先偷偷選好一個編號再假裝旋轉,而是直接抽出一個實際的停止角度:額外轉 5 到 8 圈之間的隨機圈數,再加上一個隨機的最終角度,兩者都來自 crypto.getRandomValues。每一格占圓周的一部分,所以它停在指針下的機率,就剛好等於它占 360 度的比例,誤差大約是四十億分之一。
幫選項設定自訂百分比後,轉盤會依百分比重新畫格子,而且所有百分比加起來必須等於 100 才能轉。50% 的格子占半個圓,長期大約贏一半。不用轉盤、直接從名單抽的工具則使用 weightedIndex:在總權重上隨機取一個點,依序往後找,看它落在哪一項。
- 轉動前轉盤停在哪個位置都不影響結果,因為不論起點在哪,最終角度都是均勻分布。
- 轉速和時間長短只會拉長動畫,停止角度在第一個畫面出現前就定好了。
- 移除得獎者後轉盤會重新繪製,剩下的選項重新分配整個圓周。
連續抽到同一個人很正常:真正的隨機長什麼樣
大家對抽籤轉盤最常見的抱怨是「怎麼又是他」。公平的隨機沒有記憶,所以重複不只是可能,而是預料之中。下一次和上一次結果相同的機率,就是 1 除以選項數。
| 轉盤上的選項數 | 連續兩次同一個結果 | 轉 20 次至少出現一次連中 |
|---|---|---|
| 2 | 50% | 幾乎必然 |
| 4 | 25% | 約 99.6% |
| 8 | 12.5% | 約 92% |
| 12 | 8.3% | 約 81% |
| 30 | 3.3% | 約 47.5% |
如果你要每個人都輪過一次才重複,那是「抽出不放回」,不是更公平。在抽籤點名轉盤上使用「移除並再轉一次」,或用清單隨機排序工具一次把整份名單打亂。
哪裡還用到 Math.random,為什麼不影響結果
Math.random 拿來做裝飾效果沒有問題,所以少數動畫會用到:擲骰子時骰子的微微傾斜、彈珠賽跑的晃動、數字定格前的跳動。這些情況下,結果本身都已經用密碼學亂數抽好了,裝飾的幅度也控制得夠小,不會改變你看到的順序。
共用亂數模組只有在瀏覽器完全不支援 Web Crypto 時才會退回 Math.random,這在現在只會發生在非常老舊的瀏覽器。目前版本的 Chrome、Safari、Firefox、Edge 以及 LINE 內建瀏覽器都支援 crypto.getRandomValues。
常見問題解答
抽獎轉盤真的是隨機的嗎?
本站的是。每次轉動都使用瀏覽器的密碼學安全亂數 crypto.getRandomValues,在動畫開始前決定轉盤停在哪裡。每一格得獎的機率和它的面積成正比,一樣大的格子機會完全相同。
網站或轉轉盤的人能不能作弊、內定結果?
我們的伺服器不會決定、也不會修改任何一次轉動,因為抽選是在你的瀏覽器裡完成。轉轉盤的人也控制不了:在什麼時機點、按多久,都不會影響隨機抽出的停止角度。你可以用瀏覽器的開發人員工具檢查程式碼。
為什麼同一個名字連續中了兩次?
因為隨機沒有記憶。8 個選項時,同一項連續中兩次的機率是 12.5%;連轉 20 次,至少出現一次連中的機率約 92%。如果不想重複,每次抽中後把得獎者移除就好。
轉久一點或轉快一點,會改變機率嗎?
不會。停止角度在你按下轉動的瞬間就決定了,時間長短、速度和減速曲線只改變動畫的樣子,不會改變停在哪裡。
轉盤的自訂機率怎麼運作?
幫某個選項填上百分比,它的格子就會畫成占圓周相對應的比例,所有百分比需要加起來等於 100。設為 30% 的選項長期大約每 10 次中 3 次,但短期內會高於或低於這個數字。
模擬器裡的卡方值和結論代表什麼?
卡方值是把每一格實際次數與期望次數的差平方、除以期望次數後加總。只要低於對應格數的 5% 臨界值,這次結果就和公平隨機相符。公平的產生器大約每 20 次也會有 1 次不通過,所以多跑幾次再下結論。
抽獎結果是用 Math.random 產生的嗎?
不是。Math.random 只用在骰子傾斜、彈珠晃動這類視覺效果,而且是在結果抽出之後。結果來自 crypto.getRandomValues,只有在不支援 Web Crypto 的老舊瀏覽器中才會退回 Math.random。