(12)取樣問題

2021-06-19 17:20:56 字數 3079 閱讀 8158

一、簡介

問題描述:程式的輸入包含兩個整數m和n,其中m(1)一般情況下,如果要從r個剩餘的整數中選出s個,我們以s/r的概率來選擇下乙個數。如下偽**所示:

select = m  

remaining = n

for i = [0, n)

if (bigrand() % remaining) < select

print i

select—

remaining--

下面給出乙個c++的實現:

void getknuth(int m, int n)

}cout << endl;

}

(3)另乙個解決方法是在乙個初始為空的集合裡插入隨機整數,直到個數足夠。偽**如下:

initialize set s to empty  

size = 0

while size < m do

t = bigrand() % n

if t is not in s

insert t into s

size++

print the elements of s in sorted order

利用c++的標準模板庫實現如下所示:

void gensets(int m, int n)

for (i = s.begin(); i != s.end(); ++i)

cout << endl;

}

(3)生成隨機整數的有序子集的另一種方法時把包含整數0~n-1的陣列順序打亂,然後把前m個元素排序輸出即可。

for i = [0, n)  

swap(i, randint(i, n-1) ) // randint(i, j)從i...j範圍內均勻選擇的隨機整數的函式

其實在這個問題中,我們只需要打亂陣列的前m個元素,對應的c++**如下所示:

void genshuf(int m, int n)

for (i = 0; i < m; i++)

//排序陣列中的前m個元素

sort(x, x+m);

for (i = 0; i < m; i++)

cout << endl;

}

二、原理

(1)正確理解所遇到的問題,即將問題抽象成能用數學或邏輯表示的命題;

(2)提煉出抽象問題,有助於我們把解決方案應用到其他問題中;

(3)考慮盡可能多的解法,多思考,然後會發覺寫**的時間會很短;

(4)實現一種解決方案。

三、習題

1、c庫函式rand()通常返回約15個隨機位,使用該函式實現函式bigrand()和randint(l,u),前者返回至少30個隨機位,後者返回[l,u]範圍內的乙個隨機整數,解答如下:

int bigrand() 

int region(int l, int u) //[l, u]

2、當m接近於n時,就集合的演算法生成的很多隨機數都要丟棄,因為他們之前已經存在於集合中了。能否給出乙個演算法,使得即使在隨壞情況下也能使用m個隨機數?

#include #include using namespace std;

void getset(int m,int n)//在0 -- n-1 中挑選m個 隨機數

int main()

3、如何從n個物件中隨機選擇乙個?具體說來,如何在實現不知道文字檔案行數的情況下讀取該檔案,從中隨機選擇並輸出一行?

我們總是選擇第一行,並用二分之一的概率選擇第二行,使用三分之一的概率選擇第三行,以此類推。在該過程結束的時候,每一行具有相同的選中概率(1/n,其中n是檔案的總行數): 

i = 0

while more input lines

with probability 1.0/++i

choice = this input line //如果前面做了選擇,並不會break,而是直到最後乙個為止。

print choice

這裡比較有些疑惑的是第一行:總是選第一行 為什麼概率還是1/n?

概率=1*(1/2)*(2/3)*(3/4)……(n-1/n) =1/n。

證明:當做第i步選擇(選擇第i行)時,選擇該行的概率為1/i,則不選擇的概率為(i-1)/i,對於一篇有n行的文件,現需證明最終選定第i行的概率為1/n。

當最終選擇第i行,前(i-1)步的選擇對最終結果不會產生影響,第i步選擇的概率為1/i,即選擇第i行,第(i+1~n)步中均採取不選擇的動作,即對於任意j(i+1<=j<=n),當前步的概率為(j-1)/j,那麼最終的概率為:(1/i)*((i)/(i+1))*...*((n-1)/n) = 1/n。

以一篇只有6行的文件為例,最終選擇第2行的概率為:1/2*(2/3)*(3/4)*(4/5)*(5/6) = 1/6。

擴充套件:原問題可簡化為:如何從n個有序物件中等概率地任意抽取1個,簡記為sample(n,1),其中n未知;

若將該問題改為:如何從n個有序物件中等概率地任意抽取m個,簡記為sample(n,m),其中n未知;

分析:若n已知,sample(n,m)是普通的抽樣問題;當n未知時,可否根據上述演算法進行相應的轉化求解?

解決方案:將sample(n,m)問題轉化為m個sample(n,1)問題,更具體一點是,轉化為sample(n,1);sample(n-1,1);sample(n-2,1)....;sample(n-m+1,1)問題。

仍然以一篇6行文件為例,任取其中2行,做法如下:

第一遍,以如下概率選中一行:1(1)   2(1/2)  3(1/3)  4(1/4)  5(1/5)  6(1/6),假設選中第2行,接著概率修改如下:3(1)  4(1/2)  5(1/3)  6(1/4)  1(1/5)。

說明:當選中第2行,從第3行開始修改概率,並將第2行排除在外,繼續掃瞄,這樣能保證在剩下的5個數中仍然以等概率抽取其中的乙個。

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

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

概率取樣問題

問題 程式輸入兩個整數m和n,其中m 顯然,演算法的執行時間為o n 2 基於集合的演算法 在乙個初始為空的集合裡面插入隨機整數,直到個數足夠,核心問題是如何實現集合s。我們可以考慮有序鍊錶 二叉樹等資料結構,但最直接的辦法是利用c 標準模板庫中的set容器。c stl規範要求每次插入操作都在o l...

程式設計珠璣 取樣問題

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