はじめに
この記事は初心者C++er Advent Calendar 2016の14日目の記事です。
C++のライブラリってすごく機能がいっぱいあるように思うんですけど、なんか汎用的すぎたり、痒いところに手が届かなかったりするんですよね。
今回はそんな話をしたいと思います。
真の乱数
<random>
ヘッダの話をします。
乱数ってよく使いますよね。
でもパソくんはサイコロを振る事ができません。計算をすることしかできないのです。
まあ、そこで現在の時刻とかCPUの温度とかを使ってランダムっぽい数値を引っ張ってきます。
それをやってくれるのは
std::random_device
というやつである。
これはクラスなので
std::random_device rd{};
std::cout << rd() << std::endl;
のようにして、いちいちオブジェクトを作ったりして使う。
random_device
クラスのoperator()
を呼び出すと乱数を得ることができる。
備考(C++がチョットできる人向け)
このstd::random_device
というクラスはコピーもムーブもできない。
したがってstd::vector<std::uint32_t>
をgenerateを使って乱数で埋めたい場合次のようになる。
std::random_device rd{};
std::vector<std::uint32_t> vec(10);
std::generate( vec.begin(), vec.end(), std::ref(rd) );
std::ref(rd)
のようになります。
std::ref
は<functional>
にあったと思います。
疑似乱数
いちいち真の乱数を引っ張ってくるのはコストであるため、最初乱数(初期シード)から乱数っぽい値を計算してくれるアルゴリズム的なものがあります。
それを擬似乱数と呼びます。
これらには種類がいろいろとありまして、
- minstd_rand0
- minstd_rand
- mt19937
- mt19937_64
- ranlux24_base
- ranlux48_base
- ranlux24
- ranlux48
- knuth_b
- default_random_engine
と、これだけあります。
std::mt19937
を使っとけばだいたい問題がナイと思います(適当
seed と seed_seq
擬似乱数を使う場合、
std::mt19937 mt;
std::cout << mt() << std::endl;
のようにすれば擬似乱数が得られる。
しかしこの場合はプログラムの実行をするたびに同じ値が表示される。
擬似乱数には初期値として初期シードと呼ばれる乱数を与えなければならない。
std::random_device rd;
std::mt19937 mt(rd());
std::cout << mt() << std::endl;
が正しい。
さて、C++にはstd::seed_seq
なるクラスが用意されていてこれで擬似乱数生成器クラスを初期化できる。
std::seed_seq
はイテレータのペアをとる。
std::random_device rd{};
std::vector<std::uint32_t> vec(10);
std::generate( vec.begin(), vec.end(), std::ref(rd) );
std::mt19937 mt(std::seed_seq(vec.begin(), vec.end()));
とやってやるのだ。
Distribution
最後に、ディストリビューションについてでさ。
ランダムな整数って使いにくい、1~6とかが使いやすい。
そう思うのは当たり前である。
それを実現するのがdistribution
達である。
##一様分布乱数
整数のやつ。
std::uniform_int_distribution<IntType>
IntType
には好きな整数型を指定する。
std::mt19937 mt{ std::random_device{}() };
std::uniform_int_distribution<int> dist(1, 6);
for ( int i = 0 ; i != 10 ; ++i )
std::cout << dist(mt) << std::endl;
使い方はご覧の通りである。
dist(1, 6)
とすれば1~6になる。
呼び出すときには、擬似乱数生成エンジンを渡す。
浮動小数点のやつ。
std::uniform_real_distribution<RealType>
もあるよ。
使い方は一緒。
分布乱数は他にもたくさんあります。
ベルヌーイ分布・正規分布・ポワソン分布・標本分布などがあります。
乱数まとめ
-
擬似乱数生成エンジンを使う(mt19937でおk)
-
エンジンを真の乱数で初期化する(std::random_deviceを使おう)
-
分布乱数を使うときは分布乱数クラスを使う。エンジンを渡して呼び出す。
面倒くさい、1~6の乱数を得るためにやることが多すぎだ(´・_・`)