隨機數計演算法比較,更好的隨機數對於程式是否真的值得。
隨機數計演算法比較,更好的隨機數對於程式是否真的值得。
本次,我們將評測四種隨機數生成法
測試語言為C++
測試有不嚴謹的地方歡迎提出。
本文僅僅發佈於部落格園
下面是他們時間表現
名稱 | 生成\(1\times 10^9\)個隨機數耗時(ms) |
---|---|
庫函數rand耗時 | 8634 |
mt19937 | 8176 |
xorshift32耗時 | 6724 |
modrand耗時 | 6116 |
演算法介紹
庫函數rand
這個不用多說,cstdlib里的庫函數。
用的就是LCG(linear congruential generator)演算法
基於\(X(n+1) = (a * X(n) + c) \mod m\)這樣的公式
下面的modrand就是手寫實現的這種演算法。
mt19937
也就是Mersenne Twister MT19937(馬特賽特旋轉演演算法)的實現。
基於有限二進位欄位上的矩陣線性再生。可以快速產生高品質的偽隨機數,修正了古老隨機數產生演算法的很多缺陷。
轉載自百度百科:
Mersenne Twister演算法的原理:Mersenne Twister演算法是利用線性回饋移位暫存器(LFSR)產生隨機數的,LFSR的回饋函數是暫存器中某些位的簡單異或,這些位也稱之為抽頭序列。一個\(n\)位的LFSR能夠在重複之前產生\(2^n-1\)位長的偽隨機序列。只有具有一定抽頭序列的LFSR才能通過所有\(2^n-1\)個內部狀態,產生\(2^n – 1\)位長的偽隨機序列,這個輸出的序列就稱之為m序列。為了使LFSR成為最大周期的LFSR,由抽頭序列加上常數1形成的多項式必須是本原多項式。一個n階本原多項式是不可約多項式,它能整除\(x^(2*n-1)+1\)而不能整除\(x^d+1\),其中d能整除\(2^n-1\)。例如\((32,7,5,3,2,1,0)\)是指本原多項式\(x^32+x^7+x^5+x^3+x^2+x+1\),把它轉化為最大周期LFSR就是在LFSR的第\(32,7,5,2,1\)位抽頭。利用上述兩種方法產生周期為\(m\)的偽隨機序列後,只需要將產生的偽隨機序列除以序列的周期,就可以得到\((0,1)\)上均勻分布的偽隨機序列了。
Mersenne Twister有以下優點:隨機性好,在電腦上容易實現,佔用記憶體較少(mt19937的C程式碼執行僅需\(624\)個字的工作區域),與其它已使用的偽隨機數發生器相比,產生隨機數的速度快、周期長,可達到2^19937-1,且具有623維均勻分布的性質,對於一般的應用來說,足夠大了,序列關聯比較小,能通過很多隨機性測試。
xorshift32
int xorshift32()
{
static int x(13147);
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
return x;
}
是一個比較複雜的數論問題,作者也不會。
modrand
看程式碼就知道是什麼原理了
unsigned int modrand()
{
static int seed(13147);
seed = (seed * 31 + 13) % ((1 << 31) - 1);
return seed;
}
實際表現
上面的演算法,有的實現快,但是生成的品質不一定高,有的品質高,生成就更慢。
所以實際表現到底怎麼樣呢?
我將對下面的項目進行測試
序號 | 項目名 |
---|---|
1 | FHQ_Treap實現普通平衡樹(數據加強版) |
上面兩個應該是隨機函數比較常見的應用了。
結果
FHQ_Treap(Rand生成速度將影響總時間,而隨機數的品質將影響樹高,也會影響總時間)
名稱 | 耗時(s) |
---|---|
庫函數rand耗時 | 8.33s |
mt19937 | 8.72s |
xorshift32耗時 | 8.34s |
modrand耗時 | 8.72s |
可以發現rand本身就夠用了,除非有特殊要求沒必要換用其他的函數。更沒必要自己手寫。