非重複隨機序列產生演算法

來源:互聯網
上載者:User

最近編程時遇到一個問題:有一組對象,要求隨機地訪問其中每一個對象,並且每個對象只訪問一次。如果我們將訪問順序轉換為一組整數序列,那麼這就是一個關於“非重複隨機序列產生演算法”的問題。

本文將探討這個問題的多種解法,並給出一個非常高效的演算法。

 

【問題描述】:有一個自然數N,希望得到一個整型序列,該序列包含N個整數,從0到N-1,呈隨機分布狀態,且不重複。

 

【問題分析】:產生隨機數是簡單的,關鍵是,如何保證不重複呢?一般來說,我們有兩種思路:

  思路1:我們不能保證每次產生的隨機數都是不重複的,但是可以在產生隨機數之後,判斷這個數值是否已經產生過了,如果已經產生過了,那就重建一個,直到產生一個新的數值。

  思路2:每產生一個隨機數之後,就調整隨機數的建置規則,將已經產生過的數值排除掉,從而保證每次產生的隨機數一定是新的。

接下來,我們將根據這兩種思路,給出幾種演算法,並分析每種演算法的複雜度。

 

【演算法1】

“思路1”是絕大多數程式員的思維。如果在項目開發過程中遇到這個問題,我相信大部分程式員一定會這麼做:建立一個List,用於儲存隨機產生的數值,每次插入時,先判斷這個數值是否已經在List中存在了,如果已經存在,則重建。C#代碼如下:

/// <summary>/// 產生一個非重複的隨機序列。/// </summary>/// <param name="count">序列長度。</param>/// <returns>序列。</returns>private static List<int> BuildRandomSequence1(int length) {    List<int> list = new List<int>();    int num = 0;    for (int i = 0; i < length; i++) {        do {            num = random.Next(0, length);        } while (list.Contains(num));        list.Add(num);    }    return list;} 

上述演算法簡單易懂,時間複雜度是O(N²) ,空間複雜度是O(N)。該演算法的局限在於,每產生一個隨機數,都要遍曆List,判斷是否已經存在,這就導致時間複雜度太高。那麼,如何改善這個演算法呢?請繼續往下看。

 

【演算法2】

我們知道,遍曆List的時間複雜度是O(N),訪問Hashtable的時間複雜度是O(1)。所以,如果我們使用Hashtable來判斷是否重複,就可以將整個演算法的時間複雜度從O(N²)降至O(N),而空間複雜度仍然基本保持在O(N)。

C#代碼如下:

/// <summary>/// 產生一個非重複的隨機序列。/// </summary>/// <param name="count">序列長度。</param>/// <returns>序列。</returns>private static Hashtable BuildRandomSequence2(int length) {    Hashtable tab = new Hashtable(length);    int num = 0;    for (int i = 0; i < length; i++) {        do {            num = random.Next(0, length);        } while (tab.Contains(num));        tab.Add(num, null);    }    return tab;}

經過測試,效能確實有了非常大的提升。但是,有一個問題依然存在:隨著Hashtable中的數值越來越多,重複機率也會越來越高,這一點很容易理解。如何才能降低重複機率、進一步提高演算法效能呢?不妨嘗試一下“思路2”。

 

【演算法3】

“思路2”的基本思想是:利用隨機數的產生特點,將已經產生的數值,排除在隨機區間之外,這樣就可以確保下次產生的隨機數一定是新的。具體來說,我們可以這樣做:

首先,建立一個長度為N的數組array,初始值是0…N-1。

然後,產生一個隨機數x1=random.Next(0, N),則x1∈[0,N)。取num1=array[x1]作為序列中的第一個成員。接下來是關鍵步驟:將num1和array[N-1]交換。

然後,產生下一個隨機數x2= random.Next(0, N-1),則x2∈[0,N-1)。由於num1已經被交換到了array[N-1],而x2<N-1,所以num2=array[x2]一定不等於num1,從而避免了重複。然後將num2和array[N-2]交換。

按照上述方法,可以得到序列中第三、第四…第N個成員。最後得到的array就是一個非重複的隨機序列。以下是整個計算過程的圖形示範(假設N=5):

C#代碼如下:

/// <summary>/// 產生一個非重複的隨機序列。/// </summary>/// <param name="count">序列長度。</param>/// <returns>序列。</returns>private static int[] BuildRandomSequence3(int length) {    int[] array = new int[length];    for (int i = 0; i < array.Length; i++) {        array[i] = i;    }    int x = 0, tmp = 0;    for (int i = array.Length - 1; i > 0; i--) {        x = random.Next(0, i + 1);        tmp = array[i];        array[i] = array[x];        array[x] = tmp;    }    return array;}

經過分析,演算法3的時間和空間複雜度都是O(N),效能非常高。通過巧妙的交換位置的方法,可以確保每次得到的數值一定是不重複的,所以不用去判斷是否重複。而且,使用數組來儲存序列,比List和Hashtable效能更好。

上述演算法產生的隨機序列是從0到N-1,如果我們指定了別的區間範圍呢?例如,要求產生一個非重複的隨機序列,範圍是[low, high]。實現起來非常簡單,只要把演算法3稍微改一下就可以了。C#代碼如下:

/// <summary>/// 產生一個非重複的隨機序列。/// </summary>/// <param name="low">序列最小值。</param>/// <param name="high">序列最大值。</param>/// <returns>序列。</returns>private static int[] BuildRandomSequence4(int low, int high) {    int x = 0, tmp = 0;    if (low > high) {        tmp = low;        low = high;        high = tmp;    }    int[] array = new int[high - low + 1];    for (int i = low; i <= high; i++) {        array[i - low] = i;    }    for (int i = array.Length - 1; i > 0; i--) {        x = random.Next(0, i + 1);        tmp = array[i];        array[i] = array[x];        array[x] = tmp;    }    return array;}

為了驗證上述三種演算法的實際效能,我們以產生隨機序列所耗的平均時間為標準,進行了實際測試。

測試環境為:Windows7/ CPU 1.6GHZ /VS2008/C#。測試結果為:

序列長度

演算法1

演算法2

演算法3

100

15ms

<1ms

<1ms

1000

46ms

<1ms

<1ms

10000

4430ms

31ms

<1ms

 

 

 

 

 

 

 

【總結】

本文演算法3的關鍵方法是:在現有數組基礎上,通過不斷地交換位置,來巧妙地達到時間和空間的最優。

其實,一些經典排序演算法採用的也是這個思想,例如:冒泡排序、快速排序、堆排序,等等。

演算法3也可以理解為一種隨機排序演算法,可以應用在很多場合,例如:洗牌、抽籤等。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.