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