乱数生成 [C++]
数学的に乱雑であることが求められるようなことはともかく、通常のアプリケーションでは乱数はあまり利用されないので、乱数生成は軽視されがちでもある。
ANSI Cの乱数の精度の悪さは、誰かがやってくれていたが、下位1ビット(数ビット)に規則性があるので、2の剰余などを選んだ場合にかなり偏りが出ることが、すでに判明している。
もっと精度がよく、しかもパフォーマンスのよい乱数は無いかということで、メルセンヌツイスタというものがよく使われている。
そして、そのメルセンヌツイスタが、Boostにも存在し、randomをインクルードすることで利用できる。
しかし、乱数を利用するときに、安易に剰余を使うと偏りが出やすく、また、MTに対して負の値を使うと結果は未定義であるという。(便利な代わりに何かしら対価を払うのは当然ではある、この場合は制約や手間)
よって正しい使い方はboostにあるrandom_demo.cppにあるようにしなければならない。
種に負の値が入らないようにunsigedとして定義する
// Define a random number generator and initialize it with a reproducible
// seed.
// (The seed is unsigned, otherwise the wrong overload may be selected
// when using mt19937 as the base_generator_type.)
base_generator_type generator(42u);
値の範囲を定義するときは、安易に剰余を使わず、uniform_realやuniform_intを使う
// Define a uniform random number distribution which produces "double"
// values between 0 and 1 (0 inclusive, 1 exclusive).
boost::uniform_real<> uni_dist(0,1);
boost::variate_generator<base_generator_type&, boost::uniform_real<> > uni(generator, uni_dist);
あとはuni()を呼び出すだけで乱数が生成される。
上記の例だと種が固定されているので、以下のようにすることで、時間ベースの種が設定される
generator.seed(static_cast<unsigned int>(std::time(0)));
しかし、ANSIベースのtimeはms単位ではないので、連続して実行されるような場合には種が統一になる可能性をはらんでいる。
timeの戻り値は1970 年 1 月 1 日午前 0 時以降の経過秒数を返してくるが、ネットワークで同期したときに、違うPCで同じ結果を生む可能性もあるなど、秒単位では心もとないことも多い。(乱数を使うときは、違う結果が出ることを求めていることが多い)。
よって、Windowsでの開発に限って言えば、time(0)ではなくGetTickCount()を使うのが何かと便利であろう。
コメント 0