High Quality Random RandInt
Web Cryptoの乱数を、指定した整数範囲へ偏りなく写す。
BigIntと棄却法で実装したブラウザツールです。
Overview
High Quality Random RandInt は、最小値と最大値を指定して整数を生成するWebアプリです。抽選、順番決め、テストデータ作成などに使えるよう、処理はブラウザ内で完結します。
実装で重視したのは、Math.random() の値へ単純な掛け算をするのではなく、Web Crypto APIの crypto.getRandomValues() を乱数源にし、任意の大きさの整数範囲へ偏りなく変換することです。
Features
- 最小値・最大値を含む整数範囲から生成
crypto.getRandomValues()を乱数源として使用BigIntにより大きな整数範囲を扱う- 棄却法で剰余による偏りを除く
- 入力と計算はブラウザ内で完結
単純な剰余では、結果が偏る
例えば0〜255の一様な1バイト値を、そのまま value % 10 で0〜9へ変換するとします。256は10で割り切れないため、余り0〜5は26通り、6〜9は25通りから発生します。元の乱数が一様でも、変換後は一様ではありません。
範囲が小さい場合は差が目立たなくても、抽選回数を増やすと偏りは積み重なります。この「modulo bias」を避けるため、使ってよい元の値の上限を先に決め、端数部分を捨てます。
BigIntと棄却法による生成手順
利用者が指定した最小値を min、最大値を max とすると、結果の個数は次の値です。
range = max - min + 1
range を表せるだけのバイト数 n を求め、Web Crypto APIで n バイトを生成します。その全パターン数を 2^(8n) としたとき、range で割り切れる最大の境界を計算します。
sourceSize = 2 ** (8 * n)
maxValid = sourceSize - (sourceSize % range)
do {
value = crypto.getRandomValues(n bytes) → BigInt
} while (value >= maxValid)
result = min + (value % range)
maxValid 以上の値は使わず、もう一度生成します。残ったパターン数は必ず range の倍数なので、どの結果にも同じ数の元パターンが対応します。
計算を Number ではなく BigInt で行うのは、大きな整数で浮動小数点の丸めが入らないようにするためです。入力の大小関係と範囲を検証し、必要なバイト数を範囲に合わせて増やします。
棄却はどのくらい発生するか
必要な範囲を表せる最小のバイト数を使うと、棄却されるのは末尾の端数だけです。1回で採用されない可能性はありますが、採用されるまで同じ手順を繰り返すことで分布を保ちます。
この方法は「生成後に結果を補正する」のではなく、初めから各結果へ同数の入力を割り当てる方法です。UIには結果だけを表示しますが、別ページにアルゴリズムの説明を用意し、ツール名の「高品質」が何を指すのか確認できるようにしています。
用途と限界
crypto.getRandomValues() は暗号学的に強い乱数値を得るためのブラウザAPIですが、このWebツール全体が暗号製品として監査済みという意味ではありません。
- 日常の抽選、順番決め、テスト値、ゲーム試作を想定
- 結果をサーバーへ送信・保存する処理は使用しない
- ブラウザや端末の乱数実装と実行環境に依存する
- 第三者が検証できる抽選履歴やコミット・リビール方式は備えない
- 暗号鍵、認証トークン、復旧コードの生成には用途専用の実装を使う
「乱数源がWeb Cryptoであること」「範囲変換に剰余の偏りを入れないこと」は実装上の性質です。一方、公正な抽選の運用、改ざん防止、監査可能性は別の問題として扱う必要があります。
App Info
- Platform
- Web
- Language
- JavaScript
- Developer
- okojo