2007年10月18日

ランダム

Perlの勉強のために、ランダムな文字列を作成する簡単なCGIを作ってみました。ネットで探せば、同じような内容のCGIが、両手では数え切れないほど見つかりますが、複数の文字列を同時に作れることと、そこそこ自由に使用する文字を選べることなどで、ちょっぴり差別化しました。どっちにしろ大したCGIではないですが、もしよければ何かの時に使ってください。2進数、8進数、16進数を構成する文字だけを使って、文字列が作れたりします。

random string generator
http://www.perfectsky.net/cgi/randomstringgenerator.cgi

せっかくこんなCGIを作るのだから、作成する文字列は、できるかぎりしっかりとランダムになるようにしておきたいなと思って、ここ最近いろいろと乱数について調べていたのですが、これは本当に難しくて奥が深いことなんですね・・・ 以下にそのあたりのことをずらずらと書いて見たいと思います。

いろいろな場面で、コンピュータは乱数を生成しますが、その多くはかならずしも質が良いとは言えないんだそうです。これについては次のサイトがものすごく詳しいですので、ぜひ一度読んでみてください。

良い乱数・悪い乱数
http://www001.upp.so-net.ne.jp/isaku/rand.html

少し古い内容も含まれているのかもしれませんが、中にはすごく恐ろしいのも・・・(笑) さらにこちらのサイトでは、C言語のrand関数の質の悪さをわかりやすくグラフにされています。私も試しに、手元のMinGWな環境で、rand関数を使って0と1をランダムに100万回発生させてみましたが、1度も16回以上同じ数字が連続することはありませんでした(すべての環境でこうなるわけではないです)。これは明らかにおかしいですが、私たちが普段使っている乱数はせいぜいこんなものなのですね。

じゃあ今回のCGIではどうしたかというと、先ほどのサイトなどにもでてくる、メルセンヌ・ツイスタ(Mersenne Twister)というかっこいい名前のアルゴリズムを使うことにしました。これがどう優れているかと言われても、正直困ってしまうのですが(笑)、今回のような場合には、たぶん適当だと思います。

Mersenne Twister: A random number generator
http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/mt.html

具体的にはMath-Random-MTというPerlのモジュールを使ってます(Math-Random-MT-Autoというモジュールもあります。詳しく調べていませんが、こっちのほうが何かと高機能かもしれません)。乱数のシードは、10マイクロ秒単位の時間(一応2つ)とプロセスID、それからあらかじめ用意した十分な長さの文字列をひっくるめてSHA-1のハッシュ値を取り、そこから32Bit分取り出して使用しています。今回のケースはまあこれで十分でしょう。

話が少し変わりますが、ネットでいろんなサイトを見ていると、Perlでrand関数を使う前に、srand(time^$$)とかsrand(time^($$+($$<<15)))しろとよく書いてあるんですが(私が持っている本にはsrand(time+$$)と書いてあります)、試しに次のようなスクリプトを動かしてみてください。

for(0..2){
  srand(time^($$+($$<<15)));
  print rand() . "\n";
}
for(0..2){
  srand();
  print rand() . "\n";
}

環境にもよるようですが、出力される最後の3つの数字が、ばらばらの違う数字になってはいないでしょうか? これは要するに、srandを引数なしで使ったときは、秒単位の時間やプロセスID以外のなにかも使われている証拠ですので、変な引数を与えるよりはよっぽどましだということですね。手元の環境では、Windows+ActivePerlで最後の3つの数字がが同じになりますが、FreeBSDな環境だとばらばらになります(なんとなくですが、大抵のUNIXな環境では後者になる気がします)。しかも、Perlの5.004以降では、srandをスクリプトのなかで明示的に呼んでいなくても、最初にrandが呼ばれる時に自動的にsrandを呼ぶようなので、わざわざsrand()などと書く必要もほとんどないらしいです。そのあたりはこちらのsrandの項をぜひ読んでみてください。ただし、さきほどのMath-Random-MTモジュールのsrand関数はrand関数を使う前にかならず一度明示的に呼んで、適切なシードを与えないといけないようですので、そこはちょっと注意してください。

2007年10月18日 00:07