標籤:
本文主要參考自一博文及cplusplus和cppreferrence。其中,該博文是對cplusplus上該偽隨機數條目的翻譯,下文中會參考調整。
1. C語言中的偽隨機數產生函數
2. C++語言中的偽隨機數產生器
C++中偽隨機數庫Random是C++11才開始添加的。它允許我們結合產生器(Generators)和分布器(Distributions)來產生偽隨機數。
產生器(Generators):能夠產生離散的等可能分布數值。
分布器(Distributions): 能夠把產生器產生的均勻分布值映射到其他各種各樣的分布,如均勻分布uniform,常態分佈normal,二項分布binomial,泊松分布poisson。 2.1 一個簡單的例子 下邊就是一個簡單的例子:
1 #include <iostream> 2 #include <random> 3 using namespace std; 4 5 int main() 6 { 7 default_random_engine generator; 8 uniform_int_distribution<int> dis(0, 6); 9 for (int i = 0; i < 6; i++)10 {11 std::cout << dis(generator) << std::endl; // 1 0 1 1 2 612 }13 14 return 0;15 }View Code
如果我們嫌每次都要傳入產生器對象麻煩,我們可以使用std::bind來綁定產生器對象和分布器對象(注意bind在標頭檔functional中)。如下所示:
1 #include <iostream> 2 #include <random> 3 #include <functional> // std::bind 4 using namespace std; 5 6 int main() 7 { 8 default_random_engine generator; 9 uniform_int_distribution<int> dis(0, 6);10 auto dice = bind(dis, generator);11 for (int i = 0; i < 6; i++)12 {13 std::cout << dice() << std::endl; // 1 0 1 1 2 614 }15 16 return 0;17 }View Code
其實我們會發現上邊兩個代碼的結果不管跑多少次都是一樣的,很奇怪!我們可以試著給產生器一個種子,如下:
1 #include <iostream> 2 #include <random> 3 #include <functional> // std::bind 4 using namespace std; 5 6 int main() 7 { 8 default_random_engine generator(10); // seed = 10 9 uniform_int_distribution<int> dis(0, 6);10 auto dice = bind(dis, generator);11 for (int i = 0; i < 6; i++)12 {13 std::cout << dice() << std::endl; // 2 0 4 1 2 414 }15 16 return 0;17 }View Code
現在我們看到結果不一樣了。但是,不管我們再跑多少次,其結果還是一樣的,這就更加奇怪了。究其原因,在於我們設定的種子是固定的。對於電腦而言,當輸入確定時,輸出也一定是確定的。因為產生器演算法和分布器演算法都是確定的,所以當輸入是確定的時候,輸出也必然是確定的。因此,為了得到不一樣的結果,我們可以將系統時間當作種子:
1 #include <iostream> 2 #include <random> 3 #include <functional> // std::bind 4 #include "time.h" // time 5 using namespace std; 6 7 int main() 8 { 9 default_random_engine generator(time(NULL));10 uniform_int_distribution<int> dis(0, 6);11 auto dice = bind(dis, generator);12 for (int i = 0; i < 6; i++)13 {14 std::cout << dice() << std::endl;15 }16 17 return 0;18 }View Code 2.2 關於產生器和分布器
關於產生器和分布器,請參照cplusplus手冊。
下邊是一個來自cppreferrence的例子:
1 #include <iostream> 2 #include <iomanip> 3 #include <string> 4 #include <map> 5 #include <random> 6 #include <cmath> 7 using namespace std; 8 9 int main()10 {11 // Seed with a real random value, if available12 std::random_device rd;13 cout << rd() << endl;14 // Choose a random mean between 1 and 615 std::default_random_engine e1(rd());16 std::uniform_int_distribution<int> uniform_dist(1, 6);17 int mean = uniform_dist(e1);18 std::cout << "Randomly-chosen mean: " << mean << ‘\n‘;19 20 // Generate a normal distribution around that mean21 std::mt19937 e2(rd());22 std::normal_distribution<> normal_dist(mean, 2); // default: double23 24 std::map<int, int> hist;25 for (int n = 0; n < 10000; ++n) {26 ++hist[std::round(normal_dist(e2))];27 }28 std::cout << "Normal distribution around " << mean << ":\n";29 for (auto p : hist) {30 std::cout << std::fixed << std::setprecision(1) << std::setw(2)31 << p.first << ‘ ‘ << std::string(p.second / 200, ‘*‘) << ‘\n‘;32 }33 34 return 0;35 }View Code
我們可以發現上述例子用到了random_device這個產生器。該產生器產生的是非確定性隨機數。在Linux的實現中,是讀取/dev/urandom裝置;在Windows的實現居然是用rand_s。random_device提供()操作符,用來返回一個min()到max()之間的一個數字(min()、max()均為該產生器成員函數)。如果是Linux(Unix Like或者Unix)下,都可以使用這個來產生高品質的隨機數,可以理解為真隨機數。
一個例子如下:
1 // random_device example 2 #include <iostream> 3 #include <random> 4 5 int main () 6 { 7 std::random_device rd; 8 9 std::cout << "default random_device characteristics:" << std::endl;10 std::cout << "minimum: " << rd.min() << std::endl;11 std::cout << "maximum: " << rd.max() << std::endl;12 std::cout << "entropy: " << rd.entropy() << std::endl;13 std::cout << "a random number: " << rd() << std::endl;14 15 return 0;16 }View Code
可能的輸出是:
1 default random_device characteristics:2 minimum: 03 maximum: 42949672954 entropy: 325 a random number: 4230014324
View Code
有些情況下,隨了拿時間當種子,random_device也經常被用來產生種子。
C/C++中如何產生偽隨機數