C++11生成隨機(jī)數(shù)(random庫(kù))的使用
在 C++ 程序中,在新標(biāo)準(zhǔn)出現(xiàn)之前,C 和 C++ 都依賴一個(gè)簡(jiǎn)單的 C 庫(kù)函數(shù) rand 來(lái)生成隨機(jī)數(shù),但是,這個(gè)函數(shù)生成的是均勻分布的偽隨機(jī)數(shù),每個(gè)隨機(jī)數(shù)的范圍在 0 和一個(gè)系統(tǒng)相關(guān)的最大值(至少為 32767)之間。
rand 函數(shù)有一些問(wèn)題:即使不是大多數(shù),也有很多程序需要不通范圍的隨機(jī)數(shù)。一些應(yīng)用需要隨機(jī)浮點(diǎn)數(shù)。一些程序需要非均勻分布的隨機(jī)數(shù)。而在編寫程序?yàn)榱私鉀Q這些通常會(huì)轉(zhuǎn)換 rand 生成的隨機(jī)數(shù)的范圍、類型或者是分布時(shí),常常會(huì)引入非隨機(jī)性。
在 C++ 11 標(biāo)準(zhǔn)中,定義在頭文件 random 中的隨機(jī)數(shù)庫(kù)通過(guò)一組協(xié)作的類來(lái)解決這些問(wèn)題,主要用到的是兩個(gè)類:
- 隨機(jī)數(shù)引擎類(random-number engines)
- 隨機(jī)數(shù)分布類(random-number distribution)
其中,一個(gè)引擎類可以生成 unsigned 隨機(jī)數(shù)列,一個(gè)分布使用一個(gè)引擎類生成指定類型的,在給定范圍內(nèi)的,服從指定概率分布的隨機(jī)數(shù)。
1. 隨機(jī)數(shù)引擎和分布
隨機(jī)數(shù)引擎是函數(shù)對(duì)象類,他們定義了一個(gè)調(diào)用運(yùn)算符,該運(yùn)算符不接受參數(shù)并返回一個(gè)隨機(jī)的 unsigned 整數(shù)。我們可以通過(guò)調(diào)用一個(gè)隨機(jī)數(shù)引擎對(duì)象來(lái)生成原始隨機(jī)數(shù)。
default_random_engine e; // 生成隨機(jī)無(wú)符號(hào)數(shù)
for(size_t i=0; i<10; i++)
// e() “調(diào)用”對(duì)象來(lái)生成下一個(gè)隨機(jī)數(shù)
cout << e() <<endl;在上面這幾行的代碼中,定義了一個(gè)名為 e 的 default_random_engine 的對(duì)象。在 for 循環(huán)內(nèi),我們調(diào)用對(duì)象 e 來(lái)獲得下一個(gè)隨機(jī)數(shù)。
1.1 分布類型和引擎
為了得到一個(gè)在指定范圍內(nèi)的數(shù),我們一用一個(gè)分布類型的對(duì)象:
//生成 0 到 9 之間(包含)均勻分布的隨機(jī)數(shù)
uniform_int_distribution<unsigned> u(0,9);
default_random_engine e; // 生成無(wú)符號(hào)隨機(jī)整數(shù)
for (size_t i =0;i<10; i++)
// 將 u 作為隨機(jī)數(shù)源
// 每個(gè)調(diào)用返回在指定范圍內(nèi)并服從均勻分布的值
cout<<u(e)<<" ";
cout<< endl;上面的代碼輸入如下:
0 1 7 4 5 2 0 6 6 9
上面的程序中,我們將 u 定義為 uniform_int_distribution<unsigned> 。這種類型生成均勻分布的 unsigned 值。當(dāng)我們定義一個(gè)這種類型的對(duì)象時(shí),可以提供想要的最小值和最大值。在上面這段代碼中,u(0,9) 表示我們希望得到 0 到 9 之間(包含)的數(shù)。隨機(jī)數(shù)分布類會(huì)使用包含的范圍,從而我們可以得到給定整形的每個(gè)可能值。
類似引擎類型,分布類型也是函數(shù)對(duì)象類。分布類型定義了一個(gè)調(diào)用運(yùn)算符,它接受一個(gè)隨機(jī)數(shù)引擎作為參數(shù)。分布對(duì)象使用它的引擎參數(shù)生成隨機(jī)數(shù),并將其映射到指定的分布。
傳遞給分布對(duì)象的是引擎對(duì)象本身,也就是 u(e),如果我們將調(diào)用寫為 u(e()),含義就變?yōu)閷?nbsp;e 生成的下一個(gè)值傳遞給 u,這會(huì)導(dǎo)致一個(gè)編譯錯(cuò)誤。我們傳遞的是引擎本身,而不是他生成的下一個(gè)值,原因是某些分布可能需要調(diào)用引擎多次才能得到一個(gè)值。
1.2 使用引擎生成一個(gè)數(shù)值序列
隨機(jī)數(shù)發(fā)生器有一個(gè)特性,也就是即使生成的樹看起來(lái)是隨機(jī)的,但是對(duì)于一個(gè)給定的發(fā)生器,每次運(yùn)行程序它都會(huì)返回相同的數(shù)值序列。序列不變這一事實(shí)在 調(diào)試 的時(shí)候十分有用,但是另一方面,使用隨機(jī)數(shù)發(fā)生器的程序也必須考慮到這一特性。
下面介紹一個(gè)例子,需要一個(gè)函數(shù)生成一個(gè) vector,包含 100 個(gè)均勻分布在 0 到 9 之間的隨機(jī)數(shù)。一種錯(cuò)誤的方法是使用下面的代碼:
vector<unsigned >bad_randVec()
{
default_random_engine e;
uniform_int_distribution<unsigned >u(0,9);
vector<unsigned >ret;
for(size_t i = 0;i<100;i++)
ret.push_back(u(e));
return ret;
}
// 但是 每次調(diào)用這個(gè)函數(shù)都會(huì)返回相同的 vector
vector<unsigned >v1(bad_randVec());
vector<unsigned >v2(bad_randVec());
// 將會(huì)打印輸出 equal
cout << ((v1==v2) ? "equal" : "not equal") << endl;上面這段代碼會(huì)輸出 equal,因?yàn)?vector v1 和 v2 具有相同的值。
正確的定義方法是 將引擎和關(guān)聯(lián)的分布對(duì)象定義為 static 的:
vector<unsigned >good_randVec()
{
// 由于我們希望引擎和分布對(duì)象保持狀態(tài),因此應(yīng)該將他們定義為
// static 的,從而每次調(diào)用都生成新的數(shù)
static default_random_engine e;
static uniform_int_distribution<unsigned > u(0,9);
vector<unsigned > ret;
for(size_t i = 0; i<100;i++)
ret.push_back(u(e));
return ret;
}由于 e 和 u 都是 static 的,因此它們?cè)诤瘮?shù)調(diào)用之間會(huì)保持住狀態(tài)。第一次調(diào)用會(huì)使用 u(e) 生成的序列中的前 100 個(gè)隨機(jī)數(shù),第二次調(diào)用會(huì)獲得接下來(lái) 100 個(gè)。以此類推。
注意,一個(gè)給定的隨機(jī)數(shù)發(fā)生器已知會(huì)生成相同的隨機(jī)數(shù)序列。一個(gè)函數(shù)如果定義了局部的隨機(jī)數(shù)發(fā)生器,應(yīng)該將其(包括引擎和分布對(duì)象)定義為 static 的。否則,每次調(diào)用函數(shù)都會(huì)生成相同的序列。
1.3 設(shè)置隨機(jī)數(shù)發(fā)生器種子
隨機(jī)數(shù)發(fā)生器會(huì)生成相同的隨機(jī)數(shù)序列這一特性在調(diào)試中很有用。但是,一旦我們的程序調(diào)試完畢,我們通常希望每次運(yùn)行程序都會(huì)生成不同的隨機(jī)結(jié)果,可以通過(guò)提供一個(gè)種子(seed)來(lái)達(dá)到這個(gè)目的。種子就是一個(gè)數(shù)值,殷勤可以利用它從序列中一個(gè)新位置重新開始生成隨機(jī)數(shù)。
為引擎設(shè)置種子有兩種方式:
- 在創(chuàng)建引擎對(duì)象時(shí)提供種子
- 調(diào)用引擎的 seed 成員
// 幾乎肯定是生成隨機(jī)整數(shù) vector 的錯(cuò)誤方法
// 每次調(diào)用這個(gè)函數(shù)都會(huì)生成相同的 100 個(gè)數(shù)
default_random_engine e1; // 使用默認(rèn)種子
default_random_engine e2(2147483646); // 使用給定的種子值
// e3 和 e4 將會(huì)生成相同的序列,因?yàn)樗麄兪褂昧讼嗤姆N子
default_random_engine e3;
e3.seed(32767); //調(diào)用 seed 設(shè)置為一個(gè)新種子值
default_random_engine e4(32767); // 將種子值設(shè)置為 32767
for(size_t i = 0;i != 10; i++)
{
if (e1() == e2())
cout<<"unseeded match at iteeration: "<<i<<endl;
if (e3() != e4())
cout<<"seeded differs at itertion: "<<i<<endl;
}設(shè)置種子最常用的方法是調(diào)用系統(tǒng)函數(shù) time ,這個(gè)函數(shù)定義再頭文件 ctime 中,它返回一個(gè)特定時(shí)刻到當(dāng)前經(jīng)過(guò)了多少秒。函數(shù) time 接受單個(gè)指針參數(shù),它指向用于寫入時(shí)間的數(shù)據(jù)結(jié)構(gòu)。如果此指針為空,則函數(shù)簡(jiǎn)單的返回時(shí)間:
default_random_engine e1(time(0)); // 稍微隨機(jī)些的種子--把0換成NULL也行
但是,由于 time 返回以秒計(jì)的時(shí)間,因此這種方式只適用于生成種子的間隔為秒級(jí)或更長(zhǎng)的應(yīng)用。
2. 其他隨機(jī)數(shù)分布
2.1 生成隨機(jī)實(shí)數(shù)
程序常常需要一個(gè)隨機(jī)浮點(diǎn)數(shù)源。特別是程序經(jīng)常需要 0 到 1 之間的隨機(jī)數(shù)。
可一定以一個(gè) uniform_real_distribution 類型的對(duì)象,并讓標(biāo)準(zhǔn)庫(kù)來(lái)處理從隨機(jī)整數(shù)到隨機(jī)浮點(diǎn)數(shù)的映射。與處理 uniform_int_distribution 一樣,在定義對(duì)象時(shí),我們指定最小值和最大值。
default_random_engine e; // 生成無(wú)符號(hào)隨機(jī)整數(shù)
// 0 到 1 (包含)的均勻分布
uniform_real_distribution<double >u(0,1);
for(size_t i =0;i<10;i++)
cout<<u(e)<<" ";
cout<<endl;此外,當(dāng)我們對(duì)分布函數(shù)不指定默認(rèn)生成的類型參數(shù)時(shí),程序會(huì)自動(dòng)賦予一個(gè)類型,生成浮點(diǎn)值得分布類型默認(rèn)生成 double 類型,生成整型值的分布類型默認(rèn)生成 int 類型,如下:
uniform_real_distribution<>u(-1,1); // 默認(rèn)生成 double 值
2.2 生成非均勻分布的隨機(jī)數(shù)
除了生成上面的均勻分布,C++ 11 還規(guī)定了可以生成 20 種不同的分布類型,比如 均勻分布uniform,正態(tài)分布normal,二項(xiàng)分布binomial,泊松分布poisson,學(xué)生分布 student 等等,相關(guān)函數(shù)可以查看相應(yīng)的函數(shù)(具體可以參考 C++ Primer 781頁(yè))。
到此這篇關(guān)于C++11生成隨機(jī)數(shù)(random庫(kù))的使用的文章就介紹到這了,更多相關(guān)C++11生成隨機(jī)數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 有關(guān)C++中隨機(jī)函數(shù)rand() 和srand() 的用法詳解
- c++報(bào)錯(cuò)問(wèn)題解決方案lvalue required as left operand of assignment
- C++ Boost Random隨機(jī)函數(shù)詳解
- C++11新特性之隨機(jī)數(shù)庫(kù)(Random?Number?Library)詳解
- C++ random_shuffle()方法案例詳解
- C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- C++如何解決rand()函數(shù)生成的隨機(jī)數(shù)每次都一樣的問(wèn)題
相關(guān)文章
Pipes實(shí)現(xiàn)LeetCode(195.第十行)
這篇文章主要介紹了Pipes實(shí)現(xiàn)LeetCode(195.第十行),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
C語(yǔ)言中不定參數(shù)?...?的語(yǔ)法以及函數(shù)封裝
不定參數(shù)是指函數(shù)可以接收不確定個(gè)數(shù)的參數(shù),下面這篇文章主要給大家介紹了關(guān)于C語(yǔ)言中不定參數(shù)?...?的語(yǔ)法以及函數(shù)封裝的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-01-01
C語(yǔ)言實(shí)現(xiàn)學(xué)生管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)學(xué)生管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-02-02

