乱数生成器とは何ですか?
乱数生成器(RNG)は、ランダムな数値(乱数)または擬似乱数を生成するシステムです。次に出力される数値を実際に予測できるかどうかは、生成器の種類によって異なります。物理的な現象を利用するものもあれば、アルゴリズムによって計算するものもあり、一部のアルゴリズムは生成された値を観察した者であっても次に何が出るかを予測できないように設計されています。「ランダムに見えること」と「予測不可能であること」は本質的に異なる性質であり、本稿の大部分はこの違いについて解説しています。
ハードウェア乱数生成器
ハードウェア乱数生成器(HRNG)は、真性乱数生成器(TRNG)とも呼ばれ、物理的なプロセスから数値を取得します。最も古い形態は私たちの目の前で動作しています。コイン投げ、サイコロ振り、ルーレットの回転などがこれにあたります。力学はそれらの動作を完全に説明できますが、精密に作られたルーレット盤であっても、各回転が始まるごくわずかな初期状態の違いが全く異なる結果へと拡大するため、実際には予測することが不可能です。
これに対して現代のハードウェア生成器は、より微小な現象を測定します。電子回路内のショットノイズや熱雑音、大気ノイズ、量子力学的効果などがその例です。これらは測定可能な予測不可能性である「エントロピー」の良い供給源ですが、物理的な供給源は生まれつき完全無欠というわけではありません。出現確率に偏りが生じたり、時間とともに特性がドリフト(経時変化)したり、故障したりする可能性があります。そのため、その品質を評価・監視し、必要に応じて生成された値に後処理を施す必要があり、こうした手順は NIST SP 800-90B などの標準規格で定められています。ハードウェア乱数源は、何よりも確実性が重視される分野、特にTransport Layer Security (TLS) などの通信プロトコルを支える暗号鍵の予測不能な元データを供給する暗号技術において活用されています。
擬似乱数生成器
物理デバイスに代わる選択肢がアルゴリズムです。擬似乱数生成器(PRNG)は、一見するとランダムに見えるものの、シード(seed)と呼ばれる初期値によって完全に決定される数列を生成します。同じシードを同じアルゴリズムに与えれば、毎回まったく同一の数列が得られます。これは、結果が予測不能でなければならずシードや内部状態を推測・復元される恐れがある場面では弱点となりますが、結果の再現性が求められるシミュレーションやテストを全く同じ条件で再実行したい場面では大きな利点となります。また、PRNGは高速で低コスト、実装も容易であるため、ほとんどのソフトウェアで広く採用されています。代表的なアルゴリズムには、線形合同法(LCG)、xorshift生成器、そしてメルセンヌ・ツイスタ(Mersenne Twister)などがあります。
メルセンヌ・ツイスタ
1997年に松本眞と西村拓士によって発表された Mersenne Twister は、最も広く利用されている擬似乱数生成器の1つであり、多くのプログラミング言語で標準の乱数生成器として採用されています。その名称は、周期(数列が繰り返されるまでの長さ)に由来しており、標準的なバリエーションである MT19937 ではメルセンヌ素数である 219937 − 1 の周期を持ちます。統計的な乱数検定の大部分に合格し、各種シミュレーションによく適しています。ただし、秘密情報を保護する目的では設計されていません。連続する 624 個の 32 ビット出力結果を観察すれば、誰でもその内部状態を完全に復元してそれ以降のすべての値を予測できてしまうため、暗号鍵やパスワードなど、厳密な予測不可能性が求められる用途には決して使用してはなりません。
暗号論的擬似乱数生成器とエントロピー
多くのシステムでは、アルゴリズムの高速性と物理乱数源の予測不可能性という両方の性質が同時に求められます。その解決策となるのが暗号論的擬似乱数生成器(CSPRNG)です。これもPRNGの一種ですが、出力された値の一部を観察しても残りの数値を予測する実用的な方法が存在しないように設計されており、真のエントロピー源からシードが供給され、定期的に再シード(更新)されます。単に物理乱数源からシードを与えるだけで通常のPRNGが安全になるわけではなく、アルゴリズム自体が暗号学的に安全に設計されていなければなりません。熱雑音やハードウェアイベントの発生タイミングなどの物理乱数源から少量の真のランダム性が供給され、CSPRNGはそこからはるかに高速に長い値の列を生成します。この組み合わせこそがオペレーティングシステムが動作するプログラムに提供している機能であり、今日、実際に通常「乱数生成器」と呼ばれるものの実体です。暗号化キーやセッショントークン、パスワードの生成に利用されています。
ブラウザにおける乱数生成
JavaScriptでは、Webページが乱数値を取得するための組み込み手段が2つ提供されており、それぞれ異なるクラスに属しています。Math.random() は一般的なPRNGです。言語仕様上、具体的なアルゴリズムの選定は各ブラウザに委ねられており、セキュリティに関する保証は一切ありません。アニメーションの描画などには十分ですが、結果の公平性が問われる抽選や当選者の選定には不向きです。もう1つの手段が Web Crypto API です。その crypto.getRandomValues() メソッドは、OSのエントロピーによってシードが与えられたCSPRNGから、暗号学的に強固な乱数値を返します。
当サイトの オンライン乱数生成器 は、すべての抽選処理にWeb Crypto APIを採用しており、数値はサーバー上ではなくご利用のブラウザ内で生成されます。サイコロを振る 場合も、コインを投げる 場合も、あるいは パスワードを生成する 場合も、当サイトの他のすべての生成器は同一の安全な乱数源によって駆動しています。
ランダムなビットから指定範囲の数値へ
暗号論的に安全な生成器を用意するだけでは、公平な抽選の半分を達成したにすぎません。生成器が提供するのは生のビット列であり、プログラムはそれをユーザーが求める範囲の数値に変換しなければならず、まさにここに偏り(バイアス)が生じる余地があります。たとえば、乱数源が 0 から 9 までの値を均等な確率で出力し、0 から 5 までの数値を必要としている場合を考えてみましょう。6 で割った余り(剰余)を取るのは自然なアプローチに見えますが、この方法では 0、1、2、3 はそれぞれ2通りの方法で発生するのに対し、4 と 5 は1通りしか発生しません。その結果、0 から 3 までの各数値の出現確率は 20% になるのに対し、4 と 5 はそれぞれ 10% ずつにしかなりません。この剰余による偏り(modulo bias)を排除する1つの方法は、指定範囲に収まらない余分な値を破棄して再度引き直すことです。乱数生成器の資料 では、この仕組みについて詳しく解説しています。
さらに、多くの人が驚く点が2つあります。数値の重複はごく自然な現象であるということです。1 から 10 までの整数を独立して抽出し、すべての数値が等確率である場合、直前に出た数値が再び出る確率は他のどの数値ともまったく同じ 10 分の 1 の確率です。重複のない抽選は「異なる種類の抽選」であり、「よりランダムな抽選」というわけではありません。また、公正な生成器を採用するだけでは、選考手続き全体の公平性は担保されません。参加者リストの正確さや試行回数の管理も同様に重要です。これについては ランダムなコンテスト当選者の選び方 の記事で詳しく解説しています。