
2025/12/06 4:32
A two-person method to simulate die rolls (2023)
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
概要:
記事では、2 人で行う「フェイクダイス」トリックを紹介しています。プレイヤーは言語的な選択のみで、公正な 6 面サイコロの振りを生成できます。各プレイヤーは角度または時計番号(0–11)を選びます。一方が角度差を最大化し、もう一方が最小化します。正規化された差 δ∈[0,1] をダイス結果にマッピングします:δ=0 または 6 → 6;δ=1→1;δ=2→2;δ=3→3;δ=4→4;δ=5→5。
この方法は両プレイヤーが無偏なランダム数を選択することに依存します。結果として得られる距離の分布は、0 と 6 がそれぞれ 1/12、距離 1–5 がそれぞれ 1/6 であり、多く回数振った場合に各面が 16.66 % の均等な確率になります。
Lua スクリプトではとルックアップテーブルを用い、100 万回のシミュレーションで均一分布が確認されます。math.random()
このトリックは「言語的テニス」という速攻ゲーム向けに設計されています。このゲームではプレイヤーもスタミナ(3 ポイント、3 ターン後に再生、戻り閾値を調整するために使用可能)を管理します。著者は、物理的なサイコロが利用できない場合に最適であるとし、カジュアルな D&D やその他のテーブルトップセッションでの使用を推奨しています。ただし d6 のみ対応であり、正直なランダム選択に依存する点は認めています。
記事はユーザー 42yeah による copyleft 2023 ライセンスの下で公開されています。
本文
二人でサイコロを振る方法
2023‑08‑05 22:16:30 +0800
西安へ友人のZambon21と一緒に旅行しました。ツアーガイドを待っている間、携帯電話のバッテリーが切れたため、二人だけでランダムな数値を生成する簡単な方法を思いつきました。このアイデアは、単位円上の極座標を使って公平なサイコロ振りを行うものです。以下では、その手順、離散化(D6への変換)、Luaでの実験例、そして同じメカニズムで遊べる「ボキャブルテニス」というゲームについて整理しています。
方法論
-
二人の参加者
- Aが角度 (\theta_1) を提案する。
- Bが角度 (\theta_2) を提案する。
-
角度差を計算
[ \delta = |\theta_1-\theta_2| ] 正規化: [ \delta = \frac{\delta}{\pi} ] -
もし (\delta > 1)(すなわち (\pi) を超える)なら
[ \delta \leftarrow \delta - 1 ] これでランダム数は常に ([0,1)) に収まります。 -
バイアスと公平性
両者の利害が対立すると、(\delta) を最大化/最小化しようとして半ばランダムな結果になります。
離散化 – D6への変換
人間は連続値を正確に生成できないため、離散化します:
- 単位円を90°回転させて「0」を上端に配置。
- y 軸で反転して角度が時計回りに増加するように(時計の針と同じ)。
- 円を12個の位置(時計の数字)にマッピング。
両プレイヤーは ([0,12)) の整数を同時に選びます。
(d) を時計上での角度差とすると:
| 角度距離 | サイコロ振り |
|---|---|
| 0 または 6 | 6 |
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
| 5 | 5 |
確率表
| 距離 | 着地点 | 確率 |
|---|---|---|
| 0 | 0 | (1/12) |
| 1 | 1, 11 | (1/6) |
| 2 | 2, 10 | (1/6) |
| 3 | 3, 9 | (1/6) |
| 4 | 4, 8 | (1/6) |
| 5 | 5, 7 | (1/6) |
| 6 | 6 | (1/12) |
回転対称性のおかげで、どちらのプレイヤーを基準にしても分布は変わりません。
Lua 実験
diecount = {} function propose() return math.floor(math.random() * 12) end function wrap(x) if x < 0 then return x + 12 end if x >= 12 then return x - 12 end return x end local lut = {0,1,2,3,4,5,6,5,4,3,2,1} function pdistance(a,b) return lut[wrap(b-a)+1] end function roll() local a, b = propose(), propose() local pt = pdistance(a,b) if pt == 0 then pt = 6 end diecount[pt] = (diecount[pt] or 0) + 1 end for i=1,1000000 do roll() end print("Roll | Count | %") for r=1,6 do local cnt = diecount[r] print(string.format("%2d | %6d | %.3f%%", r, cnt, cnt/1000000*100)) end
サンプル出力
| Roll | Count | % |
|---|---|---|
| 1 | 166390 | 16.639% |
| 2 | 167068 | 16.707% |
| 3 | 166697 | 16.670% |
| 4 | 166385 | 16.639% |
| 5 | 166622 | 16.662% |
| 6 | 166838 | 16.684% |
公平なサイコロならそれぞれの面が正確に (1/6 \approx 16.667%) を示します。実験結果はこれをよく近似しています。
ボキャブルテニス(任意ゲーム)
- 各プレイヤーは 3 スタミナポイント でスタート。
- プレイヤー1がサーブし、スタミナを使って必要な振り値を上げることができる。
- 両者は同時にランダム数を名乗り、その結果がサイコロ値 (a) になる。
- プレイヤー2はプレイヤー1が消費したスタミナ分を足して少なくとも (a +) スタミナを振る必要がある。
- スタミナは3ターンごとに回復。
- 必要に応じてペナルティや得点ルールなどを追加可能。
結論
この二人用のトリックで、サイコロも電子機器もなく公平な d6 を作ることができます。両者の目標が対立しているときに最も効果的です。バイアスになる可能性はあるものの、シンプルで携帯性が高く、次回の軽い D&D セッションや外出先でのランダム数生成にぜひご利用ください。
Copyleft 2023 42yeah.