水池抽樣演算法

2022-09-18 08:06:34 字數 3014 閱讀 8770

有這樣一類問題, 就是大資料流中的隨機抽樣問題,即:

當記憶體無法載入全部資料時,如何從包含未知大小的資料流中隨機選取k個資料,並且要保證每個資料被抽取到的概率相等。

這道題有兩個限制:

高效,即節省記憶體的使用

盡量隨機地返回值

假如我們去掉限制1,可以很簡單地做出來:我們將所有資料載入進記憶體,計算鍊錶長度,然後通過random函式來求取幾個隨機數。

但是這樣的效率並不高,把所有資料載入到記憶體,如果資料非常大可能會導致無法計算。

注意題目中有乙個說明,就是鍊錶。鍊錶這種資料結構是通過資料節點首尾相連形成的鏈式儲存結構。

既然是鍊錶,那麼可以乙個乙個節點處理,不需要將所有資料載入到記憶體,乙個節點乙個節點去處理,這還不夠形象,將題目換個形式來表述:

我們有大量的文字檔案存在硬碟中,想隨機抽取幾行,保證盡可能少得使用記憶體並且能夠完全隨機.

之前想到的載入到記憶體就不太適合了,但是還可以想到別的辦法,比如每次讀取一行記錄載入到記憶體,記數+1,清空記憶體中行資料,直到最後統計一共多少行,然後根據總行數來計算k個隨機數。

如何再取回行對應的資料呢?我們可以再遍歷一遍,一邊遍歷一邊記錄這一行的行數是不是在k個隨機數中,如果是,則將該行內容保留。

這樣的話遍歷兩次應該可以做到,但是資料量大的時候遍歷兩次的時間消耗是非常高的。

所以還有更好的方案嗎,那就是水塘抽樣演算法。

對於複雜問題一定要學會歸納總結,即從小例子入手,然後分析,得出結論,然後在證明。不然遇到乙個抽象問題,不舉例感覺這個問題,直接解還是比較難的。

接下來從 k = 1 開始說明

k = 1

首先考慮最簡單的情況,當 k = 1 時,如何選取:

這樣就可以看出,對於k=1的情況,我們可以制定這樣簡單的抽樣策略:

在取第n個資料的時候,我們生成乙個0到1的隨機數p,如果p小於等於1/n,保留第n個數。大於1/n,繼續保留前面的數。直到資料流結束,返回此數。資料流中第 i 個數被保留的概率為 1/i 。只要採取這種策略,只需要遍歷一遍資料流就可以得到取樣值,並且保證所有數被選取的概率均為 1/n 。

下面用數學歸納法證明此結論。

1) 當n=1時,第乙個元素以1/1的概率返回,符合條件。

2) 假設當n=k時成立,即每個元素都以1/k的概率返回,先證明n=k+1時,是否成立。

對於最後乙個元素顯然以1/k+1的概率返回,符合條件,對於前k個資料,被返回的概率為1/k * (1- 1/k+1)=1/k+1,滿足題意。

綜上所述,結論成立。

**

1

class

solution:

2def

__init__

(self, head: optional[listnode]):

3 self.head =head45

def getrandom(self) ->int:

6 node, i, ans = self.head, 1, 0

7while

node:

8if randrange(i) == 0: #

1/i 的概率選中(替換為答案)

9 ans =node.val

10 i += 1

11 node =node.next

12return ans

ps: k=1 時**中生成的不是概率而是區間數的原因見 k>1 的策略。

k >1

當 k>1時,即為水塘抽樣。

對於k>1的情況,我們可以採用類似的思考策略:

有了k =1 的理解,我們可以直接替換結論,只需把上面的 1/n 變為 k/n 即可。策略如下:

在取第n個資料的時候,我們生成乙個0到1的隨機數p,如果p小於等於 k/n,替換池中任意乙個為第n個數。大於k/n,繼續保留前面的數。直到資料流結束,返回此k個數。但是為了保證計算機計算分數額準確性,一般是生成乙個0到n的隨機數,跟k相比,道理是一樣的。

可以以同樣的方法證明。

1)初始情況k<=n,出現在水庫中的k個元素的出現概率都是一致的,都是1

2)第一步。第一步就是指,處理第k+1個元素的情況。分兩種情況:元素全部都沒有被替換;其中某個元素被第k+1個元素替換掉。

我們先看情況2:第k+1個元素被選中的概率是k/(k+1)(根據公式k/i),所以這個新元素在水塘**現的概率就一定是k/(k+1)(不管它替換掉哪個元素,反正肯定它是以這個概率出現在水塘中)。

下面來看水塘中剩餘的元素出現的概率,也就是1-p(這個元素被替換掉的概率)。水塘中任意乙個元素被替換掉的概率是:(k/k+1)*(1/k)=1/(k+1),意即首先要第k+1個元素被選中,然後自己在集合的k個元素中被選中。

那它出現的概率就是1-1/(k+1)=k/(k+1)。可以看出來,舊元素和新元素出現的概率是相等的。

情況1:當元素全部都沒有替換掉的時候,每個元素的出現概率肯定是一樣的,這很顯然。但具體是多少呢?就是1-p(第k+1個元素被選中)=1-k/(k+1)=1/(k+1)。

3)歸納法:重複上面的過程,只要證明第i步到第i+1步,所有元素出現的概率是相等的即可。

**

1 vector reservoirsampling(vector& results, vector& nums, intk)2

1011

for (int i=k; ii) 16}

1718

return

results;

19 }

演算法 蓄水池抽樣

例題 有乙個機器按自然數序列的方式吐出球,1號球,2號球.現有乙個袋子,袋子裡最多只能裝下k個球,並且除袋子以外沒有更多的空間,球扔掉不能放回。設計一種選擇方式,使得當機器吐出第n號球時,袋子中的球數是k個,同時可以保證從1號球到n號球中的每乙個被選中進袋子的概率都是k n。具體例子 有乙個只能裝下...

蓄水池抽樣演算法

給你乙個長度為n的鍊錶。n很大,但你不知道n有多大。你的任務是從這n個元素中隨機取出k個元素。你只能遍歷這個鍊錶一次。你的演算法必須保證取出的元素恰好有k個,且它們是完全隨機的 出現概率均等 蓄水池抽樣演算法 該演算法是針對從乙個序列中隨機抽取不重複的k個數,保證每個數被抽取到的概率為k n這個問題...

蓄水池抽樣演算法

題目 要求從n個元素中隨機的抽取k個元素,其中n無法確定 解法 首先選擇n中的前k個數加入 蓄水池 中,然後從第k 1個數開始,以k k i i 1,2,3.的概率選擇這個數,然後在蓄水池中隨機選擇乙個數,並將其替換,n個元素遍歷完畢後,蓄水池中的k個數就是隨機選擇的。證明 這裡即需要證明每個數出現...