1. 程式人生 > >C++中的srand(time(null))利用時間設定隨機種子產生隨機數

C++中的srand(time(null))利用時間設定隨機種子產生隨機數

   首先需要宣告的是,計算機不會產生絕對隨機的隨機數,計算機只能產生“偽隨機數”。其實絕對隨機的隨機數只是一種理想的隨機數,即使計算機怎樣發展,它也不會產生一串絕對隨機的隨機數。計算機只能生成相對的隨機數,即偽隨機數。

   偽隨機數並不是假隨機數,這裡的“偽”是有規律的意思,就是計算機產生的偽隨機數既是隨機的又是有規律的。怎樣理解呢?產生的偽隨機數有時遵守一定的規律,有時不遵守任何規律;偽隨機數有一部分遵守一定的規律;另一部分不遵守任何規律。比如“世上沒有兩片形狀完全相同的樹葉”,這正是點到了事物的特性,即隨機性,但是每種樹的葉子都有近似的形狀,這正是事物的共性,即規律性。從這個角度講,你大概就會接受這樣的事實了:計算機只能產生偽隨機數而不能產生絕對隨機的隨機數。

   那麼計算機中隨機數是怎樣產生的呢?有人可能會說,隨機數是由“隨機種子”產生的。沒錯,隨機種子是用來產生隨機數的一個數,在計算機中,這樣的一個“隨機種子”是一個無符號整形數。那麼隨機種子是從哪裡獲得的呢?

   下面看這樣一個C程式:

//rand01.c #include <dos.h> #include <stdio.h> #include <mem.h> static unsigned int RAND_SEED;

unsigned int random(void) {  RAND_SEED=(RAND_SEED*123+59)%65536;  return(RAND_SEED); }

void random_start(void) {  int temp[2];  movedata(0x0040,0x006c,FP_SEG(temp),FP_OFF(temp),4);  RAND_SEED=temp[0]; }

main() {  unsigned int i,n;  random_start();  for(i=0;i<10;i++)   printf("%u/t",random());  printf("/n"); }

這個程式(rand01.c)完整地闡述了隨機數產生的過程:
首先,主程式呼叫random_start()方法,random_start()方法中的這一句我很感興趣:

movedata(0x0040,0x006c,FP_SEG(temp),FP_OFF(temp),4);

這個函式用來移動記憶體資料,其中FP_SEG(far pointer to segment)是取temp陣列段地址的函式,FP_OFF(far pointer to offset)是取temp陣列相對地址的函式,movedata函式的作用是把位於0040:006CH儲存單元中的雙字放到陣列temp的宣告的兩個儲存單元中。這樣可以通過temp陣列把0040:006CH處的一個16位的數送給RAND_SEED。

random用來根據隨機種子RAND_SEED的值計算得出隨機數,其中這一句:

RAND_SEED=(RAND_SEED*123+59)%65536;

是用來計算隨機數的方法,隨機數的計算方法在不同的計算機中是不同的,即使在相同的計算機中安裝的不同的作業系統中也是不同的。我在linux和windows下分別試過,相同的隨機種子在這兩種作業系統中生成的隨機數是不同的,這說明它們的計算方法不同。

現在,我們明白隨機種子是從哪兒獲得的,而且知道隨機數是怎樣通過隨機種子計算出來的了。那麼,隨機種子為什麼要在記憶體的0040:006CH處取?0040:006CH處存放的是什麼?

學過《計算機組成原理與介面技術》這門課的人可能會記得在編制ROM BIOS時鐘中斷服務程式時會用到Intel 8253定時/計數器,它與Intel 8259中斷晶片的通訊使得中斷服務程式得以運轉,主機板每秒產生的18.2次中斷正是處理器根據定時/記數器值控制中斷晶片產生的。在我們計算機的主機板上都會有這樣一個定時/記數器用來計算當前系統時間,每過一個時鐘訊號週期都會使記數器加一,而這個記數器的值存放在哪兒呢?沒錯,就在記憶體的0040:006CH處,其實這一段記憶體空間是這樣定義的:

