在進行開發的時候,你難免會用到隨機數。還有更多的時候,我們需要隨機數來生成一系列的數字串備用。不過好在各種編程語言裡都提供了一個簡單好用的偽隨機數生成器供你使用,比如C#裡的 隨機。
不過,C#的 Random 默認是以系統時鐘為種子的——這種方法簡單粗暴,可惜一旦遇到短時間生成大量隨機數的情況就捉襟見肘了——一堆相同的隨機數就蹦出來了,不過正是因為隨機,所以才會有相同的數字出現,但是對於我們人類的感官來講,“隨機”其實指的是“隨機且不重複“。
所以,你需要一個去重的算法。簡單來講,使用合集類型是最好的解決辦法,比如我熟悉的 Swift 裡有個 組 類型,它自動過濾掉了重複的元素……不過我不熟悉 C# 裡的類似類型,所以我也是使用了網上比較流行的方法。
網上比較流行的方法一種是遞歸的方式去重,另一種是用 哈希表 。還有一種我忘記了,我使用的是後者,這個容易理解也好實現,邏輯簡單。
生成隨機數
大概的思路就是我們得到了隨機數,就把它添加進 哈希表 ,由於這個是字典類型,那麼我們把 鍵 和 值 都設置為相同的值——即要儲存的字串即可。接下來每個要儲存的字串都先判斷一下是否已存在就好了,而 哈希表 有這個自帶方法 .containsvalue和() 。
那麼,代碼就應該是這樣的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public Hashtable genRandom(int Number) { Random ran = new Random(); Hashtable myRandom = new Hashtable(); while(myRandom.Count < Number) { int nValue = ran.Next(); if (!myRandom.ContainsValue(nValue)) { myRandom.Add(nValue, nValue); } } return myRandom; } |
這樣,我們只要傳入需要的隨機數數量,則 genRandom(INT) 就會返回帶有對應數量的不重複隨機數序列了,你可以方便地把 哈希表 轉換為數組。
不過,這依舊不太ok,這樣返回的隨機數長度不同,從大到小都有,如果我們需要的是一段相同的長度數據呢?隨機數的產生從零到幾萬幾十萬,顯然事後再慢慢挑選是個壞主意。
設定範圍
比如具體到我自己的case,我需要一段相同位數的隨機號碼——好吧,我們還需要一個篩選器。什麼是篩選器呢?其實就是個限制,我們不需要自己實現算法,因為 .下一個() 方法已經給出了重載,使用 跑.下一個(minValue(最小值),包括maxValue)即可,這樣就可以實現讓隨機數“指哪打哪”了,好,現在我們來迭代上邊的代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public Hashtable genRandom(int Number,int length) { Random ran = new Random(); Hashtable myRandom = new Hashtable(); int lowLine = 1; for (int i = 1;i < length;++i) { lowLine *= 10; } int hightLine = lowLine * 10 - 1; while(myRandom.Count < Number) { int nValue = ran.Next(lowLine,hightLine); if (!myRandom.ContainsValue(nValue)) { myRandom.Add(nValue, nValue); } } return myRandom; } |
首先請原諒我這個中二的屬性命名法,總之,我們可以給 genRandom(INT,INT) 傳入需要的隨機數數量和每個隨機數長度(也就是位數)了。這樣,我們就得到了給定數量、給定長度的隨機數列。
不過,比如說還是我的case,這裡我需求幾千個結果——甚至說幾萬個?總之,這裡要說的是雖然我們有了去重的機制,但出現重複需要判斷是不可避免的,如果一旦出現重複,就需要再次循環,這樣的話還是會消耗一部分資源——雖然說頂了天了也就幾秒鐘的事情(其實就最慢也就一兩秒),但你想,用戶點擊“生成隨機數」按鈕,然後你的應用界面假死2秒——這不叫用戶體驗差——這叫錯誤。
所以,這時候你就需要一個更高級的隨機數實現——當然了,比起這個,我想你更需要的應該是一個更高級的種子。
提高隨機度
我們就地取材用系統的時鐘,在每次循環的時候作為種子傳給 Random 實例,這樣就大大降低了重複的概率,避免了循環多次卻得不到有效的數字。
1 2 |
long tick = DateTime.Now.Ticks; Random ran = new Random((int)(tick & 0xffffffffL) | (int)(tick >> 32)); |
把這段代碼替換到上邊的代碼段當中就可以了。
其實還有另外的調用一些加密方法裡的隨機數種子生成器來生成,但我倒是不太喜歡,那樣又要調用一個方法來生成一個隨機的種子?反而不如就地取材來的實在。
還有一件事
這裡我再提一句,我是這樣使用上邊的方法的,利用構造器將返回的 哈希表 轉換為 ArrayList的 :
1 |
ArrayList myRandomNumbers = new ArrayList(genRandom(5000,13).Values); |
本文由 落格博客 原創撰寫:落格博客 » C# 裡的 隨機數
轉載請保留出處和原文鏈接:https://www.logcg.com/archives/1347.html