程式設計珠璣 取樣總結

2021-07-07 01:57:25 字數 1504 閱讀 7641

問題描述:程式的輸入包含兩個整數m和n,其中m小於n。輸出是0~n-1範圍內的m個隨機整數,要求:每個數選擇出現的概率相等,且按序輸出。

問題中最重要的要求是概率相同。假設m=2,n=5,那麼每個數都應該以2/5的概率被選中,直觀的會想到用這樣的**實現:

if((rand()%5)<2)
然而這樣做是不對的,程式設計珠璣12章對這個問題進行了詳細的介紹。本文於是總結了程式設計珠璣中等概率取樣問題的幾種解決方法,以便以後快速回憶。

首先來分析上面那行**的問題。在我們對數字0-4之間做選擇時,首先要對0做出選擇,之後就不能用同樣的概率來選擇數字1;因為若是已經選擇了數字0,選1的概率應為1/4,沒選0,選1的概率為2/4。在這樣的選擇方案下才能保證數字1也是2/5被選中的概率: p(

1)=(

2/5∗

1/4)

+(3/

5∗2/

4)=2

/5

可以看出,我們需要維持乙個變數select來表示還需要選取數的數目,在remaining中數中選出select個的數,我們就以select/remaining 的概率選取下乙個數。實現**如下:

void genknuth(int m,int n)

}

knuth的《計算機程式設計藝術》的第二卷也有類似的**。

上述方法的執行時間正比於n,若是m遠小於n的話,或許用一種時間複雜都跟m相關的演算法也還不錯。

一種方法是在乙個初始為空的集合裡面插入隨機數,直到個數足夠。每次插入乙個數時判斷是否已經在集合裡面,不在的話插入;在的話繼續返回下乙個隨機數。這樣也能保證每個數被插入到集合中的概率是一樣的,都為m/n。

程式設計珠璣中用了set資料結構,其不儲存重複的元素,同時輸出也能保證相對的順序。**如下:

void gensets(int m,int n)

每次插入操作耗時o(logm),那麼總時間複雜都為o(mlogm).

書中還講到了一種方法是把包含整數 0至n-1的陣列順序打亂,然後把m個元素排序輸出。如下:

for

i=[0,n)

swap(i,randint(i,n-i))

進一步考慮,我們可以只把陣列中的前m個數的打亂,然後排序,這樣也是需要o(mlgm)的時間複雜度,但是同時需要o(n)的空間複雜度(用來儲存n個數)。

方法2中生成的很多隨機數都要丟掉,因為他們之前已經存在於集合中了,當m接近於n的時候尤為明顯。 當然我們可以改變想法,先生成n-m個隨機數,然後再輸出不再樣本中整數;但是m若是逼近與n/2,仍然不可避免要丟掉大量的隨機數。

珠璣習題12.9講述了乙個很巧妙的演算法來解決上述的問題,由robertfloyd提出。

實現**如下:

void genfloyd(int m,int n)

}

這個演算法保證了只生成m次隨機數,每次都可以講乙個新的隨機數加入到集合中。

程式設計珠璣 取樣問題

1.問題描述 程式的輸入包含兩個整數m和n,其中m2.解決思路與 實現 程式設計珠璣上給出了四個函式 1 genknuth 演算法依次考慮整數0,1,2,n 1,並通過乙個適當的隨機測試對每個整數進行選擇。通過按序訪問整數,可以保證輸出結果是有序的 c 實現 void genknuth int m,...

程式設計珠璣之取樣問題

要從0 n 1的整數中取出來m m第二種演算法和以前一篇的洗盤演算法比較相似,但是並不是嚴格的每個數字取到的概率為m n,演算法是這樣的,先生成乙個n維的整數陣列,a值為0 n 1,然後生成m個n內的隨機整數rand,然後交換a i 和a rand i 0.m 1 至於是否是滿足m n證明好像有點難...

程式設計珠璣筆記 第12章 取樣問題

整理了這一章提到的幾個演算法,其中蓄水池演算法書中沒有寫,這裡放在一起比較一下,出了方法2是c 的 其它都是python的實現。問題 程式的輸入包括兩個整數m和n,其中m 1 以特定概率順序選擇每乙個數 如果要從r個剩餘的整數中選出s個,則以s r的概率選擇剩餘整數中的第乙個整數,然後遞迴處理剩下的...