1、介紹
在C++11新標(biāo)準(zhǔn)之前, 我們程序都是依賴于一個(gè)簡單的C庫函數(shù)rand來生成隨機(jī)數(shù)。此函數(shù)生成均勻分布的偽隨機(jī)數(shù),每個(gè)隨機(jī)數(shù)的范圍在0和一個(gè)系統(tǒng)相關(guān)的最大值之間。
C++11的頭文件random中有一組隨機(jī)數(shù)庫通過一組協(xié)作的類來解決這些問題:隨機(jī)數(shù)引擎類和隨機(jī)數(shù)分布類。
2、隨機(jī)數(shù)引擎和分布的使用
隨機(jī)數(shù)引擎的操作:
Engine e; //默認(rèn)構(gòu)造函數(shù),使用該引擎類型默認(rèn)的種子
Engine e(s); //使用整型值s作為種子
e.seed(s); //使用種子s重置引擎的狀態(tài)
e.min(); //此引擎可生成的最小值
e.max(); //此引擎可生成的最大值
Engine::result_type; //此引擎生成的unsigned整型類型
e.discard(u); //將引擎推進(jìn)u步; u的類型為unsigned long long
調(diào)用隨機(jī)數(shù)引擎的原始隨機(jī)數(shù):
default_random_engine dre;
for (int i = 0; i < 10; ++i)
cout << dre() << endl;
大多數(shù)場合下隨機(jī)數(shù)引擎輸出的原始隨機(jī)數(shù)是不能直接使用的,需要進(jìn)行轉(zhuǎn)換。
使用分布類型和引擎,生成0到9之間均勻分布的隨機(jī)數(shù)
default_random_engine dre;
uniform_int_distribution<unsigned> uidu(0, 9);
for (int i = 0; i < 10; ++i)
cout << uidu(dre) << endl;
上面的uniform_int_distribution<unsigned>是均勻分布,生成0到9之間的隨機(jī)數(shù),將default_random_engine 傳遞給uidu,便可以生成均勻分布在0到9之間的隨機(jī)數(shù),一般我們將隨機(jī)數(shù)發(fā)生器的時(shí)候,是指分布和引擎。
隨機(jī)數(shù)引擎有一個(gè)序列不變的特性,在調(diào)試的時(shí)候非常有用,但是要是不注意的話代碼的結(jié)果便會(huì)出錯(cuò)。看下面的例子:
vector<unsigned> bad_randvec()
{
default_random_engine dre;
uniform_int_distribution<unsigned> uidu(0,9);
vector<unsigned> ret;
for (size_t i = 0; i < 10; ++i)
ret.push_back(uidu(dre));
return ret;
}
上面的代碼看起來是返回一個(gè)10個(gè)隨機(jī)數(shù)的vector,但是實(shí)際上在每次調(diào)用的時(shí)候,返回值都是一樣的。正確寫法應(yīng)該是將分布和引擎設(shè)置為static。
vector<unsigned> bad_randvec()
{
static default_random_engine dre;
static uniform_int_distribution<unsigned> uidu(0,9);
vector<unsigned> ret;
for (size_t i = 0; i < 10; ++i)
ret.push_back(uidu(dre));
return ret;
}
這樣每次輸出的結(jié)果便是隨機(jī)的了。
設(shè)置隨機(jī)數(shù)發(fā)生器的種子
default_random_engine dre1(123);
default_random_engine dre2;
dre2.seed(time(0));
for (int i = 0; i < 10; ++i)
cout << dre1() << ' ';
cout << endl;
for (int i = 0; i < 10; ++i)
cout << dre2() << ' ';
cout << endl;
其中dre1使用123作為隨機(jī)數(shù)種子,dre2使用默認(rèn)的隨機(jī)數(shù)種子,而dre2.seed(time(0))是使用時(shí)間為隨機(jī)數(shù)種子,使得隨機(jī)數(shù)種子不是固定的。
生成隨機(jī)實(shí)數(shù)
很多時(shí)候我們需要生成0到1之間的隨機(jī)浮點(diǎn)數(shù),我們可以使用生成實(shí)數(shù)的分布。
default_random_engine dre3;
uniform_real_distribution<double> urdd(0, 1); //<>中可以是空,即使用默認(rèn)模板參數(shù),默認(rèn)為double
for (int i = 0; i < 10; ++i)
cout << urdd(dre3) << ' ';
cout << endl;
分布類型d的常用函數(shù)還有以下幾個(gè):
d.min(); //返回d(e)能生成的最小值
d.max(); //返回d(e)能生成的最大值
d.reset(); //重建d的狀態(tài),使得隨后對(duì)d的使用不依賴于d已經(jīng)生成的值
生成非均勻分布的隨機(jī)數(shù)
除了均勻分布之外,我們往往還需要用到一些非均勻分布的隨機(jī)數(shù),如正態(tài)分布。
default_random_engine dre4;
normal_distribution<> nd(4, 1.5);//均值為4,標(biāo)準(zhǔn)差為1.5
vector<unsigned> vals(9);
for (size_t i = 0; i < 200; ++i)
{
unsigned val = lround(nd(dre4));
if (val < vals.size())
++vals[val];
}
for (auto e : vals)
{
cout << string(e, '*') << endl;
}
程序輸出如下:
***
***********
******************
********************************************
*******************************************************
********************************
******************************
*****
*
可以看到輸出的結(jié)果是大致符合正態(tài)分布,但是并不是完全對(duì)稱的,如果是完全對(duì)稱反倒證明這個(gè)隨機(jī)數(shù)生成器的性能應(yīng)該不好。
伯努利分布
隨機(jī)數(shù)中還支持生成伯努利分布的隨機(jī)數(shù)。聲明和調(diào)用的形式與前述的分布和引擎相似。
default_random_engine e;
bernoulli_distribution b1; //默認(rèn)是50 / 50 的機(jī)會(huì),
/*TODO*/
伯努利分布還可以調(diào)整概率:
default_random_engine e;
bernoulli_distribution b2(.60); //概率為60 / 40 的機(jī)會(huì),
/*TODO*/
總結(jié)
C++11新標(biāo)準(zhǔn)中的定義的隨機(jī)數(shù)的分布有20種,本文只是介紹了比較常用的幾種,新標(biāo)準(zhǔn)引入的不同分布的隨機(jī)數(shù),為程序的編寫帶來了更多的方便,可以舍棄原有C的rand函數(shù)了。