TIMER_LOW DW ? ;地址為 0040:006CH
TIMER_HIGH DW ? ;地址為 0040:006EH
TIMER_OFT DB ? ;地址為 0040:0070H

時鐘中斷服務程式中,每當TIMER_LOW轉滿時,此時,記數器也會轉滿,記數器的值歸零,即TIMER_LOW處的16位二進位制歸零,而TIMER_HIGH加一。rand01.c中的

movedata(0x0040,0x006c,FP_SEG(temp),FP_OFF(temp),4);

正是把TIMER_LOW和TIMER_HIGH兩個16位二進位制數放進temp陣列,再送往RAND_SEED,從而獲得了“隨機種子”。

現在,可以確定的一點是,隨機種子來自系統時鐘,確切地說,是來自計算機主機板上的定時/計數器在記憶體中的記數值。這樣,我們總結一下前面的分析,並討論一下這些結論在程式中的應用:

1.隨機數是由隨機種子根據一定的計算方法計算出來的數值。所以,只要計算方法一定,隨機種子一定,那麼產生的隨機數就不會變。

看下面這個C++程式:

//rand02.cpp 
#include 
#include 
using namespace std; 

int main() 
{ 
unsigned int seed=5; 
srand(seed); 
unsigned int r=rand(); 
cout< // 編輯者注:可能程式碼有缺 
} 

在相同的平臺環境下,編譯生成exe後,每次執行它,顯示的隨機數都是一樣的。這是因為在相同的編譯平臺環境下,由隨機種子生成隨機數的計算方法都是一樣的,再加上隨機種子一樣,所以產生的隨機數就是一樣的。

2.只要使用者或第三方不設定隨機種子,那麼在預設情況下隨機種子來自系統時鐘(即定時/計數器的值)

看下面這個C++程式:

//rand03.cpp 
#include 
#include 
using namespace std; 

int main() 
{ 
srand((unsigned)time(NULL)); 
unsigned int r=rand(); 
cout< return 0; 
} 

這裡使用者和其他程式沒有設定隨機種子,則使用系統定時/計數器的值做為隨機種子,所以,在相同的平臺環境下,編譯生成exe後,每次執行它,顯示的隨機數會是偽隨機數,即每次執行顯示的結果會有不同。

3.建議:如果想在一個程式中生成隨機數序列,需要至多在生成隨機數之前設定一次隨機種子。

看下面這個用來生成一個隨機字串的C++程式:

//rand04.cpp 
#include 
#include 
using namespace std; 
int main() 
{ 
int rNum,m=20; 
char *ch=new char ; 

for ( int i = 0; i //大家看到了,隨機種子會隨著for迴圈在程式中設定多次 
srand((unsigned)time(NULL)); 
rNum=1+(int)((rand()/(double)RAND_MAX)*36); //求隨機值 
switch (rNum){ 
case 1: ch='a'; 
break ; 
case 2: ch='b'; 
break ; 
case 3: ch='c'; 
break ; 
case 4: ch='d'; 
break ; 
case 5: ch='e'; 
break ; 
case 6: ch='f'; 
break ; 
case 7: ch='g'; 
break ; 
case 8: ch='h'; 
break ; 
case 9: ch='i'; 
break ; 
case 10: ch='j'; 
break ; 
case 11: ch='k'; 
break ; 
case 12: ch='l'; 
break ; 
case 13: ch='m'; 
break ; 
case 14: ch='n'; 
break ; 
case 15: ch='o'; 
break ; 
case 16: ch='p'; 
break ; 
case 17: ch='q'; 
break ; 
case 18: ch='r'; 
break ; 
case 19: ch='s'; 
break ; 
case 20: ch='t'; 
break ; 
case 21: ch='u'; 
break ; 
case 22: ch='v'; 
break ; 
case 23: ch='w'; 
break ; 
case 24: ch='x'; 
break ; 
case 25: ch='y'; 
break ; 
case 26: ch='z'; 
break ; 
case 27:ch='0'; 
break; 
case 28:ch='1'; 
break; 
case 29:ch='2'; 
break; 
case 30:ch='3'; 
break; 
case 31:ch='4'; 
break; 
case 32:ch='5'; 
break; 
case 33:ch='6'; 
break; 
case 34:ch='7'; 
break; 
case 35:ch='8'; 
break; 
case 36:ch='9'; 
break; 
}//end of switch 
cout< }//end of for loop 

cout< return 0; 
} 


