公開透明

轉盤真的是隨機的嗎?

是的。每次轉動在動畫開始之前,就已經用瀏覽器內建的密碼學亂數產生器決定好結果。伺服器不參與也不干預;除非你自己設定機率,否則每一格的機會完全相同。

實際執行的程式碼
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 次,並帶有正常的小幅波動。

次數
格數
機率

虛線:完全公平的轉盤在該格的理論期望次數

與期望值的最大偏差–
卡方值,自由度 7–低於 14.07 即在 5% 顯著水準下通過
結論–

均等模式每轉一次就呼叫左邊展示的 randomInt 函式,全部在你的瀏覽器裡完成。 卡方檢定會把每根長條和期望值的差距加總。就算是公平的亂數,大約每 20 次也會有 1 次超過 5% 的臨界值,所以偶爾一次「偏離」很正常;如果每次都不通過,才代表有問題。

  • 直播與 LINE 群組抽獎開獎前把這頁分享給觀眾,說明得獎者怎麼抽出來,減少「內定」的質疑。
  • 課堂抽籤點名學生知道老師也控制不了轉盤,被抽到時就不會覺得是在針對自己。
  • 朋友聚會做決定吃什麼、誰請客、誰洗碗,有人說轉盤有鬼的時候,把這頁丟給他看。
  • 檢查自訂機率看懂為什麼 30% 的格子長期大約每 10 次中 3 次,短期卻會上下波動。
使用方法

按下「轉動」之後發生了什麼事

  1. 你按下轉動

    頁面向瀏覽器的 crypto.getRandomValues 要求新的 32 位元亂數,種子來自作業系統的安全亂數來源。

  2. 停止位置當下決定

    這些亂數決定轉盤多轉幾圈、最後停在哪個角度。這一刻結果就已經固定:停下時指針下方的那一格就是得獎項目。

  3. 接著才播放動畫

    旋轉、減速和滴答聲都只是視覺效果。看得再久、手機再快,也改變不了已經決定好的停止角度。

  4. 資料不會離開你的裝置

    抽選在你的瀏覽器中完成,名單不會上傳到我們的伺服器來決定結果,所以不存在後台「開關」可以操控。

大家為什麼用它

確保每次抽選公平的六個設計

密碼學等級亂數crypto.getRandomValues 是瀏覽器產生安全金鑰用的亂數來源,不是可預測的 Math.random。
沒有取餘數偏差拒絕取樣會丟掉少數偏向小數字的原始值,讓 n 個結果各自剛好是 n 分之一。
Fisher–Yates 洗牌打亂名單、分組、排順序都用 Fisher–Yates 演算法,每一種排列出現的機會都一樣。
動畫前就已決定按下轉動的瞬間停止角度就固定了,速度設定只影響你看多久。
伺服器不參與結果在你的裝置上計算,沒有帳號、沒有隱藏佇列,也沒有遠端干預。
只有你設定才加權自訂百分比會畫成大小不同的格子,轉盤上看到的面積就是真實機率。

為什麼直接取餘數會有一點不公平,拒絕取樣又怎麼解決

亂數產生器給的是固定範圍內的整數。瀏覽器給的是 32 位元整數,一共 4,294,967,296 種可能。最直覺的做法是用 value % n,也就是除以 n 取餘數,從 n 個選項裡挑一個。問題是:除非 2 的 32 次方剛好能被 n 整除,否則總有幾個餘數會多出現一次。

用一個只會產生 0 到 7 的迷你產生器、從 3 個選項裡抽,最容易看懂。

方法選項 1選項 2選項 3
直接取餘數,數值 0 到 70、3、6:8 分之 31、4、7:8 分之 32、5:只有 8 分之 2
拒絕取樣,丟掉 6 和 7 重抽0、3:6 分之 21、4:6 分之 22、5:6 分之 2

本站的 randomInt 會先算出 limit,也就是 2 的 32 次方減去餘數,凡是大於或等於 limit 的值都丟掉重抽。在 32 位元亂數下,這個偏差本來就極小,一個班級人數的名單不到一億分之一,也幾乎不會真的需要重抽,但結果從「幾乎準確」變成「完全準確」。

轉盤怎麼把亂數變成得獎結果

轉盤並不是先偷偷選好一個編號再假裝旋轉,而是直接抽出一個實際的停止角度:額外轉 5 到 8 圈之間的隨機圈數,再加上一個隨機的最終角度,兩者都來自 crypto.getRandomValues。每一格占圓周的一部分,所以它停在指針下的機率,就剛好等於它占 360 度的比例,誤差大約是四十億分之一。

幫選項設定自訂百分比後,轉盤會依百分比重新畫格子,而且所有百分比加起來必須等於 100 才能轉。50% 的格子占半個圓,長期大約贏一半。不用轉盤、直接從名單抽的工具則使用 weightedIndex:在總權重上隨機取一個點,依序往後找,看它落在哪一項。

  • 轉動前轉盤停在哪個位置都不影響結果,因為不論起點在哪,最終角度都是均勻分布。
  • 轉速和時間長短只會拉長動畫,停止角度在第一個畫面出現前就定好了。
  • 移除得獎者後轉盤會重新繪製,剩下的選項重新分配整個圓周。

連續抽到同一個人很正常:真正的隨機長什麼樣

大家對抽籤轉盤最常見的抱怨是「怎麼又是他」。公平的隨機沒有記憶,所以重複不只是可能,而是預料之中。下一次和上一次結果相同的機率,就是 1 除以選項數。

轉盤上的選項數連續兩次同一個結果轉 20 次至少出現一次連中
250%幾乎必然
425%約 99.6%
812.5%約 92%
128.3%約 81%
303.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。