N2
NanToo
Mersenne Twister 1998 — サイコロ・くじ引き・パスワードを支える日本発の世界標準乱数アルゴリズム
DDEVELOPER
開発9 分で読める

Mersenne Twister 1998 — サイコロ・くじ引き・パスワードを支える日本発の世界標準乱数アルゴリズム

NanToo の サイコロコイントスくじ引きルーレットパスワード生成 など「ランダム」を扱うツールの裏側では、ブラウザの Math.random()crypto.getRandomValues() が呼ばれています。前者の中核となっているのが、1998 年に広島大学の松本眞・西村拓士が発表した Mersenne Twister (MT19937)。Python・PHP・Excel・MATLAB・C++・R など主要言語のデフォルト乱数を担う、日本発の世界標準アルゴリズムです。本記事では原論文 (ACM TOMACS, 1998) と松本研の公開資料を一次資料に、その仕組み・なぜ偉いか・暗号には不向きな理由までを整理します。

#乱数#Mersenne Twister#MT19937#アルゴリズム#暗号

「乱数」とは何か — 真の乱数 vs 疑似乱数

コンピュータが生成する「乱数」は、ほぼすべて疑似乱数 (PRNG, Pseudo-Random Number Generator)。決定的なアルゴリズムが内部状態を更新しながら数値を吐き続けます。同じ初期値 (seed) を入れれば同じ系列が再現される計算機にとって都合の良い "ランダム"です。

真の乱数は物理現象 (原子崩壊・サーマルノイズ・大気電気) などからしか得られず、ハードウェア乱数源として CPU の RDRAND 命令や Linux の /dev/random が利用しています。一方の疑似乱数は:

  • 長所: 高速、再現可能 (seed を保存すれば再生)、ポータブル
  • 短所: 内部状態を見抜かれると以降が予測可能 (後述)

シミュレーション・ゲーム・統計サンプリングなどでは速度と再現性が重要なので疑似乱数が主流。Mersenne Twister はこの分野の事実上の標準になっています。

Mersenne Twister — 1998 年の革命

1998 年、広島大学の 松本眞西村拓士 が ACM Transactions on Modeling and Computer Simulation に発表した論文 "Mersenne Twister: A 623-Dimensionally Equidistributed Uniform Pseudo-Random Number Generator" が転換点でした。

主要なバリアントは MT19937。名前の由来は内部状態が 2^19937 - 1 = メルセンヌ素数 で、これが乱数列の周期になることから。具体的な性能:

周期: 2^19937 - 1 ≈ 4.3 × 10^6001
状態空間: 624 個の 32-bit 整数 (= 19968 bit)
等分布次元: 623 次元 (各次元で一様に分布)

「周期 2^19937」が何を意味するか: 1 秒間に 10 億個の乱数を生成しても、宇宙の年齢の何兆倍経っても周期が一周しない。実用上は無限と同じです。

等分布次元 623 とは、1 ビットから 623 次元までのベクトル空間で乱数列が一様に分布することを意味します。これは多次元シミュレーション (モンテカルロ法など) で重要で、それまでの線形合同法 (LCG) などと比較して桁違いに優れていました。

アルゴリズムの仕組み (簡略版)

MT19937 のコア:

// 内部状態: 624 個の 32-bit 整数の配列 mt[0..623]
// 1) 初期化 (Knuth の手法)
mt[0] = seed
for i = 1..623:
  mt[i] = 1812433253 * (mt[i-1] xor (mt[i-1] >> 30)) + i

// 2) 624 個の出力ごとに状態を更新 (twist)
for i = 0..623:
  y = (mt[i] & 0x80000000) | (mt[(i+1) mod 624] & 0x7fffffff)
  mt[i] = mt[(i+397) mod 624] xor (y >> 1)
  if (y mod 2 != 0):
    mt[i] = mt[i] xor 0x9908B0DF

// 3) 出力 (tempering で各ビットの偏りを減らす)
y = mt[i]
y = y xor (y >> 11)
y = y xor ((y << 7) & 0x9D2C5680)
y = y xor ((y << 15) & 0xEFC60000)
y = y xor (y >> 18)
return y

1 回の出力ごとに状態を 1 ステップ進め、624 個出した時点で全体を「twist」(ねじり) 操作で大きく更新する設計。シフト・XOR・剰余・乗算しか使わないため 1 出力あたりナノ秒オーダーで高速。

"Mersenne" = メルセンヌ素数 (2^p - 1 の形の素数、ここでは p=19937)、"Twister" = 状態をねじって混ぜる操作 — この 2 つから命名されました。

どこで使われているか — 世界の主要実装

言語/環境デフォルト乱数関数
PythonMT19937random.random() (Python 3.0+)
PHPMT19937mt_rand() (PHP 7.1 以降は rand() も内部で MT)
ExcelMT19937=RAND() (Excel 2003 以降)
MATLABMT19937rand() のデフォルト
RMT19937runif() のデフォルト
RubyMT19937Random.new
C++ 11+MT19937std::mt19937 (標準ライブラリ <random>)
NumPyMT19937 (default_rng は PCG64)numpy.random.RandomState (legacy)

これだけメジャーな言語でデフォルトに採用された PRNG はほかに存在せず、事実上の世界標準として君臨しています。Mersenne Twister は無料で公開されており、論文も BSD ライセンス (実装) で提供されているため広範に普及しました。

Hiroshima 大学のサイト 松本研究室公式 で C / Fortran のリファレンス実装が今も配布されています。