而執行結果顯示的隨機字串的每一個字元都是一樣的,也就是說生成的字元序列不隨機,所以我們需要把srand((unsigned)time(NULL)); 從for迴圈中移出放在for語句前面,這樣可以生成隨機的字元序列,而且每次執行生成的字元序列會不同(呵呵,也有可能相同,不過出現這種情況的機率太小了)。
如果你把srand((unsigned)time(NULL));改成srand(2);這樣雖然在一次執行中產生的字元序列是隨機的,但是每次執行時產生的隨機字元序列串是相同的。把srand這一句從程式中去掉也是這樣。

此外,你可能會遇到這種情況,在使用timer控制元件編制程式的時候會發現用相同的時間間隔生成的一組隨機數會顯得有規律,而由使用者按鍵command事件產生的一組隨機數卻顯得比較隨機,為什麼?根據我們上面的分析,你可以很快想出答案。這是因為timer是由計算機時鐘記數器精確控制時間間隔的控制元件,時間間隔相同,記數器前後的值之差相同,這樣時鐘取值就是呈線性規律的,所以隨機種子是呈線性規律的,生成的隨機數也是有規律的。而使用者按鍵事件產生隨機數確實更呈現隨機性,因為事件是由人按鍵引起的,而人不能保證嚴格的按鍵時間間隔,即使嚴格地去做,也不可能完全精確做到,只要時間間隔相差一微秒,記數器前後的值之差就不相同了,隨機種子的變化就失去了線性規律,那麼生成的隨機數就更沒有規律了,所以這樣生成的一組隨機數更隨機。這讓我想到了各種晚會的抽獎程式,如果用人來按鍵產生幸運觀眾的話,那就會很好的實現隨機性原則,結果就會更公正。

最後,我總結兩個要點:
1.計算機的偽隨機數是由隨機種子根據一定的計算方法計算出來的數值。所以,只要計算方法一定,隨機種子一定,那麼產生的隨機數就是固定的。
2.只要使用者或第三方不設定隨機種子,那麼在預設情況下隨機種子來自系統時鐘。

==============================================================================================================================

一、C++中不能使用random()函式

本文由青松原創並依GPL-V2及其後續版本發放,轉載請註明出處且應包含本行宣告。

C++中常用rand()函式生成隨機數,但嚴格意義上來講生成的只是偽隨機數(pseudo-random integral number)。生成隨機數時需要我們指定一個種子,如果在程式內迴圈,那麼下一次生成隨機數時呼叫上一次的結果作為種子。但如果分兩次執行程式,那麼由於種子相同,生成的“隨機數”也是相同的。

在工程應用時,我們一般將系統當前時間(Unix時間)作為種子,這樣生成的隨機數更接近於實際意義上的隨機數。給一下例程如下:

#include <iostream>
#include <ctime>
#include <cstdlib>
using namespace std;
 
int main()
{
    double random(double,double);
    srand(unsigned(time(0)));
    for(int icnt = 0; icnt != 10; ++icnt)
        cout << "No." << icnt+1 << ": " << int(random(0,10))<< endl;
    return 0;
}
 
double random(double start, double end)
{
    return start+(end-start)*rand()/(RAND_MAX + 1.0);
}
/* 執行結果
* No.1: 3
* No.2: 9
* No.3: 0
* No.4: 9
* No.5: 5
* No.6: 6
* No.7: 9
* No.8: 2
* No.9: 9
* No.10: 6
*/


利用這種方法能不能得到完全意義上的隨機數呢?似乎9有點多哦?卻沒有1,4,7?!我們來做一個概率實驗,生成1000萬個隨機數,看0-9這10個數出現的頻率是不是大致相同的。程式如下:

