概率随机问题
http://www.cnblogs.com/yysblog/archive/2012/06/27/2566276.html
1、問題定義可以簡化如下:在不知道文件總行數的情況下,如何從文件中隨機的抽取一行?
首先想到的是我們做過類似的題目嗎?當然,在知道文件行數的情況下,我們可以很容易的用C運行庫的rand函數隨機的獲得一個行數,從而隨機的取出一 行,但是,當前的情況是不知道行數,這樣如何求呢?我們需要一個概念來幫助我們做出猜想,來使得對每一行取出的概率相等,也即隨機。這個概念即蓄水池抽樣(Reservoir Sampling)。
有了這個概念,我們便有了這樣一個解決方案:定義取出的行號為choice,第一次直接以第一行作為取出行 choice ,而后第二次以二分之一概率決定是否用第二行替換 choice ,第三次以三分之一的概率決定是否以第三行替換 choice ……,以此類推,可用偽代碼描述如下:
i = 0
while more input lines
?????????? with probability 1.0/++i
?????????????????? choice = this input line
print choice
這種方法的巧妙之處在于成功的構造出了一種方式使得最后可以證明對每一行的取出概率都為1/n(其中n為當前掃描到的文件行數),換句話說對每一行取出的概率均相等,也即完成了隨機的選取。
證明如下:
回顧這個問題,我們可以對其進行擴展,即
2、如何從未知或者很大樣本空間隨機地取k個數?
類比下即可得到答案,即先把前k個數放入蓄水池,對第k+1,我們以k/(k+1)概率決定是否要把它換入蓄水池,換入時隨機的選取一個作為替換項,這樣一直做下去,對于任意的樣本空間n,對每個數的選取概率都為k/n。也就是說對每個數選取概率相等。
偽代碼:
Init : a reservoir with the size: k
for i= k+1 to N
??? M=random(1, i);
??? if( M < k)
???? SWAP the Mth value and ith value
end for?
證明如下:
?
蓄水池抽樣問題是一類問題,在這里總結一下,并由衷的感嘆這種方法之巧妙,不過對于這種思想產生的源頭還是發覺不夠,如果能夠知道為什么以及怎么樣想到這個解決方法的,定會更加有意義。
3、等概率隨機排列數組(洗牌算法)
問題描述:假設有一個數組,包含n個元素。現在要重新排列這些元素,要求每個元素被放到任何一個位置的概率都相等(即1/n),并且直接在數組上重排(in place),不要生成新的數組。用 O(n) 時間、O(1) 輔助空間。
先想想如果可以開辟另外一塊長度為n的輔助空間時該怎么處理,顯然只要對n個元素做n次(不放回的)隨機抽取就可以了。先從n個元素中任選一個,放入新空間的第一個位置,然后再從剩下的n-1個元素中任選一個,放入第二個位置,依此類推。
按照同樣的方法,但這次不開辟新的存儲空間。第一次被選中的元素就要放入這個數組的第一個位置,但這個位置原來已經有別的(也可能就是這個)元素了,這時候只要把原來的元素跟被選中的元素互換一下就可以了。很容易就避免了輔助空間。
用Python來寫一段簡單的程序描述這個算法:
| 1 2 3 4 5 6 7 | from?random?import?Random def?Shuffle(li): ? rand?=?Random() ??for?x?in?xrange(len(li)?-?1,?0,?-1): ?# 逆序遍歷li ? ? y?=?rand.randint(0,?x)?? ? ? ? ? ? ?# 從剩余數據中隨機選取一個 ? ? li[x],?li[y]?=?li[y],?li[x]?? ? ? ??# 將隨機選取的元素與當前位置元素互換 |
主要的代碼僅僅三行而已,淺顯易懂。
來計算一下概率。如果某個元素被放入第i(1≤i≤n)個位置,就必須是在前 i - 1 次選取中都沒有選到它,并且第 i 次選取是恰好選中它。其概率為:
pi=n?1n×n?2n?1×?×n?i+1n?i+2×1n?i+1=
可見任何元素出現在任何位置的概率都是相等的。
4、利用等概率Rand5產生等概率Rand3
int?Rand3(){
? ??int?x;
? ??do
? ??{
? ? ? ? x?=?Rand5();
? ??}?while?(x?>=?3);
? ??return?x;
}
算法很簡單,x是我們最終要輸出的數字,只要它不在[0, 3)范圍內,就不斷地調用Rand5來更新它。直觀地看,算法輸出的數字只有0、1、2這三個,而且對任何一個都沒有偏袒,那么顯然每個數字的概率都是1/3,那讓我們來嚴格地計算一下。
以輸出0為例,看看概率是多少。x的第一個有效數值是通過Rand5得到的。Rand5返回0的概率是1/5,如果這事兒發生了,我們就得到了0, 否則只有當Rand5返回3或4的時候,我們才有機會再次調用它來得到新的數據。第二次調用Rand5之后,又是有1/5的概率得到0,2/5的概率得到 3或4導致循環繼續執行下去,如此反復。因此概率的計算公式為:
p=====15+25×(15+25×(15+25×(?)))15×∑∞i=0(25)i15×11?2515×5313
喏,計算表明,Rand3輸出0的概率確實是1/3,對于另外兩個數字也是一樣的。
5、給定一個函數rand5(),使函數rand7()可以隨機等概率的生成1-7的整數
題目:
給定一個函數rand5(),該函數可以隨機生成1-5的整數,且生成概率一樣。現要求使用該函數構造函數rand7(),使函數rand7()可以隨機等概率的生成1-7的整數。
思路:
很 多人的第一反應是利用rand5() + rand()%3來實現rand7()函數,這個方法確實可以產生1-7之間的隨機數,但是仔細想想可以發現數字生成的概率是不相等的。rand()%3 產生0的概率是1/5,而產生1和2的概率都是2/5,所以這個方法產生6和7的概率大于產生5的概率。
正確的方法是利用rand5()函數生成1-25之間的數字,然后將其中的1-21映射成1-7,丟棄22-25。例如生成(1,1),(1,2),(1,3),則看成rand7()中的1,如果出現剩下的4種,則丟棄重新生成。
簡單實現:
Java代碼?
???我的備注:
????這 種思想是基于,rand()產生[0,N-1],把rand()視為N進制的一位數產生器,那么可以使用rand()*N+rand()來產生2位的N進 制數,以此類推,可以產生3位,4位,5位...的N進制數。這種按構造N進制數的方式生成的隨機數,必定能保證隨機,而相反,借助其他方式來使用 rand()產生隨機數(如?rand5() + rand()%3?)都是不能保證概率平均的。
????此題中N為5,因此可以使用rand5()*5+rand5()來產生2位的5進制數,范圍就是1到25。再去掉22-25,剩余的除3,以此作為rand7()的產生器.
給定一個函數rand()能產生0到n-1之間的等概率隨機數,問如何產生0到m-1之間等概率的隨機數?
如何產生如下概率的隨機數?0出1次,1出現2次,2出現3次,n-1出現n次?
總結
- 上一篇: 一次遍历等概率选取字符串中的某个字符
- 下一篇: grep gawk