網站首頁 編程語言 正文
在 C++ 程序中,在新標準出現之前,C 和 C++ 都依賴一個簡單的 C 庫函數?rand
?來生成隨機數,但是,這個函數生成的是均勻分布的偽隨機數,每個隨機數的范圍在 0 和一個系統相關的最大值(至少為 32767)之間。
??rand
?函數有一些問題:即使不是大多數,也有很多程序需要不通范圍的隨機數。一些應用需要隨機浮點數。一些程序需要非均勻分布的隨機數。而在編寫程序為了解決這些通常會轉換?rand
?生成的隨機數的范圍、類型或者是分布時,常常會引入非隨機性。
??在 C++ 11 標準中,定義在頭文件?random
?中的隨機數庫通過一組協作的類來解決這些問題,主要用到的是兩個類:
- 隨機數引擎類(random-number engines)
- 隨機數分布類(random-number distribution)
其中,一個引擎類可以生成?unsigned
?隨機數列,一個分布使用一個引擎類生成指定類型的,在給定范圍內的,服從指定概率分布的隨機數。
1. 隨機數引擎和分布
??隨機數引擎是函數對象類,他們定義了一個調用運算符,該運算符不接受參數并返回一個隨機的?unsigned
?整數。我們可以通過調用一個隨機數引擎對象來生成原始隨機數。
default_random_engine e; // 生成隨機無符號數 for(size_t i=0; i<10; i++) // e() “調用”對象來生成下一個隨機數 cout << e() <<endl;
在上面這幾行的代碼中,定義了一個名為?e
?的?default_random_engine?的對象。在 for 循環內,我們調用對象 e 來獲得下一個隨機數。
1.1 分布類型和引擎
??為了得到一個在指定范圍內的數,我們一用一個分布類型的對象:
//生成 0 到 9 之間(包含)均勻分布的隨機數 uniform_int_distribution<unsigned> u(0,9); default_random_engine e; // 生成無符號隨機整數 for (size_t i =0;i<10; i++) // 將 u 作為隨機數源 // 每個調用返回在指定范圍內并服從均勻分布的值 cout<<u(e)<<" "; cout<< endl;
上面的代碼輸入如下:
0 1 7 4 5 2 0 6 6 9
??上面的程序中,我們將?u
?定義為?uniform_int_distribution<unsigned>
?。這種類型生成均勻分布的?unsigned
?值。當我們定義一個這種類型的對象時,可以提供想要的最小值和最大值。在上面這段代碼中,u(0,9)
?表示我們希望得到 0 到 9 之間(包含)的數。隨機數分布類會使用包含的范圍,從而我們可以得到給定整形的每個可能值。
??類似引擎類型,分布類型也是函數對象類。分布類型定義了一個調用運算符,它接受一個隨機數引擎作為參數。分布對象使用它的引擎參數生成隨機數,并將其映射到指定的分布。
??傳遞給分布對象的是引擎對象本身,也就是?u(e)
,如果我們將調用寫為?u(e())
,含義就變為將?e
?生成的下一個值傳遞給?u
,這會導致一個編譯錯誤。我們傳遞的是引擎本身,而不是他生成的下一個值,原因是某些分布可能需要調用引擎多次才能得到一個值。
1.2 使用引擎生成一個數值序列
??隨機數發生器有一個特性,也就是即使生成的樹看起來是隨機的,但是對于一個給定的發生器,每次運行程序它都會返回相同的數值序列。序列不變這一事實在?調試?的時候十分有用,但是另一方面,使用隨機數發生器的程序也必須考慮到這一特性。
??下面介紹一個例子,需要一個函數生成一個 vector,包含 100 個均勻分布在 0 到 9 之間的隨機數。一種錯誤的方法是使用下面的代碼:
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; } // 但是 每次調用這個函數都會返回相同的 vector vector<unsigned >v1(bad_randVec()); vector<unsigned >v2(bad_randVec()); // 將會打印輸出 equal cout << ((v1==v2) ? "equal" : "not equal") << endl;
上面這段代碼會輸出?equal,因為 vector v1 和 v2 具有相同的值。
正確的定義方法是?將引擎和關聯的分布對象定義為 static 的:
vector<unsigned >good_randVec() { // 由于我們希望引擎和分布對象保持狀態,因此應該將他們定義為 // static 的,從而每次調用都生成新的數 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 的,因此它們在函數調用之間會保持住狀態。第一次調用會使用?u(e)
?生成的序列中的前 100 個隨機數,第二次調用會獲得接下來 100 個。以此類推。
??注意,一個給定的隨機數發生器已知會生成相同的隨機數序列。一個函數如果定義了局部的隨機數發生器,應該將其(包括引擎和分布對象)定義為?static
?的。否則,每次調用函數都會生成相同的序列。
1.3 設置隨機數發生器種子
??隨機數發生器會生成相同的隨機數序列這一特性在調試中很有用。但是,一旦我們的程序調試完畢,我們通常希望每次運行程序都會生成不同的隨機結果,可以通過提供一個種子(seed)來達到這個目的。種子就是一個數值,殷勤可以利用它從序列中一個新位置重新開始生成隨機數。
??為引擎設置種子有兩種方式:
- 在創建引擎對象時提供種子
- 調用引擎的 seed 成員
// 幾乎肯定是生成隨機整數 vector 的錯誤方法 // 每次調用這個函數都會生成相同的 100 個數 default_random_engine e1; // 使用默認種子 default_random_engine e2(2147483646); // 使用給定的種子值 // e3 和 e4 將會生成相同的序列,因為他們使用了相同的種子 default_random_engine e3; e3.seed(32767); //調用 seed 設置為一個新種子值 default_random_engine e4(32767); // 將種子值設置為 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; }
設置種子最常用的方法是調用系統函數?time
?,這個函數定義再頭文件?ctime
?中,它返回一個特定時刻到當前經過了多少秒。函數?time
?接受單個指針參數,它指向用于寫入時間的數據結構。如果此指針為空,則函數簡單的返回時間:
default_random_engine e1(time(0)); // 稍微隨機些的種子--把0換成NULL也行
但是,由于?time
?返回以秒計的時間,因此這種方式只適用于生成種子的間隔為秒級或更長的應用。
2. 其他隨機數分布
2.1 生成隨機實數
??程序常常需要一個隨機浮點數源。特別是程序經常需要 0 到 1 之間的隨機數。
??可一定以一個?uniform_real_distribution
?類型的對象,并讓標準庫來處理從隨機整數到隨機浮點數的映射。與處理?uniform_int_distribution
?一樣,在定義對象時,我們指定最小值和最大值。
default_random_engine e; // 生成無符號隨機整數 // 0 到 1 (包含)的均勻分布 uniform_real_distribution<double >u(0,1); for(size_t i =0;i<10;i++) cout<<u(e)<<" "; cout<<endl;
此外,當我們對分布函數不指定默認生成的類型參數時,程序會自動賦予一個類型,生成浮點值得分布類型默認生成?double
?類型,生成整型值的分布類型默認生成?int
?類型,如下:
uniform_real_distribution<>u(-1,1); // 默認生成 double 值
2.2 生成非均勻分布的隨機數
??除了生成上面的均勻分布,C++ 11 還規定了可以生成 20 種不同的分布類型,比如 均勻分布uniform,正態分布normal,二項分布binomial,泊松分布poisson,學生分布 student 等等,相關函數可以查看相應的函數(具體可以參考 C++ Primer 781頁)。
原文鏈接:https://blog.csdn.net/CSDNwei/article/details/113865349
相關推薦
- 2022-07-11 Verilog中$display和$write任務以及格式化輸出
- 2022-10-09 C#使用struct類型作為泛型Dictionary<TKey,TValue>的鍵_C#
- 2022-05-05 Tomcat使用https配置實戰教程_Tomcat
- 2023-11-22 Linux fatal: unable to access ‘https://github xxxx
- 2023-03-03 一文詳解Go?Http?Server原理_Golang
- 2022-09-06 React封裝CustomSelect組件思路詳解_React
- 2022-08-19 Go語言fsnotify接口實現監測文件修改_Golang
- 2022-04-19 python 讀寫yaml
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支