rand()函數
rand()函數是從C語言繼承過來的隨機數函數,存在於標准庫<cstdlib>中。它會向我們返回一個0~RAND_MAX(32767)的整數,不需要參數。
簡單測試一下:
int main( ){ for(int i=0;i<5;i++){ cout<<rand()<<endl; } } /** 輸出結果為: 41 18467 6334 26500 19169 **/
好像確實輸出了一串隨機數,但事實上,無論何時何地去運行這段代碼,得到的結果總是一樣的。
隨機數種子
rand()函數的作用其實並不是生成隨機數,而是根據種子生成一個序列,它的邏輯是這樣的:
規定一個函數f,令a1=f(seed),an+1=f(an)。如此下去,就得到了一個數字序列,在程序里每調用一次rand()函數,就返回這個序列中的下一個數。只要函數f的計算方式足夠復雜(事實上rand()函數的計算原理確實比較復雜),那么表現出來就是隨機的。由於這個數列的首項是對種子seed的運算結果,所以只要種子相同,得到的數列也是一樣的,這也能解釋為什么每次調用rand()得到的第一個隨機數都是41,因為種子默認為1。
事實上,我們知道計算機系統都是按照指令來行動的,而處理這些指令的邏輯都是既定的。因此,在計算機系統內部,完全的真隨機數是不存在的,想要實現真隨機數,就必須引入外部變量。例如著名的隨機數網站random.org就是通過引入大氣噪聲這種無法預測的外部變量來實現真隨機數的。
srand()函數
既然隨機數是由種子經過既定的算法模擬的,因此我們只需要在每次運行程序前更新種子就能使每次運行程序得到不同的隨機數序列了。srand()函數就是用來設置rand()函數的種子的,根據不同的參數產生不同的種子。因此只需要每次運行程序時給srand()函數傳入不同的參數就行了,這個參數我們一般使用時間戳(timestamp),只要不在一秒內運行兩次程序,得到的隨機數序列就是不同的。srand()函數只需要在程序里設置一次就可以了。
time(NULL);函數會返回1970年1月1日至今所經歷的時間(以秒為單位),需要引入頭文件<time.h>。
#include <iostream> #include <time.h> using namespace std; int main( ){ srand(time(NULL)); for(int i=0;i<5;i++){ cout<<rand()<<endl; } }
雖然連續運行程序,得到的序列很接近,但經過我們的四則和取余運算后,確實是能滿足絕大部分的需求了。
一些常見用法
其實這個屬於數學運算了:
1、如果你要產生0~99這100個整數中的一個隨機整數,可以表達為:int num = rand() % 100;
這樣,num的值就是一個0~99中的一個隨機數了。
2、如果要產生1~100,則是這樣:int num = rand() % 100 + 1;
3、總結來說,可以表示為:int num = rand() % n +a;
其中的a是起始值,n-1+a是終止值,n是整數的范圍。
4、一般性:rand() % (b-a+1)+ a ; 就表示 a~b 之間的一個隨機整數。
5、若要產生0~1之間的小數,則可以先取得0~10的整數,然后均除以10即可得到“隨機到十分位”的10個隨機小數。
若要得到“隨機到百分位”的隨機小數,則需要先得到0~100的10個整數,然后均除以100,其它情況依 此類推。
隨機數庫(Random Number Library)
從上面所述,rand()函數使用笨拙且原始。C++既然引入了面向對象的特性,自然也要對隨機數做封裝,於是在C++11中,隨機數庫誕生了。
主要用到的是兩個類:隨機數引擎類(random-number engines)、隨機數分布類(random-number distribution)。
#include <iostream> #include <ctime> #include <random> using namespace std; int main( ){ //設置隨機數引擎 default_random_engine e; /** * 為引擎設置隨機數種子 * 也可以寫default_random_engine e(time(0)) */ e.seed(time(0)); //生成0到9之間均勻分布的隨機無符號整數 uniform_int_distribution<unsigned > u(0,9); for(int i=0;i<9;i++) { /** * u作為隨機數源 * e生成的隨機數是在u定義的范圍內並服從均勻分布的 */ cout << u(e) << endl; } }
從代碼上看,似乎和rand()也沒多少區別,甚至變得更復雜了。其實隨機數庫厲害的地方不在引擎,而在分布。除了生成上面的均勻分布,C++ 11 還規定了可以生成 20 種不同的分布類型,比如 均勻分布uniform,正態分布normal,二項分布binomial,泊松分布poisson,學生分布 student 等等,相關函數可以查看相應的文檔。