取样问题
問題描述:程序的輸入包含兩個整數(shù)m和n,其中m<n。輸出是0~n-1范圍內的m個隨機整數(shù),要求:每個數(shù)選擇出現(xiàn)的概率相等,且按序輸出。該題目是從《編程珠璣》的第12章看到的。
學習過概率統(tǒng)計的同學應該都知道每一個數(shù)字被抽取的概率都應該為m/n. 那么我們怎么構造出這樣的概率呢?在《編程珠璣》上面是這樣解析的:
依次考慮整數(shù)0,1,2,.....,n-1,并通過一個適當?shù)碾S機測試對每個整數(shù)進行選擇。通過按序訪問整數(shù),我們可以保證輸出結果是有序的。 假如我們考慮m = 2,n = 5的情況,那么選擇的每一個數(shù)字的概率都應該是2/5,我們怎么樣才能做到呢?不慌張,慢慢來。
下面給出我的分析過程:在0,1,2,3,4這五個數(shù)字中,我們依次對每一個數(shù)進行分析,第一次遇到0時,它的選擇概率應該是2/5,如果選中了,我們開始測試第二個數(shù)1,這個時候因為1選中了,所以1這個數(shù)字的選中概率就變小了,變成1/4了,有人說這似乎不對吧,因為題目說讓每一個數(shù)字選中的概率是一樣大的,而現(xiàn)在?一個2/5,一個1/4,這怎么行呢?其實不是這樣的,認真思考一下就知道了,數(shù)字1選中的概率等于什么? 數(shù)字1選中的概率p(1) = 數(shù)字0選中的概率 * (1/4) + 數(shù)組0沒選中的概率*(2/4)這樣推算下 (2/5 * 1/4) + (3/5 * 2/4) = 8/20 = 2/5 。這不就一樣了嗎?呵呵!下面給出來自Knuth的《The Art of Computer Programming, Volume2:Seminumerical Algorithms》的偽代碼:
select = m remaining = n for i = [0,n)if (rand() % remaining) < selectprint iselect --remaining--代碼很精簡,代碼遵守的規(guī)則應該是要從r個剩余的整數(shù)中選出s個,我們以概率s/r選擇下一個數(shù)。這個概率的選擇方式和我們上面證明的是一樣的。所以在程序結束的時候一定會打印出m個數(shù)字,且每一個數(shù)字的被選擇概率相同,為m/n。 (不要小看任何一段小代碼,其背后隱含著很多的意義!)
當然了,這個題目還有其他的解法,這是在網(wǎng)上看到的其他的解法。他們將這樣的問題抽象的定義為蓄水池抽樣問題。其思路是這樣的,先把前k個數(shù)放入蓄水池中,對第k+1,我們以k/(k+1)的概率決定是否要把它換入蓄水池,換入時我們可以隨機挑選一個作為替換位置,這樣一直到樣本空間N遍歷完,最后蓄水池中留下的就是結果。這樣的方法得到的結果也是正確的,且每一個數(shù)字被選擇的概率也是k/n。
證明:1) 當遍歷到到m+1個元素時, 該元素被保存在A’中的概率為 m/(m+1), 前面m個元素被保存在A’中的概率為 1- (m/m+1 * 1/m) = m/m+1
2) 當遍歷到第i個元素時,設前面i-1個元素被保存在A’中的概率為 m/(i-1)。根據(jù)算法, 第i個元素被保存在A’中的概率為m/i , 前面i-1各個元素留在A’中的概率為 m/(i-1) * (1-(m/i* 1/m) = m/i;
3)通過歸納,即可得到每個元素留在A’中的概率為 m/n;
參見:http://blog.csdn.net/hackbuteer1/article/details/6878627#reply?九樓smqjzzm1988?回復
?
這個問題其實還可以擴展一下:
如何從n個對象(可以以此看到這n個對象,但事先不知道n的值)中隨機選擇一個?比如在不知道一個文本中有多少行,在這樣的情況下要求你隨機選擇文件中一行,且要求文件的每一行被選擇的概率相同。 在知道n這個總對象個數(shù)的情況下,誰都知道概率是1/n. 但是我們現(xiàn)在不知道,怎么辦呢?
考慮這樣是不是可以,我們總是以1/i的概率去選擇每一次遍歷的對象,比如從1,2,3,4,5,6,....,N, 每一次遍歷到x時,總是以1/x的概率去選擇它.
整體思路如下:
我們總選擇第一個數(shù)字(文本行),并以概率1/2選擇第二個(行),以1/3選擇第三行,也就是說設結果為result,遍歷第一個時result = 1,第二個時以1/2的概率替讓result = 2,這樣一直遍歷概率性的替換下去,最終的result就是你的結果。他被選擇的概率就是1/n。
證明思路如下:
第x個數(shù)被選擇的概率等于x被選擇的概率 * (x+1沒被選擇的概率) * (x+2沒有被選擇的概率) *......*(N沒有被選擇的概率) ?具體化一下
2被選擇的概率 = 1/2 ?* 2/3 * 3/4 * 4/5 .....* (n-1/n) 我想你知道答案了吧? 對! 是1/n.這樣就可以在不知道N的大小的情況下等概率的去選擇任意一個對象了!
參考偽代碼如下:
i = 0 while more input lineswith probability 1.0/++ichoice = this input line print choice由于本文章中的偽代碼思路很清晰,所以不必要貼出代碼了。
以上轉自:http://www.cnblogs.com/fxplove/archive/2012/07/25/2607544.html
?
現(xiàn)對取樣做一個小結:
1.輸出是0~n-1范圍內的m個隨機整數(shù),可重復。
void genRandoms1(int n,int m){for(int i=0;i<m;i++){int r=rand()%n;cout<<r<<' ';}cout<<endl; }?
2.輸出是0~n-1范圍內的m個隨機整數(shù),不可重復。
//generate random digit in [l,u)
int rand(int l,int u){
//srand(time(0));
return rand()%(u-l)+l;
}
void genRandoms2(int n,int m){int* a=new int[n];for(int i=0;i<n;i++){a[i]=i;}srand(time(0));for(int i=0;i<m;i++){int j=rand(i,n);swap(a[i],a[j]);cout<<a[i]<<' ';}cout<<endl;delete []a; }
?
3.輸出是0~n-1范圍內的m個有序的隨機整數(shù),可重復。
方法一,先生成可重復m個隨機整數(shù),然后排序。方法二,將生成的可重復m個隨機整數(shù)插入到非降序的集合中。
4.輸出是0~n-1范圍內的m個有序的隨機整數(shù),不可重復。
void genRandoms4(int n,int m){srand(time(0));for(int i=0;i<n;i++){if(rand()%(n-i)<m){cout<<i<<' ';m--;}}cout<<endl; }轉載于:https://www.cnblogs.com/freewater/archive/2012/08/12/2635465.html
總結
- 上一篇: sql:无法解决 equal to 操作
- 下一篇: Peace