C++ でお手軽乱数(boost::random のラッパー)


C++ で乱数、特に正規乱数とか欲しいなあ。


rand() 関数は使っちゃダメ! 絶対! ということらしいので、boost::random を使ってみた。
が、なんでちょこっと乱数が欲しいくらいでそんなややこしいコード書かないといけないの!? と、キレたくなるほど複雑。
同じ型名を何度も書くのとか、ダサダサでしょう……


なので、最小限の汎用性を持たせつつ、便利でお手軽に使えるラッパーを書いてみた。

#include <boost/random.hpp>

template<class D, class G = boost::mt19937>
class Rand {
    G gen_;
    D dst_;
    boost::variate_generator<G, D> rand_;
public:
    Rand() : gen_(static_cast<unsigned long>(time(0))), rand_(gen_, dst_) {}
    template<typename T1>
    Rand(T1 a1) : gen_(static_cast<unsigned long>(time(0))), dst_(a1), rand_(gen_, dst_) {}
    template<typename T1, typename T2>
    Rand(T1 a1, T2 a2) : gen_(static_cast<unsigned long>(time(0))), dst_(a1, a2), rand_(gen_, dst_) {}

    typename D::result_type operator()() { return rand_(); }
};

ちょっとシード周りがキナ臭い(連続してインスタンス化したら……)けど、まあお手軽版ということで。
必要なら固定にするなり、外から渡すなり。
ただし、改造するときはメンバーの定義順をいじらないように(初期化順序が変わったら動かなくなる)。


使い方はこんな感じ。

int main(int argc, _TCHAR* argv[]) {

	Rand<boost::uniform_int<> > dice(1,6);      // さいころ(1〜6の整数)
	Rand<boost::uniform_real<> > runif(-1,1);   // 実数 [-1,1]
	Rand<boost::normal_distribution<> > rnorm;  // 正規乱数 N(0,1)

	for(int i=0;i<20;++i)
		std::cout << dice() << " " << runif() << " " << rnorm() << std::endl;

	return 0;
}
4 0.048512 -2.14372
6 0.809638 -0.329267
1 -0.729432 1.15611
5 0.568796 1.31608
      :

テンプレートに distributor(分布関数) を、コンストラクタの引数にその分布関数のコンストラクタに渡す引数を渡せるので、一様乱数や正規乱数だけじゃあなくて、ポワソン分布やコーシー分布など、boost::random に用意されているどの分布にも対応できる、はず。


C++ でテンプレートを含むコードが思い通りに動作すると気持ちいいね。ハマると地獄だけど……