コンピュータはどうやって乱数を作る?疑似乱数・真の乱数・暗号学的乱数の完全解説

「ランダム」ボタンを押すと、プログラムは瞬時に数値を返します。シンプルに見えるこの処理の裏には、コンピュータ科学の根本的なパラドックスが潜んでいます:コンピュータは決定論的な機械——同じ入力は必ず同じ出力を生み出します。では、どうして「本当にランダム」になれるのでしょうか?ラッキーホイールからパスワードジェネレーターまで、「乱数」への要求はシーンによってまったく異なります。そしてその差が、場合によっては、あなたのアカウントのセキュリティを左右します。

1. コンピュータはなぜ「本当にランダム」になれないのか?

従来のコンピュータは決定論的チューリング機械の実装です:すべての操作は明確なルールに従い、同じ初期状態は必ず同じ後続状態をもたらします。つまり、純粋にコンピュータ内部で動くアルゴリズムの出力は、初期値(「シード」と呼ばれる)によって完全に決定されます。

シードがわかれば、その後に続くすべての「乱数」を予測できます。このようなアルゴリズムが生成する数列は、統計的にはランダムに見えますが、本質的には予測可能な、周期的な疑似乱数列です。これが計算機科学の分野でこれらを「疑似乱数生成器」(PRNG: Pseudo-Random Number Generator)と呼ぶ理由です。

「疑似」だからといって役に立たないわけではない
疑似乱数は悪いものではありません——シミュレーション、ゲーム、統計サンプリングなど大多数のユースケースでは PRNG で十分です。問題は「予測不可能性」が必要なシーン(暗号学)においてのみ発生します——その場合、疑似乱数は深刻なセキュリティ上の欠陥となります。

2. 疑似乱数生成器(PRNG)の仕組み

2.1 線形合同法(LCG)

最も古くシンプルな PRNG のひとつです。公式は次のとおりです:

X(n+1) = (a × X(n) + c) mod m

a(乗数)、c(増分)、m(係数)は事前に設定された定数で、X(0) がシードです。呼び出されるたびに前の値から次の値を計算します。

LCG は非常に高速でメモリ消費も少ないですが、品質には限界があります:周期が有限で、多次元空間での分布に規則的な超平面構造が現れ、統計テストで検出されやすいです。初期の ANSI C の rand() 関数は LCG でしたが、現在は単体での使用はほとんどありません。

2.2 メルセンヌ・ツイスタ(Mersenne Twister)

1998 年に松本眞と西村拓士によって提案された MT19937 は、現在最も広く使われている PRNG のひとつです。周期は 2^19937 − 1(天文学的な大きさ)に達し、ほぼすべての統計的ランダム性テストに合格しています。Python の random モジュール、Ruby、PHP の rand() など多くの言語のデフォルト乱数関数はメルセンヌ・ツイスタを使用しています。

しかし、メルセンヌ・ツイスタには深刻な弱点があります:内部状態は 624 個の 32 ビット整数で構成されています。攻撃者が連続する 624 個の出力値を観察できれば、内部状態を完全に再構築し、その後のすべての出力を予測できます。これにより、セキュリティが必要なアプリケーションにはまったく適していません。

2.3 現代的な高品質 PRNG

近年、xorshift128+、PCG(Permuted Congruential Generator)、xoshiro256** など、より現代的な PRNG が登場しています。速度・統計品質・周期のすべてで優れた性能を発揮しますが、暗号学への応用には依然として不適切です。

アルゴリズム周期速度統計品質暗号学的に安全?
LCG(線形合同法)中程度非常に速い普通いいえ
メルセンヌ・ツイスタ2^19937−1速い優秀いいえ
PCG / xoshiro2^128 以上非常に速い優秀いいえ
ChaCha20(CSPRNG)該当なし速い優秀はい
ハードウェア乱数(TRNG)該当なし遅い真のランダムはい

3. 真の乱数:物理世界から予測不可能性を得る

決定論的コンピュータの限界を突破するには、外の世界からのエントロピー(Entropy)——真に予測不可能な物理的事象——を取り込む必要があります。OS やハードウェアはさまざまな方法でこのエントロピーを収集します:

  • ユーザー行動:マウスの動き、キーボードのタイミング——人間の動作は非常に予測しにくい
  • ハードウェア割り込みのタイミング:ディスクの読み書き、ネットワークパケットが到着する正確な時刻——電磁ノイズなどの物理的要因の影響を受ける
  • CPU のジッター:温度や電圧変動など物理的要因による、プロセッサのわずかな時間差
  • 専用ハードウェア乱数生成器(HRNG/TRNG):熱雑音、放射性崩壊、光子の振る舞いなどの量子効果を利用して真のランダムビットを生成するハードウェア

Linux の /dev/urandom/dev/random は OS が管理するエントロピープールのインターフェースであり、上記のハードウェアイベントを収集して暗号学的アプリケーションに高品質な乱数シードを提供します。Intel の RDRAND 命令は CPU レベルでハードウェア乱数を直接提供します。