JavaScript の Math.random() は何を使っているか

JavaScript の Math.random() はエンジン依存で:

  • V8 (Chrome / Node.js / Edge): 2015 年まで MT19937、それ以降は xorshift128+ ベース → 2018 年に xoroshiro128+、2024 年現在は xoshiro256** 派生
  • SpiderMonkey (Firefox): 同じく xorshift 系列
  • JavaScriptCore (Safari): 独自実装 (高速重視)

つまり Math.random() はもはや MT19937 ではないのですが、xorshift 系も MT を改善する形で誕生したアルゴリズムで、設計思想に MT のレガシーが受け継がれています。ブラウザ側ツール (本サイトのサイコロ等) で使う Math.random() は、現代では xorshift 系が主流です。

致命的な弱点 — 暗号には絶対使ってはいけない

MT19937 の最大の落とし穴: 暗号学的に安全ではない (Not Cryptographically Secure)

具体的には: 内部状態 624 個の整数 = 19968 ビットを観測できれば、以降のすべての出力を予測できます。実際には連続する 624 個の出力 (32-bit × 624) から内部状態を逆算する手法が公開されており、線形ソルバ等で容易に攻撃可能。

つまり、MT でパスワード・暗号鍵・セッション ID・ノンス・トークンを生成すると、出力を一定数集めるだけで以降が予測でき、深刻な脆弱性につながります。

過去の実例:

  • 2015 年: PHP 5.x の session_id()mt_rand ベースだった件 — 攻撃可能性が指摘され PHP 7 で修正
  • 古いオンラインカジノ・MMO のアイテムドロップ判定が MT で、攻撃者が乱数列を予測してチート可能だった事例多数

セキュリティ用途では暗号学的擬似乱数 (CSPRNG) を使うこと:

  • Web (ブラウザ): crypto.getRandomValues() (Web Crypto API)
  • Node.js: crypto.randomBytes()
  • Python: secrets モジュール (Python 3.6+)、os.urandom()
  • OS レベル: Linux /dev/urandom、Windows BCryptGenRandom

ブラウザの crypto.getRandomValues() はOS のエントロピープールから取得し、CPU の RDRAND・キーボード/マウス入力タイミング・ディスクアクセスタイミングなどを混合した CSPRNG 出力です。本サイトの パスワード生成ツールはこちらを使用しているので、生成パスワードの予測リスクはありません。

後継アルゴリズム — 2010 年代以降の進化

MT19937 は決して廃止されたわけではありませんが、2010 年代以降に「より小さな状態で同等の品質を出す」「より高速」を目指した PRNG 群が登場しています。

アルゴリズム発表年状態サイズ速度 (相対)
Mersenne Twister (MT19937)1998624 × 32-bit (2.5 KB)1.0× (基準)
WELL512a2006512-bit0.9×
xorshift128+2014128-bit2.5×
xoroshiro128+2018128-bit2.5×
xoshiro256**2018256-bit2.0×
PCG642014128-bit1.5×
SFMT (松本ら)20082.5 KB2-3× (SIMD)

SFMT (SIMD-oriented Fast Mersenne Twister) は同じく松本研の発表で、CPU の SIMD 命令を使い MT を 2-3 倍高速化したもの。2008 年論文発表後、シミュレーション分野で広く採用されています。

「では MT は時代遅れか?」というと NO。**統計品質 (等分布性、独立性) では今でも世界トップクラス**で、新しいアルゴリズムも MT19937 をベンチマークに比較されます。シミュレーション・科学計算・乱数列再現が必要な用途では現在も第一選択です。

本サイトのツール群と乱数

本サイトのランダム系ツールの実装を整理:

ツール使用乱数選定理由
サイコロ Math.random() 娯楽用途、暗号品質不要
コイントス Math.random() 同上
くじ引き Math.random() + Fisher-Yates シャッフル 同上、ただし結果の偏りがないよう Fisher-Yates 採用
ルーレット Math.random() 同上
パスワード生成 crypto.getRandomValues() (CSPRNG) セキュリティ用途のため暗号品質必須

サイコロやコイントスで暗号品質の乱数を使うのは過剰、パスワード生成で Math.random() を使うのは危険 — この使い分けが原則です。本サイトはツールの目的に応じて適切に切り分けています。

まとめ

  • Mersenne Twister (MT19937) は 1998 年広島大学発の疑似乱数アルゴリズム
  • 周期 2^19937 - 1 (メルセンヌ素数)、623 次元等分布
  • Python・PHP・Excel・MATLAB・R・C++・Ruby のデフォルト乱数として採用
  • JavaScript Math.random() は 2015 年以降 xorshift 系列に移行
  • 暗号学的に安全ではない — パスワード・トークン生成には絶対に使わない
  • セキュリティ用途では crypto.getRandomValues() / secrets / os.urandom を使う
  • 本サイトのツールは目的に応じて Math.random と CSPRNG を使い分け

参考文献・ソース

記事作成に関する注記

本記事は AI(大規模言語モデル)を編集補助として活用して作成しています。 公開前に編集者が内容を確認していますが、事実誤認・仕様の解釈ミス・最新情報との齟齬が含まれる可能性があります。 重要な判断を行う際は、本文中の一次ソースや公式ドキュメントを必ずご自身でご確認ください。 誤りにお気づきの場合は、お問い合わせフォームよりご連絡いただけると助かります。

🔧 関連ツール

📚 関連記事