轉盤是隨機的嗎?
是。你點下去的那一瞬間,中獎的格子就從瀏覽器的加密隨機來源決定了。這一頁完整說明過程,並教你自己驗證。
這個線上轉盤是隨機的:每一轉都從瀏覽器內建的加密隨機來源 crypto.getRandomValues 取一個新的數字,而且只有這個數字決定哪一格中獎。決定發生在你點下去的那一瞬間,轉盤開始動之前,動畫、時間、之前的結果都改變不了它。這一頁接下來逐步展示每個環節,最後附一個模擬器,讓你親眼驗證轉盤公不公平。
按下「轉」之後發生了什麼
三件事依序發生,而順序就是重點。第一,瀏覽器交出一個隨機數字,程式用它選出一格。第二,程式算出那一格裡的一個停止角度,再加上好幾整圈。第三,轉盤從現在的位置動畫轉到那個角度,照固定的曲線減速,直到停止。
- 1你點下去的那一瞬間,就用瀏覽器的加密隨機數字決定了中獎的那一格。
- 2轉盤轉好幾圈,再照固定的減速曲線慢慢停向那一格。
- 3指針停在格子內部、離邊線有段距離,所以永遠不會壓線。
所以你看到的轉動其實是一個已經做好的決定的重播。如果你能在點下去之後的下一個影格暫停動畫,讀出存放選定格子的那個變數,它早就寫著即將中獎的名字。轉盤不是「邊減速邊決定」,轉動過程中也沒有任何一刻還牽涉到運氣。轉動中再點一次沒有任何作用;下一次抽選只在你開始下一轉時才發生。
隨機數字從哪裡來
瀏覽器提供兩種取得隨機數字的方法。Math.random 是日常用的那一種,快速、拿來做遊戲沒問題,但它是偽隨機序列,瀏覽器並不保證無法預測。crypto.getRandomValues 是另一種:它由作業系統的熵池供應,和產生加密金鑰與連線憑證用的是同一個來源,轉盤用的就是這一個。
程式要求一個 32 位元的無號整數,也就是 0 到 4,294,967,295 之間的整數,再除以 4,294,967,296。這樣會得到一個從 0 開始、不到 1 的小數,每一個可能的值機率都相同。如果某個非常老的瀏覽器完全沒有加密來源,程式會退回 Math.random 而不是拒絕轉動,但現在桌機和手機上的每一個瀏覽器都已經支援 crypto.getRandomValues 很多年了,所以實際上這個備援從來不會執行。
每一轉取一個小數,第二個小數只用來決定指針要停在中獎格子裡的哪個位置。兩個都不會重複使用、儲存或傳送到任何地方。這一頁下方的模擬器呼叫的是同一個函式,所以它的結果可以代表真正的轉盤。
隨機數字怎麼變成一格
把轉盤想成一個 360 度的圓,指針固定在最上方。八個相同的項目時每一格佔 45 度。隨機小數乘以 360 就得到一個角度;角度除以 45 再無條件捨去,就是落在哪一格。小數 0.564 變成 203 度,從指針開始順時針數就是第五格。
轉盤是一個 360° 的圓。8 個相同的項目時,每格佔 45°。隨機抽出的是角度,把角度除以每格的度數,就是落在哪一格。
203° ÷ 45° = 4.51 → 第 5 格
加權改變的是格子的大小,不是方法。寫成 披薩*3 的項目權重是 3,一般項目權重是 1,所以披薩加三個一般項目總權重是 6,披薩的格子佔圓的六分之三,也就是 180 度。程式依序走過每個項目把權重加總,直到累計超過「隨機數字乘以總權重」為止,停在哪一項哪一項就中獎。畫在畫面上的格子用的是一模一樣的算術,這就是為什麼你看到的機率就是實際的機率。權重 0 的項目完全沒有範圍,永遠不會中,但會留在名單裡看得到。
為什麼動畫改變不了結果
格子選定之後,程式會在那一格 15% 到 85% 之間挑一個停止角度,所以指針永遠不會停在邊界上,也永遠不需要判定平手。接著加上整圈數:「短」四圈、「標準」七圈、「長」十一圈。轉盤從目前的角度沿著一條緩出曲線移到目標,先快後慢穩定減速,最後剛好停在目標上。
這條曲線每次形狀都相同。長的轉動並沒有比短的「更隨機」;它是同一個決定,前面多加幾圈裝飾而已。指針經過每格邊緣的喀喀聲和最後的彩帶都是動畫產生的,不是抽選程式產生的,關掉它們對機率沒有任何影響。超過 200 項、頁面把畫出來的轉盤換成滾動清單時也一樣:目標仍然事先選好,滾動的節奏是算好要停在它上面的。
轉盤可以動手腳嗎?
只有看得到的方式。這個轉盤的機率由名單決定,別無其他,而名單就在你眼前的文字框裡。一個名字出現兩次,它就有兩格。帶著 *3,它就有三格的份量。兩種都會在轉盤上以更大或重複的區域呈現,也都以文字呈現。沒有任何設定能讓某一格在不變大的情況下更常中,也沒有伺服器參與、能偷換結果。
辦抽獎的話,可以這樣向觀眾證明:秀出名單、秀出每個名字只出現一次且沒有加權,然後全螢幕轉。如果你建立分享連結,整份名單就編碼在連結本身,打開的人看到的是同一個轉盤,可以確認沒有藏任何東西。抽籤轉盤就是為此設計的,有「抽中後自動移除」,抽過的名字不會再被抽到。
有兩件事常被誤認為偏差,值得點出來。短期的結果看起來會很不均勻:八個名字轉十次,幾乎一定有某個名字沒被抽到、某個名字被抽到兩次,而這就是隨機在小數量時的樣子。另外,人會記住符合某個故事的轉動,例如同一個學生連續兩天被抽到,卻忘記幾百次沒有這樣的轉動。下面的模擬器就是為了用數字取代這兩種印象。
自己測試:模擬轉盤
模擬器執行的是真正的抽選程式,同一個 pickWeighted 函式、同一個加密來源,你要幾次就跑幾次,只省掉動畫。每一條長條是八個相同格子的其中一格,虛線是完全平均時每格應有的次數。先試 100 次,再試 100,000 次,看長條隨著次數增加越來越貼近虛線。
要把圖變成一個數字,模擬器會做卡方檢定。它把每一格的次數減掉期望次數、平方、除以期望次數,再把八格的結果加總。總和小表示每一條都靠近虛線;總和大表示至少有一條離得很遠。1,000 次的話,每格的期望次數是 125,大約 105 到 145 之間的長條都是正常的。
p 值 把這個總和翻譯成一個問題:如果轉盤完全公平,純粹靠運氣產生至少這麼不均勻的分布,機率有多高?p 值 0.30 表示公平的轉盤大約 30% 的時候會這樣,完全不稀奇。結論那一行用的是慣例的 0.05 門檻。在它以上,結果 符合公平轉盤的表現。在它以下,這個分布是公平轉盤每二十次出現不到一次的,少見到值得再看一眼。
低於 0.05 並不表示轉盤有偏差,這是大家最常搞錯的地方。依定義,公平的轉盤每二十次就有一次落在那裡,所以如果你按二十次按鈕,大概就該預期有一次警告。有偏差的轉盤表現是不同的:它會一次又一次不過關,而且每次突出的都是同一格。八格的情況下,卡方總和要超過大約 14.1,p 值才會掉到 0.05 以下;一個悄悄偏袒某一格哪怕只有幾個百分點的轉盤,每一次 100,000 轉都會衝破這個門檻,p 值遠低於 0.001。
如果你想檢查真正的轉盤而不是模擬器,打開任何一個名單不長的轉盤,把「轉動時間」設成「短」,轉一百次。「結果」分頁保留這次的每一個結果;複製、貼到試算表、數一數。你會看到和上面長條一樣的行為:次數在平均附近晃動,每一次偏的方向都不同,從來不會固定偏向某一項。
首頁的線上轉盤是上面所有說明的通用版本,有加權、儲存的轉盤和分享連結。只需要一個公平的二選一,是非轉盤把「是」和「否」交錯排在轉盤上,兩個答案永遠機率相同。要抽名字、而且每一次抽選都必須公平也要讓人看見公平,抽籤轉盤會保留每個結果的紀錄,並隨著抽選移除抽中的人。
常見問題
轉盤真的是隨機的嗎?
是。每一轉都從 crypto.getRandomValues 取一個數字,那是瀏覽器產生加密金鑰時使用的同一個隨機來源,而且只有這個數字決定落在哪一格。轉動的動畫在決定之後才開始,只是把轉盤帶到選定的那一格。
轉盤可以動手腳嗎?
沒有任何你看不到的方式。要改變這個轉盤的機率只有兩種方法:把某個項目加兩次,或替它加權,例如「名字*3」,而這兩種都會在轉盤上變成更大的格子、在名單裡變成看得到的文字。沒有隱藏設定、沒有伺服器、沒有帳號可以偏袒某一項。
什麼時候點、轉多久,有差嗎?
沒有。隨機數字在點下去的那一刻取得,之後那一格就固定了。「轉動時間」只改變動畫抵達之前轉幾圈;同一次抽選,「短」、「標準」、「長」都會停在同一格。
加權怎麼改變機率?
一個項目的機率是它的權重除以名單的總權重。四個項目裡有一個權重 3,那一項就有 6 分之 3,也就是 50% 的機率,它的格子佔半個轉盤。隨機數字是拿去和這些格子的大小比對的,所以畫出來的圖和真實機率是同一件事。
模擬轉盤裡的 p 值是什麼意思?
它是一個完全公平的轉盤產生至少和你剛才看到的一樣不均勻的分布的機率。p 值在 0.05 以上表示這個結果對公平轉盤來說很平常。低於 0.05 是不尋常,但不是偏差的證據:公平的轉盤大約每 20 次也會碰巧有 1 次落在那裡,所以再跑一次。
要怎麼檢查真正的轉盤,而不是模擬器?
用一個項目不多的轉盤轉一百次以上,打開「結果」分頁,它保留這次的每一個結果。複製清單,數每一項出現幾次。各項的次數應該接近平均,而且每一次的偏差方向都不一樣,絕不會一直偏向同一項。
轉盤會停在兩格中間的線上嗎?
不會。選定格子之後,停止角度會放在那一格 15% 到 85% 之間的位置,所以指針永遠清楚地停在格子裡面。指針經過格子邊緣時的小抖動只是動畫。