.Net中我們通常使用Random類生成隨機數,在一些場景下,我卻發現Random生成的隨機數並不可靠,在下面的例子中我們通過循環隨機生成10個隨機數:
for (int i = 0; i < 10; i++) { Random random1 = new Random(); Console.WriteLine(random1.Next()); }
測試生成隨時基本都是相同的結果:
很顯然上面的結果是不靠譜的,為什麼會這樣呢,因為微軟的Random類,發現在C#中生成隨機數使用的算法是線性同余法,這種算法生成的不是絕對隨機,而是一種偽隨機數,線性同余法算法的的公式是
:第n+1個數 = ( 第N個數 * a + b) % m ,公式中a、b和m分別為常數,是生成隨機數的因子,如果之前從未通過同一個Random對象生成過隨機數(也就是調用過Next方法),那麼第N個隨機數為將被指定為一個默認的常數,這個常數在創建一個Random類時被默認值指定,Random也提供一個構造函數允許開發者使用自己的隨機數因子.
有人說要將 Random random1 = new Random(); 要放到循環的外面:
Random random2 = new Random(); for (int i = 0; i < 10; i++) { Console.WriteLine(random2.Next()); }
測試上面的代碼執行的結果是這樣的:
得到結果還是不靠譜的
有人說使用GUID產生填充因子:
for (int i = 0; i < 10; i++) { byte[] buffer = Guid.NewGuid().ToByteArray(); int iSeed = BitConverter.ToInt32(buffer, 0); Random random3 = new Random(iSeed); Console.WriteLine(random3.Next()); }
測試上面的代碼得到的結果:
得到的結果還是不靠譜的。
為了生成更加可靠的隨機數,微軟在System.Security.Cryptography命名空間下提供一個名為system.Security.Cryptography.RNGCryptoServiceProvider的類,它采用系統當前的硬件信息、進程信息、線程信息、系統啟動時間和當前精確時間作為填充因子,通過更好的算法生成高質量的隨機數,它的使用方法如下所示:
for (int i = 0; i < 20; i++) { byte[] randomBytes = new byte[8]; System.Security.Cryptography.RNGCryptoServiceProvider rngServiceProvider = new System.Security.Cryptography.RNGCryptoServiceProvider(); rngServiceProvider.GetBytes(randomBytes); int result = BitConverter.ToInt32(randomBytes, 0); result = System.Math.Abs(result); //求絕對值 Console.WriteLine(result); }
測試結果未發現重復的:
總結:
Random算法簡單,性能較高,適用於隨機性要求不高的情況,由於RNGCryptoServiceProvider在生成期間需要查詢上面提到的幾種系統因子,所以性能稍弱於Random類,但隨機數質量高,可靠性更好。使用哪一種方式視情況而定