続 シードなしで乱数が欲しい
乱数の精度がそれほど必要なく、シードをスレッド固有領域(TSS)に置く場合、Windowsならstd::rand()を使うのが簡単です。なぜなら、少なくともVC++/Borland/CodeWarriorではrand()のシードがTSSに置かれるからです。呼び出し元にstd::srand()を呼んでもらうのは格好悪いので、
int my_rand() { std::srand( static_cast<unsigned>( std::rand() ^ ::GetTickCount() ^ ::GetCurrentThreadID() ) ); return std::rand(); }
というのを思いつきました。シードにrand()を混ぜているので、連続して呼ばれた際にGetTickCount()の値が変わっていなくても別のシードになる寸法です。
ただし、この方法はUNIXでは使えませんから、結局シードをTSSに自前で置くことになります。
試しにboost::thread_specific_ptrとPOSIXのrand_r()を使ってTSS版rand()を作成し、Cygwinでテストしたところ、元のrand()と比べて100倍くらい遅くなりました。
VC8のrand()はCygwinのrand()とほぼ同じスピードなので、コンパイラ組み込みのTSSサポートを使えばずっと速くなると思うんですが、Cygwinだと__threadキーワードが使用できないみたいです。
で、逃げの一手を打つことにしました。
- 既定では時間/プロセスID/スレッドIDなどからシードを作り、Boost.Randomで乱数を生成する(多くの場合これで十分)
- 1.で問題がある場合は自分で乱数を生成して渡せるようにしておく
まぁ、ごく普通の解ですね。やっぱりグローバル変数は良くない、と。
というわけで、既定の乱数生成はこうなりました。
<hamigaki/detail/random.hpp>
<hamigaki/detail/windows/random.hpp>
<hamigaki/detail/posix/random.hpp>