不重複隨機數列產生演算法(1)

來源:互聯網
上載者:User

本文將講述一個高效的不重複隨機數列的產生演算法,其效率比通常用hashtable 消重的方法要快很多。

首先我們來看命題:

給定一個正整數n,需要輸出一個長度為n的數組,數組元素是隨機數,範圍為0 – n-1,且元素不能重複。比如 n = 3 時,需要擷取一個長度為3的數組,元素範圍為0-2,

比如 0,2,1。

這個問題的通常解決方案就是設計一個 hashtable ,然後迴圈擷取隨機數,再到 hashtable 中找,如果hashtable 中沒有這個數,則輸出。下面給出這種演算法的代碼

 
  1. public static int[] GetRandomSequence0(int total)  
  2. {  
  3.     int[] hashtable = new int[total];  
  4.     int[] output = new int[total];  
  5.  
  6.     Random random = new Random();  
  7.     for (int i = 0; i < total; i++)  
  8.     {  
  9.         int num = random.Next(0, total);  
  10.         while (hashtable[num] > 0)  
  11.         {  
  12.             num = random.Next(0, total);  
  13.         }  
  14.  
  15.         output[i] = num;  
  16.         hashtable[num] = 1;  
  17.     }  
  18.  
  19.     return output;  

代碼很簡單,從 0 到 total - 1 迴圈擷取隨機數,再去hashtable 中嘗試匹配,如果這個數在hashtable中不存在,則輸出,並把這個數在hashtable 中置1,否則迴圈嘗試擷取隨機數,直到找到一個不在hashtable 中的數為止。這個演算法的問題在於需要不斷嘗試擷取隨機數,在hashtable 接近滿時,這個嘗試失敗的機率會越來越高。

那麼有沒有什麼演算法,不需要這樣反覆嘗試嗎?答案是肯定的。

如所示,我們設計一個順序的數組,假設n = 4

第一輪,我們取 0 – 3 之間的隨機數,假設為2,這時,我們把數組位置為2的數取出來輸出,並把這個數從數組中刪除,這時這個數組變成了

第二輪,我們再取 0-2 之間的隨機數,假設為1,並把這個位置的數輸出,同時把這個數從數組刪除,以此類推,直到這個數組的長度為0。這時我們就可以得到一個隨機的不重複的序列。

這個演算法的好處是不需要用一個hashtable 來儲存已擷取的數字,不需要反覆嘗試。演算法代碼如下:

 
  1. public static int[] GetRandomSequence1(int total)  
  2. {  
  3.     List<int> input = new List<int>();  
  4.     for (int i = 0; i < total; i++)  
  5.     {  
  6.         input.Add(i);  
  7.     }  
  8.  
  9.     List<int> output = new List<int>();  
  10.  
  11.     Random random = new Random();  
  12.     int end = total;  
  13.     for (int i = 0; i < total; i++)  
  14.     {  
  15.         int num = random.Next(0, end);  
  16.         output.Add(input[num]);  
  17.         input.RemoveAt(num);  
  18.         end--;  
  19.     }  
  20.  
  21.     return output.ToArray();  
  22. }  

這個演算法把兩個迴圈改成了一個迴圈,演算法複雜度大大降低了,按說速度應該比第一個演算法要快才對,然而現實往往超出我們的想象,當total = 100000 時,測試下來,第一個演算法用時 44ms, 第二個用時 1038 ms ,慢了很多!這是為什麼呢?問題的關鍵就在這個 input.RemoveAt 上了,我們知道如果要刪除一個數組元素,我們需要把這個數組元素後面的所有元素都向前移動1,這個移動操作是非常耗時的,這個演算法慢就慢在這裡。到這裡,可能有人要說了,那我們不用數組,用鏈表,那刪除不就很快了嗎?沒錯,鏈表是能解決刪除元素的效率問題,但尋找的速度又大大降低了,無法像數組那樣根據數組元素下標直接定位到元素。所以用鏈表也是不行的。到這裡似乎我們已經走到了死胡同,難道我們只能用hashtable  反覆嘗試來做嗎?在看下面內容之前,請各位讀者先思考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.