生成不重複隨機數

        這個題目來自於《編程珠璣》第一章的問題的衍生,簡化後的需求是這樣的:要求生成小於n且沒有重複的n個隨機整數。php

         通常而言,各類編程語言都有庫函數來供咱們生成隨機數,可是庫函數所生成的隨機數可能有重複,爲此,面對這個需求,我須要本身編寫無重複隨機數生成器。考慮到我使用的是php語言,因此結合這個語言所擁有的庫函數,我首先很天然的想到使用shuffle函數,這個函數的做用是打亂數組中元素的順序從新排列,因此,第一種方法的思路是這樣的:首先按照給定的範圍生成一個數組,數組中每一個位置填上對應的整數,即arr[i] = i ,而後使用shuffle函數打亂這個數組從新輸出便可。html

    

//生成特定範圍不重複的隨機數組
//使用shuffle函數
function createRand_shuffle($min, $max)
{
    $store = array();
    for($i=$min;$i<=$max;$i++)
    {
        $store[$i] = $i; 
    }
    shuffle($store);
    return $store;
}


        若是某種編程語言庫裏沒有相似PHP的數組洗牌函數shuffle,那麼就採用第二種方法,這個方法利用了去重的思想,即仍然使用庫函數的隨機數發生器,可是爲了不產生重複的數據,須要對生成的數據進行去重處理,所以這裏就須要一個標記表來記錄已經生成的數據,以便讓新生成的數據對照。思路以下:初始化兩個數組,一個數組用來存放最終生成的數據,另外一個做爲Hash去重對照表,每次生成一個數據,首先到Hash去重數組裏檢查該數據是否已經存在,若是存在,則從新生成數據,不然存入該數據到第一個數組,同時在Hash去重數組裏標記該數據,標記採用位圖的思想,即對於數據i,標記數組第i位爲1。代碼以下:算法

            

//生成特定範圍不重複隨機數組
//hash去重法
function createRand_hash($min, $max)
{
    $hash = array();
    $out = array();
    for($i=$min;$i<=$max;$i++)
    {
        $randnum = rand($min,$max);
        while(@$hash[$randnum]==1)
        {
            $randnum = rand($min,$max);
        }
    $out[] = $randnum;
    @$hash[$randnum]=1;
    }
    return $out;
}


        針對上面這個方法,它的缺點是每次生成一個隨機數,都須要到Hash表內進行對比,當Hash錶快滿時候,對比失敗的次數會愈來愈多,因此爲了解決這個問題,有了第三種方法。
編程

        第三種方法很巧妙,尤爲是它的改進優化方法,頗有意思,來源於eaglet的博客
數組

        博客做者eaglet是著名中文分詞組件盤古分詞的做者,我在網上找尋其餘思路的時候發現的。數據結構

        首先講下優化前的思路:從集合的角度來說,這個方法的主要思想我認爲仍是去重,可是不一樣於第二種方法,這個方法把去重的步奏放到了源數據集合中來作,即每從源集合裏面獲取一個數據,則將該數據從源集合內刪除,保證下次不會再取到這個數據,具體的思路以下:假設n=5,則初始狀況,該數組下標與該下標所「裝」的數是對應的,如圖:編程語言

    初始化一個順序數組(長度爲n,則下標範圍0到n-1)來做爲源集合,每獲取一個元素(第一個元素範圍從0-n),則將該元素從數組裏面刪除,則下次獲取隨機數應當位於0-(n-1)之間。函數

        

        到通過實際驗證,發現這個算法的效率好像不是很高,仔細想一想,原來根據數據結構知識,知道順序表刪除一個元素須要進行挪位,而挪位是很是耗費時間的,原來如此,如今仔細想一想,怎麼對這個地方進行改進。性能

        第三種方法的本質思想和第二種同樣,都是保證源數據集合中的惟一性,只不過是在具體的實現上作了改變。經過不斷更改範圍上界來確保源數據集合的惟一性。測試

        爲了說明清楚,這裏約定用i表示下標序數,c表示內容即數據,n表示長度,一樣的採用上圖示例,

(1)n=5,獲取0-4之間的隨機數,即獲取0-(n-1)之間的隨機數,假設第一次隨機出的數組下標爲3,則下標爲3的數據c=3,將3取出做爲獲取的隨機數,同時將以前的最後一位的數據,即i=n-1上的數據c=4移動到i=3的位置上。如圖:

    


(2)第二次時,假設隨機出的下標序數爲1,則將i=1位置上的數取出,即c=1,而後i=(n-1)=3位置上的數據即範圍上界上的數據c=4挪動到i=1的位置上。

如圖:

        

(3)以此類推,直到取出n個數據。

    這個方法的具體思路其實就是每次經過挪動範圍的上界來將數組劃分爲兩個部分,一個部分是保證惟一性的源數據集合,另外一個部分是已經取過的數據集合,只不過是任然在數組中並未刪除。

三種方法有各自的特色,具體的性能測試沒來得急作,只是將思路羅列出來,往後補充。

相關文章
相關標籤/搜索