1. 公平な抽選のやり方
このツールは、名簿とシード(抽選の元になる言葉)が同じなら、誰がいつやっても同じ結果になります。これを使って抽選が公平だったと示すには、次の順番を守ります。
- 名簿を確定し、名簿の指紋(後述)を参加者に伝える
- シードを決める。名簿を確定した時点ではまだ誰にも分からず、主催者が左右できない公開の値にする(例:抽選日の翌営業日に出る、決まった指標の終値)
- その値が出たら、それをシードにして「自分で決める」で抽選する
2で使う値は、名簿の指紋と一緒に「どの値を、どの日のものを、どう書いて入れるか」まで先に伝えておきます。書き方(桁区切りを入れるかなど)を後から選べると、書き方の数だけ結果を試せてしまうためです。結果をコピーすると、シードと名簿の指紋が付きます。
順番を守らないと、公平さは示せません。理由は2つあります。
- シードを先に公開すると、名簿を変えて結果を選べる。主催者はシードを知ってから、名簿の順番や名前の書き方を変えて、望む結果が出るまで手元で試せます。名簿を先に確定して指紋を伝えておけば、後から名簿を変えていないことを示せます。
- 名簿の確定後でも、主催者がシードを選べるなら、何通りも試して選べる。主催者が自分でシードを考える場合、手元でいくつものシードを試し、都合のよい結果が出たものを「最初から決めていたシード」として出せます。名簿を確定した時点では誰にも分からない値を使うのは、このためです。
自動のシードでも、やり直していないことは示せません。自動のシードは押すたびに新しく作られるので、何度も押して都合のよい結果を選ぶこともできてしまいます。自動のシードで出した結果は、誰でも同じ結果を再現できることは示せますが、それが1回目の抽選だったかどうかは示せません。身内の順番決めなど、そこまで要らない場面で使ってください。
2. 3つの方法の違い
「当選者を選ぶ」「順番を決める」「グループに分ける」は、どれもまず名簿の全員を同じ手順で並べ替えます。違うのは、その並びをどう使うかだけです。例のボタンで入る10人の名簿とシード yoskly では、次の結果になります。
| 方法 | 結果 |
|---|---|
| 順番を決める | 1. 田中 2. 高橋 3. 小林 4. 中村 5. 渡辺 6. 加藤 7. 鈴木 8. 佐藤 9. 伊藤 10. 山本 |
| 当選者を選ぶ(3人) | 1. 田中 2. 高橋 3. 小林 |
| グループに分ける(3組) | 1組:田中・中村・鈴木・山本 2組:高橋・渡辺・佐藤 3組:小林・加藤・伊藤 |
当選者は、順番の上から3人と同じです。順番の1〜3番目は田中・高橋・小林で、当選者の3人もこの順です。同じ名簿とシードなら、方法を変えても結果どうしが食い違いません。
グループは、順番の上から1組・2組・3組・1組…と1人ずつ配っています。
| 順番 | 名前 | 組 |
|---|---|---|
| 1 | 田中 | 1組 |
| 2 | 高橋 | 2組 |
| 3 | 小林 | 3組 |
| 4 | 中村 | 1組 |
| 5 | 渡辺 | 2組 |
| 6 | 加藤 | 3組 |
| 7 | 鈴木 | 1組 |
| 8 | 佐藤 | 2組 |
| 9 | 伊藤 | 3組 |
| 10 | 山本 | 1組 |
1組には順番の1・4・7・10番目の田中・中村・鈴木・山本が入るので、1組だけが4人になります。
3. 結果を確かめる方法
コピーした結果には、シードと名簿の指紋が付いています。
【抽選】当選 3人 / 10人
1. 田中
2. 高橋
3. 小林
シード: yoskly
名簿の指紋: d730dd4e
確かめる人は、同じ名簿をこのツールに入れ、「自分で決める」で同じシードを入れて抽選します。同じ結果になれば、その名簿とシードから出た結果です。
名簿の指紋
名簿の指紋は、名簿が同じかどうかを確かめるための短い文字列です。名前の順番や書き方が1文字でも違うと、別の値になります。たとえば「佐藤・鈴木・高橋・田中・伊藤・渡辺・山本・中村・小林・加藤」の10人の名簿は d730dd4e です。指紋が違うのに結果も違う場合は、名簿が違っています。
各行の前後の空白と空行は取り除いてから計算するので、空白や空行の違いでは指紋は変わりません。名前の全角・半角は区別します。
シードから乱数を作る手順
シードは、全角の英数字を半角にし、前後の空白を取ってから使います。yoskly と yoskly は同じシードです。大文字と小文字は区別するので、Yoskly は別のシードになり、結果も変わります。
整えたシードは SHA-256(データから決まった長さの値を作る標準の計算方法)にかけ、その先頭を乱数の初期値にします。SHA-256 は標準の方法なので、ほかの道具でも途中の値を確かめられます。たとえば yoskly の SHA-256 は a47bcb3e… で始まります。
printf '%s' yoskly | sha256sum
乱数を作る方法には xoshiro128** を使っています。作者が公開している方法と同じ手順で計算しています。
4. ランダムに並べる正しい方法
名簿をランダムに並べ替えるには、Fisher–Yates(フィッシャー–イェーツ)という方法を使います。末尾から順に、まだ決まっていない範囲から1つを選んで入れ替えていきます。
for (let i = names.length - 1; i >= 1; i--) {
const j = randomInt(i + 1); // 0〜i の整数
[names[i], names[j]] = [names[j], names[i]];
}
この方法なら、どの並び方も同じ確率で出ます。4人の名簿を何度も並べ替えると、24通りの並び方がどれもほぼ同じ回数ずつ出ます。
よく見かける次の書き方は、偏ります。
names.sort(() => Math.random() - 0.5);
並べ替えの関数は、比べた結果に矛盾がないことを前提に作られています。比べるたびに答えが変わると、並び方によって出やすさに差が出ます。偏りの大きさはブラウザの並べ替えの作り方によって違います。
このツールでは、当選者・順番・グループのどれも、まず名簿全体をこの方法で並べ替えてから決めます。当選者は並びの先頭からの人数分なので、同じ名簿とシードなら、当選者は順番の先頭と一致します。
5. 余りを取るだけだと偏る
乱数から「0〜n−1 の整数」を作るとき、乱数を n で割った余りを使うだけでは偏ることがあります。
たとえば 0〜255 の乱数を 3 で割った余りを使うと、余りが 0 になるのは86通り、1 と 2 になるのは85通りずつです。256 が 3 で割り切れないため、0 だけが1通り多くなります。
このツールでは、割り切れない端の部分が出たら捨てて、乱数を引き直します。上の例なら、255 が出たら引き直し、0〜254 の余りだけを使います。0・1・2 はどれも85通りずつになります。
6. グループの人数
グループに分けるときは、並べ替えた名簿の1人目を1組、2人目を2組…と順に配っていきます。割り切れないときは、前の組から順に1人多くなります。どの組の人数も、差は1人以内です。
- 10人を3組に分ける → 4人・3人・3人
- 10人を4組に分ける → 3人・3人・2人・2人
人数の多い組を後ろにしたい場合は、組の名前を読み替えてください。
名前はどこにも送りません
名簿・シード・抽選はすべてこのページの中で処理します。名前をサーバーに送ったり、URL に含めたりはしません。