続 シードなしで乱数が欲しい

乱数の精度がそれほど必要なく、シードをスレッド固有領域(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キーワードが使用できないみたいです。


で、逃げの一手を打つことにしました。

  1. 既定では時間/プロセスID/スレッドIDなどからシードを作り、Boost.Randomで乱数を生成する(多くの場合これで十分)
  2. 1.で問題がある場合は自分で乱数を生成して渡せるようにしておく

まぁ、ごく普通の解ですね。やっぱりグローバル変数は良くない、と。


というわけで、既定の乱数生成はこうなりました。
<hamigaki/detail/random.hpp>
<hamigaki/detail/windows/random.hpp>
<hamigaki/detail/posix/random.hpp>