C 中生成的隨機數為什麼不隨機?

2021-09-07 16:02:22 字數 2565 閱讀 3848

from:

隨機數生成方法可以說是任何程式語言必備的功能,它的重要性不言而言,在c#中我們通常使用random類生成隨機數,在一些場景下,我卻發現random生成的隨機數並不可靠,在下面的例子中我們通過迴圈隨機生成5個隨機數:

for (int i = 0; i < 5; i++)
這段**執行後的結果如下所示:

2140400647 2140400647 2140400647 2140400647 2140400647
通過以上結果可知,隨機數類生成了5個相同的數,這並非我們的預期,為什麼呢?為了弄清楚這個問題,零度剖析了微軟官方的開源random類,發現在c#中生成隨機數使用的演算法是線性同餘法,經百科而知,這種演算法生成的不是絕對隨機,而是一種偽隨機數,線性同餘法演算法的的公式是:

第n+1個數 = ( 第n個數 * a + b) % m
上面的公式中a、b和m分別為常數,是生成隨機數的因子,如果之前從未通過同乙個random物件生成過隨機數(也就是呼叫過next方法),那麼第n個隨機數為將被指定為乙個預設的常數,這個常數在建立乙個random類時被預設值指定,random也提供乙個建構函式允許開發者使用自己的隨機數因子,這一切可通過微軟官方開源**看到:

public random() : this(environment.tickcount)   public random(int seed)
通過預設建構函式建立random類時,乙個environment.tickcount物件作為因子被預設傳遞給第二個建構函式,environment.tickcount表示作業系統啟動後經過的毫秒數,計算機的運算運算速度遠比毫秒要快得多,這導致乙個的具有毫秒精度的因子參與隨機數的生成過程,但在5次迴圈中,我們使用了同乙個毫秒級的因子,從而生成相同的隨機數,另外,第n+1個數的生成與第n個數有著直接的關係。

在上面的例子中,假設系統啟動以來的毫秒數為888毫秒,執行5次迴圈用時只有0.1毫秒,這導致在迴圈中建立的5個random物件都使用了相同的888因子,每次被建立的隨機物件又使用了相同的第n個數(預設為常數),通過這樣的假設我們不難看出,上面的結果是必然的。

現在我們改變這個格局,在迴圈之外建立乙個random物件,在每次迴圈中引用它,並通過它生成隨機數,並在同乙個物件上多次呼叫next方法,從而不斷變化第n個數,**如下所示:

random random = new 

random(); for (int i = 0; i < 5; i++)

執行後的結果如下所示:

391098894 1791722821 1488616582 1970032058 201874423
我們看到這個結果確實證實了我們上面的推斷,第1次迴圈時公式中的第n個數為預設常數;當第二次迴圈時,第n個數為391098894,隨後不斷變化的第n個數作為因子參與計算,這保證了結果的隨機性。

雖然通過我們的隨機數看起來也很隨機了,但必定這個演算法是偽隨機數,當第n個數和因子都相同時,生成的隨機數仍然是重複的隨機數,由於random提供乙個帶參的建構函式允許我們傳入乙個因子,如果傳入的因子隨機性強的話,那麼生成的隨機數也會比較可靠,為了提供乙個可靠點的因子,我們通常使用guid產生填充因子,同樣放在迴圈中測試:

for (int i = 0; i < 5; i++)
這樣的方式保證了填充因子的隨機性,所以生成的隨機數也比較可靠,執行結果如下所示:

734397360 1712793171 1984332878 819811856 1015979983
在一些場景下這樣的隨機數並不可靠,為了生成更加可靠的隨機數,微軟在system.security.cryptography命名空間下提供乙個名為rngcryptoserviceprovider的類,它採用系統當前的硬體資訊、程序資訊、執行緒資訊、系統啟動時間和當前精確時間作為填充因子,通過更好的演算法生成高質量的隨機數,它的使用方法如下所示:

byte randombytes = new byte[4]; rngcryptoserviceprovider rngserviceprovider = new 

rngcryptoserviceprovider(); rngserviceprovider.getbytes(randombytes); int32 result = bitconverter.toint32(randombytes, 0);

通過這種演算法生成的隨機數,經過成千上萬次的測試,並未發現重複,質量的確比random高了很多。另外windows api也提供了乙個非託管的隨機數生成函式cryptgenrandom,cryptgenrandom與rngcryptoserviceprovider的原理類似,採用c++編寫,如果要在.net中使用,需要進行簡單的封裝。它的原型如下所示:

bool winapi cryptgenrandom(   _in_     hcryptprov hprov,   _in_     dword dwlen,   _inout_  byte *pbbuffer );
以上就是零度為您帶來的隨機數生成方法和基本原理,您可以通過需求和場景選擇最佳的方式,random演算法簡單,效能較高,適用於隨機性要求不高的情況,由於rngcryptoserviceprovider在生成期間需要查詢上面提到的幾種系統因子,所以效能稍弱於random類,但隨機數質量高,可靠性更好。

C 中生成的隨機數為什麼不隨機?

隨機數生成方法可以說是任何程式語言必備的功能,它的重要性不言而言,在c 中我們通常使用random類生成隨機數,在一些場景下,我卻發現random生成的隨機數並不可靠,在下面的例子中我們通過迴圈隨機生成5個隨機數 for int i 0 i 5 i 這段 執行後的結果如下所示 2140400647 ...

ios 中生成隨機數

ios 有如下三種隨機數方法 1.srand unsigned time 0 不加這句每次產生的隨機數不變 int i rand 5 2.srandom time 0 int i random 5 3.int i arc4random 5 注 rand 和random 實際並不是乙個真正的偽隨機數發...

ios 中生成隨機數

ios 有如下三種隨機數方法 1.srand unsigned time 0 不加這句每次產生的隨機數不變 int i rand 5 2.srandom time 0 int i random 5 3.int i arc4random 5 注 rand 和random 實際並不是乙個真正的偽隨機數發...