/dev/random/dev/urandom:何が違う?
/dev/random はエントロピープールが不足しているときに「ブロック」してさらなるエントロピーの蓄積を待ちます。/dev/urandom はエントロピーが不足していても CSPRNG で出力を延伸し続け、ブロックしません。現代のシステムでは、初期化後の /dev/urandom はほぼすべてのアプリケーションに対して十分な安全性を持ちます。

4. 暗号学的安全疑似乱数生成器(CSPRNG)

CSPRNG(Cryptographically Secure Pseudo-Random Number Generator)は PRNG の速度とエントロピー源の予測不可能性を組み合わせたものです。2 つの核心的な要件を満たす必要があります:

  • 次のビット予測不可能性:これまでに出力されたビットすべてがわかっていても、次のビットを 50% を超える確率で予測することはできない。
  • 状態漏洩後の後方安全性:攻撃者がある時点で CSPRNG の内部状態を入手したとしても、過去に生成された出力を遡って推算することはできない。

代表的な CSPRNG:

  • ChaCha20 ベースの CSPRNG:Linux kernel 4.8 以降の /dev/urandom の実装基盤、OpenBSD の arc4random でも採用
  • Fortuna:Windows の BCryptGenRandom のアルゴリズム基盤のひとつ
  • Hash_DRBG / HMAC_DRBG:NIST 標準化の CSPRNG で、さまざまなセキュリティライブラリで広く使用

5. なぜ Math.random() を絶対パスワードに使ってはいけないのか?

これは最もよくある、そして最も危険な誤解のひとつです。ほぼすべての言語に組み込まれている「基本的な乱数関数」——JavaScript の Math.random()、Python の random.random()、PHP の rand()——は PRNG であって CSPRNG ではありません。

JavaScript の Math.random() を例にとると、研究者が示したのは:連続する 3 つの Math.random() の浮動小数点出力を観察するだけで内部状態を完全に再構築でき、その後のすべての「乱数」を予測できるということです。

正しいアプローチは、暗号学向けに設計された API を使用することです:

  • JavaScript/Web:window.crypto.getRandomValues()
  • Node.js:crypto.randomBytes()
  • Python:secrets モジュール(Python 3.6+)
  • PHP:random_bytes()random_int()
  • Java:java.security.SecureRandom
シードが固定 = 結果は完全に予測可能
多くの人が学習時に random.seed(42) と書いて結果を再現可能にします——これは教育やテストにおいては良い実践です。しかしこれはまさに PRNG の本質を示しています:シードが固定されれば、すべての出力は決定的です。シードが推測可能(例:現在のタイムスタンプ)であれば、攻撃者は有限のシード値を試すだけで、あなたのすべての「乱数」出力を再現できます。

6. 用途別の乱数要件

6.1 ゲームと娯楽:PRNG で十分

サイコロシミュレーション、ラッキーホイール、くじ引きツール、カードゲーム——これらの核心的な要件は均等な分布、高速さ、ユーザーが「公平」と感じることです。メルセンヌ・ツイスタや現代的な PRNG で完全に対応できます。

6.2 統計シミュレーション:PRNG + シード制御

モンテカルロシミュレーション、統計サンプリング、機器学習のデータ分割——高品質な統計分布が必要で、検証のために再現性も必要です。固定シードの PRNG が標準的なアプローチです。

6.3 暗号学とセキュリティ:必ず CSPRNG を使用

パスワード生成、暗号鍵、Token、Session ID、CSRF Token、ワンタイムパスワード(OTP)——これらのシーンのセキュリティは乱数の予測不可能性に完全に依存しています。どんな PRNG も受け入れられず、OS が提供する CSPRNG インターフェースを使用する必要があります。

用途推奨タイプAPI 例重要な要件
ゲーム/ホイール/サイコロPRNGMath.random()均等分布、速度
統計シミュレーションPRNG(固定シード)random.seed(n)再現性、統計品質
パスワード/鍵/TokenCSPRNGcrypto.getRandomValues()予測不可能性
科学実験TRNGRDRAND / random.org真のエントロピー源

7. まとめ

コンピュータが乱数を生成するメカニズムは、表面上の「ボタンを押すと数字が現れる」の裏に、豊富な技術的層が隠れています:

  • PRNG(疑似乱数):決定論的アルゴリズム、速くて統計品質が良いが本質的に予測可能——ゲーム・シミュレーション向き、暗号学には不適
  • TRNG(真の乱数):物理世界からエントロピーを収集し、真に予測不可能——速度は遅く、シード提供や特殊用途向け
  • CSPRNG(暗号学的安全乱数):PRNG の速度とエントロピー源の予測不可能性を組み合わせる——パスワード・鍵・Token に唯一正しい選択

次にパスワードジェネレーターを使ったり、「ランダム」機能を目にしたりするとき、ひとつ自問してみてください:このシーンに必要なのは「ランダムに見える」ことか、それとも「真に予測不可能である」ことか?その違いが、あなたのデータセキュリティの境界線かもしれません。