#include <iostream>
#include <ctime>
#include <cstdlib>
#include <iomanip>
using namespace std;
 
int main()
{
    double random(double,double);
    int a[10] = {0};
    const int Gen_max = 10000000;
    srand(unsigned(time(0)));
    
    for(int icnt = 0; icnt != Gen_max; ++icnt)
        switch(int(random(0,10)))
        {
        case 0: a[0]++; break;
        case 1: a[1]++; break;
        case 2: a[2]++; break;
        case 3: a[3]++; break;
        case 4: a[4]++; break;
        case 5: a[5]++; break;
        case 6: a[6]++; break;
        case 7: a[7]++; break;
        case 8: a[8]++; break;
        case 9: a[9]++; break;
        default: cerr << "Error!" << endl; exit(-1);
        }
    
    for(int icnt = 0; icnt != 10; ++icnt)
        cout << icnt << ": " << setw(6) << setiosflags(ios::fixed) << setprecision(2) << double(a[icnt])/Gen_max*100 << "%" << endl;
    
    return 0;
}
 
double random(double start, double end)
{
    return start+(end-start)*rand()/(RAND_MAX + 1.0);
}
/* 執行結果
* 0: 10.01%
* 1:   9.99%
* 2:   9.99%
* 3:   9.99%
* 4:   9.98%
* 5: 10.01%
* 6: 10.02%
* 7: 10.01%
* 8: 10.01%
* 9:   9.99%
*/


可知用這種方法得到的隨機數是滿足統計規律的。

另:在Linux下利用GCC編譯程式,即使我執行了1000000次運算,是否將random函式定義了inline函式似乎對程式沒有任何影響,有理由相信,GCC已經為我們做了優化。但是冥冥之中我又記得要做inline優化得加O3才行...

不行,於是我們把迴圈次數改為10億次,用time命令檢視執行時間:
[email protected] ~/workspace/test/Debug $ time ./test
0: 10.00%
1: 10.00%
2: 10.00%
3: 10.00%
4: 10.00%
5: 10.00%
6: 10.00%
7: 10.00%
8: 10.00%
9: 10.00%

real 2m7.768s
user 2m4.405s
sys 0m0.038s
[email protected] ~/workspace/test/Debug $ time ./test
0: 10.00%
1: 10.00%
2: 10.00%
3: 10.00%
4: 10.00%
5: 10.00%
6: 10.00%
7: 10.00%
8: 10.00%
9: 10.00%

real 2m7.269s
user 2m4.077s
sys 0m0.025s

前一次為進行inline優化的情形,後一次為沒有作inline優化的情形,兩次結果相差不大,甚至各項指標後者還要好一些,不知是何緣由...

==================================================================================
   random函式不是ANSI C標準,不能在gcc,vc等編譯器下編譯通過。可改用C++下的rand函式來實現。

   1、C++標準函式庫提供一隨機數生成器rand,返回0-RAND_MAX之間均勻分佈的偽隨機整數。 RAND_MAX必須至少為32767。rand()函式不接受引數,預設以1為種子(即起始值)。 隨機數生成器總是以相同的種子開始,所以形成的偽隨機數列也相同,失去了隨機意義。(但這樣便於程式除錯)
   2、C++中另一函式srand(),可以指定不同的數(無符號整數變元)為種子。但是如果種子相同,偽隨機數列也相同。一個辦法是讓使用者輸入種子,但是仍然不理想。
   3、 比較理想的是用變化的數,比如時間來作為隨機數生成器的種子。 time的值每時每刻都不同。所以種子不同,所以,產生的隨機數也不同。

// C++隨機函式(VC program) 
#include <stdio.h> 
#include <iostream> 
#include <time.h> 
using namespace std; 
#define MAX 100 
int main(int argc, char* argv[]) 
{    srand( (unsigned)time(NULL));//srand()函式產生一個以當前時間開始的隨機種子.應該放在for等迴圈語句前面 不然要很長時間等待 
   for (int i=0;i<10;i++) 
   cout<<rand()%MAX<<endl;//MAX為最大值,其隨機域為0~MAX-1
   return 0; 
} 


