深度解讀如何生成大範圍随機數
前幾天在做畢業設計的時候需要用到随機數,發現随機數并不“随機”。Rand生成的都是僞随機數。
随機數介紹
先來看看rand的函數原型及用法
#include <stdlib.h> int rand( void ); |
功能: 函數傳回一個在零到RAND_MAX 之間的僞随機整數。
例如:
srand( time(NULL) );
for( i = 0; i < 10; i++ )
printf( "Random number#%d: %d\n", i, rand() );
RAND_MAX在不同的平台下定義的大小不一樣 windows 定義的為0X7FFF。
與rand()配套使用的還有另外一個函數 srand()。
#include <stdlib.h> void srand( unsigned seed ); |
功能: 設定rand()随機序列種子。對于給定的種子seed, rand()會反複産生特定的随機序列。
srand( time(NULL) );
for( i = 0; i < 10; i++ )
printf( "Random number#%d: %d\n", i, rand() );
解釋:
srand就是為rand産生一個初始值,若不手動調用srand系統也會自動調用srand(1),這樣每次産生的随機數就都一樣了。程式第一次執行“随機數”産生順序為5,8,2,5,0,1…,第二次執行仍然是5,8,2,5,0,1…,第三次執行仍然是5,8,2,5,0,1…若我們把時間做為種子的話每次生成的“随機數”就不一樣了。
在整個程式中srand()隻需要運作一次就夠了,沒有必要在每次運作的時候都要調用,若調用的時間間隔太近的話會造成設定的種子seed全部一樣導緻産生的随機數也都一樣。
基于以上幾個特性我們稱rand産生的随機數為“僞随機數”。
随機數數内部是如何實作的
rand的内部實作是用線性同餘法做的,他不是真的随機數,隻不過是因為其周期特别長,是以有一定的範圍裡可看成是随機的,rand()會傳回一随機數值,範圍在0至RAND_MAX間。
這些是數學上的内容我們不做讨論。
如何産生大範圍的随機數
rand()函數産生的随機數範圍比較小隻能是0x0000~0x7FFF之間的整數随機數即2^15以内的數,我們在實際的應用中經常需要用到大範圍的随機數例如會用到2^32 以内的随機數。顯然rand已經無法滿足我們的要求了。
這時候就要想辦法對随機數做擴充,C++标準庫提供了标準的擴充方法RAND_MAX*rand() + rand();這樣就在[0, RAND_MAX*RAND_MAX)做到了随機。這種方法有缺點就是容易産生數的“兩端聚集”現象。但是rand*rand和rand+rand的方法做不到随機數的擴充隻會使得出的随機數産生“二次聚集”現象。
有沒有一種更好的擴充方法呢?
有利用計算機的位偏移和位運算可以實作随機數的更好擴充。
unsigned long ulrand(void)
{
return (
(((unsignedlong)rand()<<24)&0xFF000000ul)
|(((unsignedlong)rand()<<12)&0x00FFF000ul)
|(((unsigned long)rand() )&0x00000FFFul));
}
關于位運算我就不做過多的解釋。需要注意的一點是rand傳回的随機數最高位始終為0,并不随機,是以不能參與随機數擴充的二進制運算。
負随機數
rand()-RAND_MAX/2這樣就産生了[-RAND_MAX,RAND_MAX]之間的随機數。負随機數的擴充同上。
随機小數
rand()/RAND_MAX 産生[0,1)之間的小數。
rand()/(RAND_MAX-1) 産生[0,1]之間的小數。
倍率* 和 +起始值,可以合成我們想要的随機數。
随機數終極利器Boost随機庫
rand函數産生的随機數随機性并不夠強,範圍不夠大。我們要找另外一種更加強大的随機數數生成函數。
Boost随機庫中利用了MersenneTwister算法,可以快速産生高品質的僞随機數Mersenne Twister有以下優點:随機性好,在計算機上容易實作,占用記憶體較少(mt19937的C程式碼執行僅需624個字的工作區域),與其它已使用的僞随機數發生器相比,産生随機數的速度快、周期長,可達到2^19937-1,且具有623維均勻分布的性質,對于一般的應用來說,足夠大了,序列關聯比較小,能通過很多随機性測試。
voidtest_mt19937()
{
// 以時間為種子建立一個随機數發生器
boost::mt19937 rng(time(0));
for (int i = 0; i < 100; ++i)
{
std::cout << rng() <<std::endl;
}
}
不做過多解釋,boost的強大有點反人類了!