C++隨機數簡介

C Random

C接口中提供的隨機數接口爲rand(),該隨機數接口產生0 ~ RAND_MAX範圍內的均勻分佈的整數,其中RAND_MAX爲32767。通常我們可以用rand()接口產生多種隨機數:

srand(time(NULL)); // 設置隨機數種子
rand(); // 隨機unsigned short 
a + rand() % (b - a); // [a, b]範圍內的整數
a + rand() / RAND_MAX; // [a, a + 1]範圍內的小數
rand() * (1<<16)  + rand(); // [0, (1<<32) - 1)的數

值得一提的是,rand()接口並沒有我們想象的那樣好用,根據視頻rand()的問題[1],C庫隨機數生成的缺點有:

  • rand()生成的隨機數範圍有限,srand(seed)接受的種子範圍有限
  • rand接口產生的隨機數質量較低
  • 使用rand() % n來產生一定範圍的數並不是均勻分佈,因爲不存在一個映射能將32767個值均勻映射到 n個值。

C++ Random

幸運的是,在C++的STL中提供了隨機數質量更高,功能更強大的隨機數生成器和分佈器[2],我們只需要增加<random>頭文件即可。在進入使用C++隨機數生成器之前,我們首先需要區分隨機數生成器和分佈器。

  • 生成器:隨機數生成器使用一個隨機數種子產生僞隨機數。依據所採用的隨機數生成算法,隨機數成器大致分爲三類:線性同餘算法,梅森纏繞器算法,帶進位減算法。

  • 分佈器:隨機數分佈器處理隨機數生成器產生的隨機數,使其滿足一定概率分佈。根據其概率分佈不同,主要分爲:均勻分佈,伯努利分佈,泊松分佈,正態分佈,採樣分佈。詳情參見隨機數分佈

C++ STL中提供了預定義的隨機數生成器非確定的隨機數生成器預定義的隨機數生成器定義了數個特別流行的算法,是較爲良好的僞隨機數生成接口,其中默認的隨機數生成器爲std::default_random_engine非確定隨機數生成器則使用硬件作爲隨機數來源,類型爲std::random_device。random_device由於調用代價較高,通常用來產生隨機數種子。注意若不存在相應硬件,則每次調用產生的隨機數序列均一致。

接口使用

通過上面的介紹,我們大致瞭解了C++隨機數庫的內容,接下來我們嘗試提供基本場景的隨機數生成的例子。

//如果有硬件熵源:產生硬件隨機種子
std::random_device rd;
unsigned int seed = rd();
//替代方案:使用時間產生隨機種子
unsigned int seed = time(NULL);

//使用種子產生隨機數生成器: 下面給出了4種,可按喜好挑選。
std::default_random_engine e1(seed); 
// std::minstd_rand e2(seed);
// std::mt19937 e3(seed);
// std::ranlux48 e4(seed); 

//使用隨機數生成器產生均勻分佈隨機數, 同樣不能像rand() % n方式產生範圍內均勻分佈的隨機數,故一般不使用
//unsigned int rand_num = e1();

//產生一定範圍的隨機整數
std::uniform_int_distribution<int> uniform_dist(0, 99);
int rand_int = uniform_dist(e1); //產生一個0-99的隨機整數,均勻分佈

//產生一定範圍的隨機實數
std::uniform_real_distribution<> uniform_dist2(0, 1);
float rand_real = uniform_dist2(e1); //產生一個0-1的實數,均勻分佈
    
//產生一個正態分佈: 均值爲0,方差爲2
std::normal_distribution<> normal_dist(0, 2);
float sample = normal_dist(e1); //產生一個正態分佈的樣本

故爲了產生可用的隨機數,我們至少得執行兩行代碼準備隨機數生成器和分佈器,隨後調用對應的接口即可生成隨機數序列。

std::default_random_engine e1(time(NULL));
std::uniform_int_distribution<int> dist(0, n);

這比傳統C庫的隨機數生成稍微麻煩了一些:C庫的隨機數種子只需設置一次,即可在任何作用域調用rand()產生隨機數。在C++中隨機數生成器和分佈器僅在相應作用域內可用。

參考文獻

[1]. Avoid Using rand() and srand(), https://stackoverflow.com/questions/18726102/what-difference-between-rand-and-random-functions 2020,2,19

[2]. Pseudo-random Number Generation, https://en.cppreference.com/w/cpp/numeric/random 2020,2,19

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章