二、rand()的用法
   rand()不需要引數,它會返回一個從0到最大隨機數的任意整數,最大隨機數的大小通常是固定的一個大整數。 這樣,如果你要產生0~10的10個整數,可以表達為:
int N = rand() % 11;  這樣,N的值就是一個0~10的隨機數,如果要產生1~10,則是這樣:int N = 1 + rand() % 10; 總結來說,可以表示為:a + rand() % n,其中的a是起始值,n是整數的範圍。   

   a + rand() % (b-a+1) 就表示a~b之間的一個隨機數。若要0~1的小數,則可以先取得0~10的整數,然後均除以10即可得到隨機到十分位的10個隨機小數,若要得到隨機到百分位的隨機小數,則需要先得到0~100的10個整數,然後均除以100,其它情況依此類推。
   通常rand()產生的隨機數在每次執行的時候都是與上一次相同的,這是有意這樣設計的,是為了便於程式的除錯。若要產生每次不同的隨機數,可以使用srand( seed )函式進行隨機化,隨著seed的不同,就能夠產生不同的隨機數。
   如大家所說,還可以包含time.h標頭檔案,然後使用srand(time(0))來使用當前時間使隨機數發生器隨機化,這樣就可以保證每兩次執行時可以得到不同的隨機數序列(只要兩次執行的間隔超過1秒)。

相關推薦

C++srand(time(null))利用時間設定隨機種子產生隨機數

   首先需要宣告的是,計算機不會產生絕對隨機的隨機數,計算機只能產生“偽隨機數”。其實絕對隨機的隨機數只是一種理想的隨機數,即使計算機怎樣發展,它也不會產生一串絕對隨機的隨機數。計算機只能生成相對的隨機數,即偽隨機數。    偽隨機數並不是假隨機數,這裡的“偽”是有規律的

android上執行C++程式碼time(NULL)獲取當前時間戳,為什麼得到的結果不對

