C++11 の乱数ライブラリ <random>
C++11 では、新しい乱数ライブラリのヘッダ <random> が追加されました。
一様にランダムな整数を生成する「乱数生成エンジン」と、値を特定の方法で分布させる「分布生成器」が用意されています。
乱数生成エンジンは
- 線形合同法
- メルセンヌ・ツイスター
- Lagged Fibonacci 法
- ハードウェアエントロピーソース(時刻や CPU カウンター、I/O の状態など大量の低レベルシステム情報)を利用した非決定論的(予測不可能)な乱数生成
の 4 種類が実装されています。非決定論的な乱数生成以外の手法では、初期シードから擬似乱数列を生成します。
分布生成器は
- 一様分布
- 正規分布
- ベルヌーイ分布
- ポアソン分布
をはじめ多数のアルゴリズムが実装されています。
非決定論的な乱数生成エンジン std::random_device を使ってランダムな値を出力してみましょう。プログラムを実行するたびに結果は変わります。
# include <iostream> # include <random> int main() { std::random_device rd; for(int i=0; i<10; ++i) { std::cout << rd() << '\n'; } }
3308319028 365183181 2340454256 3096350927 4279452162 187611341 2401382326 2350646780 3808287678 2785229970
std::random_device は毎回ハードウェアエントロピーソースを収集するため、実行速度が遅いのが欠点です。
パフォーマンスが必要な用途には、初期シードから長周期の乱数列を高速に生成するメルセンヌ・ツイスターの使用を検討しましょう。
次の例では、メルセンヌ・ツイスターの初期シードに std::random_device で生成した乱数を与えています。
# include <iostream> # include <random> int main() { std::random_device rd; std::mt19937 mt(rd()); for(int i=0; i<10; ++i) { std::cout << mt() << '\n'; } }
713509159 1816771193 3197916962 2547031796 2527415289 1626738998 2687124336 2472947651 795929379 2943562879
シードを配列で与えることもできます。
# include <iostream> # include <random> # include <array> int main() { std::random_device rd; std::array<unsigned,100> seeds; for(auto& s : seeds) { s = rd(); } std::seed_seq seq(seeds.begin(),seeds.end()); std::mt19937 mt(seq); for(int i=0; i<10; ++i) { std::cout << mt() << '\n'; } }
乱数を、サイコロ [1,6] や ゲームのスコア [0.0,10.0] のように、特定の型と範囲に分布させるには、分布生成器を使います。
一様分布生成器 uniform distribution を使って、今述べた 2 つの範囲に一様に分布する乱数を作りましょう。
# include <iostream> # include <random> int main() { std::random_device rd; std::mt19937 mt(rd()); std::uniform_int_distribution<int> dice(1,6); std::uniform_real_distribution<double> score(0.0,10.0); for(int i=0; i<10; ++i) { std::cout << dice(mt) << '\n'; } for(int i=0; i<10; ++i) { std::cout << score(mt) << '\n'; } }
4 4 1 2 1 3 1 6 4 3 6.92453 0.734112 5.41101 0.659598 7.50605 1.50859 6.84939 6.46092 7.53252 2.54386
一様分布の場合、整数型に対しては uniform_int_distribution, 浮動小数点数型に対しては uniform_real_distribution と区別することに注意しましょう。
正規分布やポアソン分布などの特殊な分布を再現したい場合は
http://ja.cppreference.com/w/cpp/numeric/random
を参考にしてください。
[おまけ]
Visual Studio の std::random_device が利用するハードウェアエントロピーソースの一覧
http://blogs.msdn.com/b/michael_howard/archive/2005/01/14/353379.aspx の 2 件目のコメント