當你在Stack Overflow網站標題中看到“隨機”這個詞你基本可以確定這是相同的基本問題無數的相似問題。本文帶你探討為什麼隨機性會引起這麼多問題並且如何解決它們。
Stack Overflow (or newsgroup, or mailing list etc) )網站的問題通常是這樣的:
我使用Random.Next產生隨機數,但它一直給我相同的號碼。 它不停的運行,但每次它會產生相同數量很多次。
這是由於這樣的代碼:
// Bad code! Do not use! for (int i = 0; i < 100; i++) { Console.WriteLine(GenerateDigit()); } .... static int GenerateDigit() { Random rng = new Random(); // Assume there'd be more logic here really return rng.Next(10); }
那麼,這程式到底出了什麼問題?
1.解讀
這種Random類不是真正的隨機數發生器,它是一個偽隨機數發生器。任何Random執行個體都有一定量的狀態,而當你調用Next( or NextDouble or NextBytes),它會使用該狀態來返回到似乎是隨機的資料,相應的改變它內部狀態以便於在下一步調用時你將得到另一個偽隨機數。
所有的這一切都是確定的,如果你開始一個Random的執行個體以相同的初始狀態(可通過種子來提供),並使用相同的序列方法調用它,那你會得到相同的結果。
那麼在我們的範例程式碼中到底出了什麼問題? 我們使用的一個新的Random執行個體也在迴圈迭代。隨機無參數的建構函式取當前日期和時間作為種子-在內部定時器工作之前你通常可以執行大量代碼,當前的日期和時間就會發生變化。 因此,我們重複使用相同的種子就會重複得到相同的結果。
2.對此我們能做什嗎?
這個問題有很多的解決方案, 其中有些方法是比其他的更好。 讓我們先挑出其中一種方法,因為它不同於其他的方法。
3.使用加密的隨機數發生器
.NET有一個RandomNumberGenerator類應該是所有加密隨機數產生器派生而來的抽象類別。 這個架構本身附帶了一個這樣的衍生類別: RNGCryptoServiceProvider 。 加密隨機數發生器的理念是,即使它可能仍然是一個偽隨機產生器,它還是很難做到不可預料。 內建的實現需要多個熵源在你的電腦有效地呈現“噪音”,並難以預測。它可以使用這種噪音不僅僅是計算一個種子,也可以在產生下一個數字時讓你知道當前的狀態,這也許可能不足以預測下一個結果(或者那些已經產生),這主要取決於具體的實施。Windows也可以利用專業硬體資源的隨機性(如一塊硬體觀察放射性同位素衰變),從而使得隨機數發生器更加安全。
相比於這種隨機,如果你看到(說)10個結果調用Random.Next(100)並投入大量計算資源任務,你可能會制定出最初的種子並預知接下來的結果將是...很有可能也會知道之前的結果是什麼。 如果這種隨機數應用於證券或金融的目的,這會是災難性的事態。 加密隨機數產生器通常比Random慢 ,但它在賦予數字難以預測和獨立方面做得更好。
在很多情況下,隨機數產生器的效能不是一個問題-但有一個適當的API就會出現問題。 隨機數字產生器設計基礎僅此是用來產生隨機位元組。比較這種API的隨機 ,它可以讓你請求一個隨機整數,或隨機double,或一組隨機位元組。我經常發現我需要一個整數的範圍,得到可靠且一致地隨機位元組數組是很重要的。這不是不可能,但至少你可能會想要一個適配器類在隨機數字產生器上。大多情況下,如果你能避免前面所述的陷阱,偽隨機性的Random是可以接受的。
讓我們看看如何能做到這一點。