如題,用NDK執行C++程式碼中time(NULL)函式,得到的結果不對,這是為什麼呀?? time_t current_time = time(NULL)*1000; CLog::Log(LOGDEBUG, "%ll

c語言中 srand(time(NULL)); 這句話是什麽意思(尤其是 NULL

什麽 返回值 產生 con 現在時間 完成 時間 一個 div 這是兩個函數! srand函數 一個是srand函數!這是在調用rand()這個函數之前使用的!rand()是一個產生隨機數的函數!而srand是一個設置隨機數種子的函數!通常這兩個函數是一起使用的!來完成

Linux C 獲取local日期和時間 time()&localtime()函數

運行 clas fine stdlib.h erro 分享圖片 mda else str 1. time() 函數 /* time - 獲取計算機系統當前的日歷時間(Calender Time) * 處理日期時間的函數都是以本函數的返回值為基礎進行運

c/c++ linux epoll系列3 利用epoll_wait設定timeout時間長度

linux epoll系列3 利用epoll_wait設定timeout時間長度 epoll_wait函式的第四個引數可以設定,epoll_wait函式的等待時間(timeout時間長度)。 例子1,是接收端。 例子2,是傳送端。 例子1,接收端 #include <stdio.h>

Linux C 獲取local日期和時間 time()&localtime()函式

1.  time() 函式 /* time - 獲取計算機系統當前的日曆時間(Calender Time) * 處理日期時間的函式都是以本函式的返回值為基礎進行運算 * 函式原型: * #include <time.h>

C++學習記錄6--srand(time(NULL)產生隨機數

time() 函式:返回從1970/1/1 00:00:00到呼叫time()函式時所經過的時間,以秒為單位,所以是個整數。time(NULL)或time(0)表示在記憶體中不儲存返回的數值。標頭檔案:#include<time.h> rand()

程序員如何在百忙更有效地利用時間,如何不走岔路,不白忙(忙得要有效率,要有收獲)-----https://www.cnblogs.com/JavaArchitect/p/9080484.html

logs 有效 時間 hit 收獲 AR log title blog https://www.cnblogs.com/JavaArchitect/p/9080484.html 程序員如何在百忙中更有效地利用時間,如何不走岔路,不白忙(忙得要有效率,要有收獲) 程序員如何

C#,我們可以利用以下方法判斷Windows服務是否存在

  在C#中,我們可以利用以下方法判斷Windows服務是否存在          private bool IsServiceExisted(string se

C++計算程式的執行時間

在我們實際開發中,尤其對於演算法工程師來說,有時候為了比較不同的演算法或者優化演算法,需要計算各個演算法執行的時間或者關鍵程式碼段的執行時間,以此來衡量演算法在速度上的優劣或者進行程式碼優化時的一個參考。 因此對於程式設計師來說,如何計算程式的執行時間就是一個需

C++獲得CPU高精度時間戳( 納秒級 )

對關注效能的程式開發人員而言,一個好的計時部件既是益友,也是良師。計時器既可以作為程式元件幫助程式設計師精確的控制程式程序,又是一件有力的除錯武器,在有經驗的程式設計師手裡可以儘快的確定程式的效能瓶頸,或者對不同的演算法作出有說服力的效能比較。  在Windows平臺下,常用

C#的?和??,null和Nullable

不能 ron repl 編譯器 提示 data- 外行 date 如果 1.單問號(?) 1.1 單問號運算符可以表示:可為Null類型,C#2.0裏面實現了Nullable數據類型 //A.比如下面一句,直接定義int為null是錯誤的,錯誤提示為無法將null轉

C++—rand和srand的用法(簡單易懂版)—產生隨機數

每天進步一點點,目標距離縮小點在C++中,可以使用rand()函式產生隨機數。(rand()函式的標頭檔案在<cstdlib>中)如果想產生在一定範圍內的數,可以用取餘的方法獲得。如想獲得0—100的數同樣的道理,如果想獲得100-200之間的數—————————

徹底搞清楚 C/C++ 日期和時間 time_t 與 struct tm,time(NULL),ctime;strftime

#include <stdio.h> #include <stdlib.h> #include <time.h> int main( void ) { long i = 10000000L;

c語言關於srand((unsigned)time(NULL))和rand的區別

一直以為rand()直接生成不同的隨機數,實驗結果不對才搜了下原因,轉載分享下... 可以認為rand()在每次被呼叫的時候,它會檢視: 1) 如果使用者在此之前呼叫過srand(seed),給seed指定了一個值,那麼它會自動呼叫 srand(seed)一次來初始

C++程式設計srand((unsigned int)(time(NULL)))這句程式碼的解讀

分享這個部落格連結,講的真的很好。 主要有以下3方面收穫: 1.srand跟rand的關係。 就是每次使用rand(),都會看之前呼叫了srand()函式沒有,沒有,預設呼叫了srand(1). 有呼叫,就是strang(seed),這裡的seed就是標

在LoadRunner設定HTTP請求time out的時間

If the error occurs during a scenario execution and happens in the middle of the scenario (usually with a large amount of load), the application is handlin

C++ time(NULL) 計算持續的時間長度

有時候在實際應用中要計算一個事件持續的時間長度,比如計算打字速度。在第1節計時部分中,我已經用clock函式舉了一個例子。Clock()函式可以精確到毫秒級。同時,我們也可以使用difftime()函式,但它只能精確到秒。該函式的定義如下: double difftime

C#設定session過期的時間

【**需進一步跟進**】 Asp.net 預設配置下,Session莫名丟失的原因及解決辦法 正常操作情況下Session會無故丟失。因為程式是在不停的被操作,排除Session超時的可能。另外,Session超時時間被設定成60分鐘,不會這麼快就超時的。這次到CSDN上搜

C/C++關於時間的函式 time()

1.當引數為NULL時(大多數情況下),返回值是從1970年1月1日至今所經歷的時間(以秒為單位),見下面的程式碼: #include <stdio.h> #include <time.h> #include <windows.h>