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

來源:互聯網
上載者:User

標籤:normal   功能   provider   選擇   https   相同   毫秒級   get   問題   

from:https://www.xcode.me/more/net-csharp-generate-random

 

 

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

for (int i = 0; i < 5; i++) {     Random random = new Random();     Console.WriteLine(random.Next()); }

這段代碼執行後的結果如下所示:

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++) {     Console.WriteLine(random.Next()); }

執行後的結果如下所示:

391098894 1791722821 1488616582 1970032058 201874423

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

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

for (int i = 0; i < 5; i++) {     byte[] buffer = Guid.NewGuid().ToByteArray();     int iSeed = BitConverter.ToInt32(buffer, 0);     Random random = new Random(iSeed);     Console.WriteLine(random.Next()); }

這樣的方式保證了填滿因數的隨機性,所以產生的隨機數也比較可靠,運行結果如下所示:

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#中產生的隨機數為什麼不隨機?

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.