PHPの疑似乱数と真乱数の詳細


まず声明が必要なのは、コンピュータは絶対的にランダムな乱数を生成しません。コンピュータは「疑似乱数」しか生成できません。実際には絶対ランダムな乱数は理想的な乱数だけです。コンピュータがどのように発展しても、絶対的な乱数は発生しません。コンピュータは相対的な乱数、すなわち疑似乱数しか生成できません。
疑似乱数は偽乱数ではなく、ここでの「偽」は規則的な意味であり、コンピュータによって生成される疑似乱数はランダムであり、また規則的である。どう理解しますか?発生した疑似乱数は時々一定の法則を守り、時にはいかなる法則も守らない。疑似乱数の一部は一定の法則に従う。他の部分はどんな法則も守らない。例えば、「同じ形の木の葉が二つもない」というのは、物事の特性、すなわちランダム性を指していますが、どの木の葉にも似たような形があります。これは事物の共通性、すなわち規則性です。この角度から言えば、コンピューターは絶対的な乱数を生成することができず、疑似乱数しか生成できないという事実を受け入れるかもしれません。
まず,真の乱数と疑似乱数の概念を調べた。
真の乱数発生器:英文は:true randm number generators、略称は:TRNGS、予知できない物理の方式を利用して発生する乱数です。
疑似乱数発生器:英語は:pspeudo-random number generators、略称は:PRNGS、コンピュータが一定のアルゴリズムを利用して発生したのです。
二つの方法で発生した乱数の画像を比較します。
Random.org(大気騒音を利用して乱数を生成し、大気騒音は空気中の雷嵐によって発生する)で生成されるランダムビットマップ:

Windows下のPHPのらんd()関数によるランダム画像:

後者の擬似乱数発生器によって生成された画像にはこのような顕著なストライプがあることが明らかである。
phpのrandランダム関数を利用してこの画像を生成するコードは以下の通りです。

// gd
header("Content-type: image/png");
$im = imagecreatetruecolor(512, 512)
or die("Cannot Initialize new GD image stream");
$white = imagecolorallocate($im, 255, 255, 255);
for ($y=0; $y<512; $y++) {
for ($x=0; $x<512; $x++) {
if (rand(0,1) === 1) {
imagesetpixel($im, $x, $y, $white);
}
}
}
imagepng($im);
imagedestroy($im);
実際にはすべての疑似乱数発生器(PRNGS)の効果が悪いわけではないが、WindowsのPHPにぴったりのランnd関数はこのようになっている。Linuxで同じコードをテストすると、発生した画像にも明らかなストライプが見えません。Windowsでmt_を使えばrand()関数がラド()関数に代わると効果も多くなります。これはmt_のためですランダム数は、Mersenne Twisterアルゴリズムを用いて生成される。PHPの文書によると、mt_ランダム値の平均速度はlibcから与えられたrand()より4倍速い。
また、Linuxカーネル(1.30以上)は、乱数発生器/dev/randomを含み、多くの安全目的に十分である。
Linuxの乱数発生器の原理について説明します。
Linuxオペレーティングシステムは、本質的にランダム(または少なくとも強いランダム性を有するコンポーネント)のライブラリデータを提供する。これらのデータは通常、デバイスドライバから来ます。例えば、キーボードドライバは、2つのキー間の時間の情報を収集し、この環境ノイズを乱数発生器ライブラリに書き込む。
ランダムデータはエントロピープール(linuxカーネルは、デバイスドライバと他のソースからの環境騒音を収集するためのエントロピープールを維持する。理論的にはエントロピープールにおけるデータは完全にランダムであり,真の乱数シーケンスを生成することが可能である。エントロピープールにおけるデータのランダム性を追跡するために,カーネルはデータをプールに追加する際にデータのランダム性を推定し,このプロセスをエントロピー推定と呼ぶ。エントロピー推定値はプールに含まれる乱数の桁数を記述し、その値が大きいほどプールにおけるデータのランダム性が良いことを示します。)の中で、新しいデータが入るたびに「撹拌」します。この撹拌は実際には数学的変換であり,ランダム性の向上を助ける。データがエントロピープールに追加されると,システム推定は真のランダムビットがどれぐらい得られたかを示した。
ランダム性の総量を測定することは重要である。問題はいくつかの量がよく考えた時よりランダム性が小さいということです。例えば、前回キーボードを押してからの秒数を示す32桁を追加したが、実際には32ビットのランダム情報は提供されていない。
エントロピープールは、/dev/randowmからバイトを読み出すと、MD 5アルゴリズムを用いて暗号ハッシュを行い、このハッシュの各バイトはデジタルに変換されてから戻る。
エントロピープールに利用可能なランダムビットがない場合、/dev/randowmはプールに十分なランダム性がある前に待ち、結果を返さない。これは、多くの乱数を生成するために/dev/randowmを使用すると、遅すぎて実用的ではないことが分かります。私たちはよく/dev/randowmが数十バイトのデータを生成し、多くの秒で結果を発生しないことを見ます。
幸いなことに、エントロピー池があるもう一つのインターフェースはこの制限を避けられます。エントロピープールにランダム性がなくても、この代替デバイスは常に乱数を返します。エントロピープールに十分な時間を与えずにたくさんの数を取り出したら、様々なソースの併用エントロピーの利点が得られなくなります。しかし、エントロピープールのMD 5ハッシュから非常に良い乱数が得られます。このような方式の問題は、MD 5アルゴリズムを解読した人がいれば、ハッシュ入力に関する情報を表示出力によって知ると、あなたの数はすぐに完全に予測できるようになります。ほとんどの専門家はこのような分析は計算の観点から実行できないと考えています。しかし、依然としてdev/urandom比/dev/randomは「安全ではない」と考えています。
Windowsでは/dev/randomは利用できませんが、マイクロソフトの「capicomp.dll」が提供しているCAPICOM.Utilitiesオブジェクトを使用することができます。
以下はPHP使用時のmt_を使用します。rand()関数は、より良い疑似乱数の一例コードを生成する。

<?php
// get 128 pseudorandom bits in a string of 16 bytes

$pr_bits = '';

// Unix/Linux platform?
$fp = @fopen('/dev/urandom','rb');
if ($fp !== FALSE) {
$pr_bits .= @fread($fp,16);
@fclose($fp);
}

// MS-Windows platform?
if (@class_exists('COM')) {
try {
$CAPI_Util = new COM('CAPICOM.Utilities.1');
$pr_bits .= $CAPI_Util->GetRandom(16,0);

// if we ask for binary data PHP munges it, so we
// request base64 return value. We squeeze out the
// redundancy and useless ==CRLF by hashing...
if ($pr_bits) { $pr_bits = md5($pr_bits,TRUE); }
} catch (Exception $ex) {
// echo 'Exception: ' . $ex->getMessage();
}
}

if (strlen($pr_bits) < 16) {
// do something to warn system owner that
// pseudorandom generator is missing
}
?>

PHPは本物の乱数を生成するか、それとも外部の要素を呼び出してサポートします。