海量數據等概率選取問題

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爲當前掃描到的文件行數),換句話說對每一行取出的概率均相等,也即完成了隨機的選取。

具體操作可參考如下僞代碼:

複製代碼
Element RandomPick(file): 
Int count = 0; 
while(count <= file.size) 
    If(random(0,count) == 0) 
        picked = file[count]; 
    ++ count; 
Return picked 
複製代碼

證明如下:

2、可以對其進行擴展,即如何從未知或者很大樣本空間隨機地取k個數?

類比下即可得到答案,即先把前k個數放入蓄水池,對於第 i>=k+1,我們以 k/i 概率決定是否要把它換入蓄水池,換入時隨機的選取一個作爲替換項,這樣一直做下去,對於任意的樣本空間n,對每個數的選取概率都爲k/n。也就是說對每個數選取概率相等。
僞代碼:

複製代碼
init a reservoir with the size k
add the first k elements into the reservoir
for i = k+1 to N
    m = random(1,i);
    if(m < k)
        swap the m_th value and i_th value
end for
複製代碼

數學證明:

一些等概率選取相關的題目:

1.等概率隨機排列數組(洗牌算法)
問題描述:假設有一個數組,包含n個元素。現在要重新排列這些元素,要求每個元素被放到任何一個位置的概率都相等(即1/n),並且直接在數組上重排(in place),不要生成新的數組。用 O(n) 時間、O(1) 輔助空間。

思路:先想想如果可以開闢另外一塊長度爲n的輔助空間時該怎麼處理,顯然只要對n個元素做n次(不放回的)隨機抽取就可以了。先從n個元素中任選一個,放入新空間的第一個位置,然後再從剩下的n-1個元素中任選一個,放入第二個位置,依此類推。按照同樣的方法,但這次不開闢新的存儲空間。第一次被選中的元素就要放入這個數組的第一個位置,但這個位置原來已經有別的(也可能就是這個)元素了,這時候只要把原來的元素跟被選中的元素互換一下就可以了。很容易就避免了輔助空間。
詳情:http://www.gocalf.com/blog/shuffle-algo.html

2.單次遍歷,等概率隨機選取問題 
問題描述:假設我們有一堆數據(可能在一個鏈表裏,也可能在文件裏),數量未知。要求只遍歷一次這些數據,隨機選取其中的一個元素,任何一個元素被選到的概率相等。O(n)時間,O(1)輔助空間(n是數據總數,但事先不知道)。
詳情:http://www.gocalf.com/blog/random-selection.html

3.單次遍歷,帶權隨機選取問題
問題描述:有一組數量未知的數據,每個元素有非負權重。要求只遍歷一次,隨機選取其中的一個元素,任何一個元素被選到的概率與其權重成正比。

詳情:http://www.gocalf.com/blog/weighted-random-selection.html


轉:http://www.cnblogs.com/luxiaoxun/archive/2012/09/09/2677267.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章