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 件目のコメント