水塘抽樣演算法

2022-07-03 13:36:12 字數 2380 閱讀 7407

作用:水塘抽樣演算法是一種抽樣演算法,對於乙個很大的集合,抽取的樣本值能夠保證隨機.

特點:其複雜度並不很高o(n),並且能夠很大程度地節省記憶體.

很多大公司的面試題都考察過這個演算法,以谷歌為例,有一道關於水塘抽樣的例題

我有乙個長度為n的鍊錶,n的值非常大,我不清楚n的確切值.我怎樣能寫乙個盡可能高效地演算法來返回k個完全隨機的數.

這道題有兩個限制:

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

2:盡量隨機地返回值

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

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

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

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

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

之前想到的載入到記憶體就不太適合了,但是還可以想到別的辦法,比如每次讀取一行記錄載入到記憶體,記數+1,清空記憶體中行資料,直到最後統計一共多少行,然後根據總行數來計算k個隨機數.如何再取回行對應的資料呢?我們可以再遍歷一遍,一邊遍歷一邊記錄這一行的號碼是不是在k個隨機數中,如果是,則將該行內容保留.

這樣的話遍歷兩次應該可以做到,但是1t資料遍歷兩次的時間消耗是非常高的.

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

具體例子

我們先從具體案例中理解水塘抽樣演算法的實現,再從抽象的角度來理解.

假如10000個數,我們要抽取十個隨機數.

一萬個數的樣本集合陣列記作s.

十個隨機數的陣列記作r,代表result.

先取陣列s中前十個數填充進陣列r.

演算法的第一次迭代流程是這樣的:

遍歷完成生成的r陣列,就是我們要求的隨機數組.

抽象概念

$ s[n] $記作:樣本集合

$ r[k] $記作:結果集合

$ n $記作:s陣列大小

\(j\)記作:每次的隨機數

\(k\)記作:前k個隨機數

\(i\)記作:迭代次數.

步驟

int s = new int[10000];

int n = s.length;

random random = new random();

//生成一萬個數的陣列

for (int r = 0;r < n; r ++)

int k = 10;

int r = new int[k];

//s前k個數填充r陣列

for (int f = 0;f < k; f++)

int j ;

//遍歷陣列s,根據演算法,替換r陣列中的元素,最終生成結果r陣列.

for (int i = k;i < s.length;i++)

//列印r陣列的結果

for (int i =0;i < r.length;i++)

總結一下這種演算法.通過一遍遍歷就獲得了k個隨機數,在很大資料的情況下效率是非常高的,非常適合我們的應用場景.

但是為什麼這樣生成的數是完全隨機的呢?

就剛才的具體例子來講,第一次遍歷時,i=10,隨機數的範圍是0到10共11個數,那麼不替換的概率是\(10/11\),等到第二次迭代時,不替換的概率變成\(10/12\),第三次\(10/13\),第四次\(10/14\).......

這樣看來好像每一次的概率並不相等,其實並不是這樣,我們要看的是最終進入陣列\(r\)中的概率,雖然第十乙個數進入\(r\)的概率比較大,但是到最後他被替換的概率也很大,所以每個數最終保留在\(r\)中的概率到底是多少呢?

可以參考一下維基百科中的證明,我覺得非常清晰.

在迴圈內第n行被抽取的機率為k/n,以\(p_n\)表示。如果檔案共有n行,任意第n行(注意這裡n是序號,而不是總數).

被抽取的機率為:

我們可以求得每行被抽取的概率是相同的,等於\(k/n\).

非常巧妙,所以當我們面對這種情景時,可以考慮使用水塘抽樣進行隨機抽取.

計算引擎 水塘抽樣演算法

spark中的分割槽器有三種 1.hashpartitioner分割槽可能hashpartitioner導致每個分割槽中資料量的不均勻。2.rangepartitioner分割槽盡量保證每個分割槽中資料量的均勻,將一定範圍內的數對映到某乙個分區內。分割槽與分割槽之間資料是有序的,但分區內的元素是不能...

水塘抽樣(Reservoir sampling)

水塘抽樣 reservoir sampling 題目 給出乙個資料流,這個資料流的長度很大或者未知。並且對該資料流中的資料只能訪問一次。請寫出乙個隨機選擇演算法,使得資料流中所有資料被選中的概率相等。這個問題的擴充套件就是 如何從未知或者很大樣本空間隨機的取k個數?或者說,資料流長度為n行,要隨機抽...

Shuffle an Array 水塘抽樣

隨機性問題 水塘抽樣演算法可保證每個樣本被抽到的概率相等 使用場景 從包含n個專案的集合s中選取k個樣本,其中n為一很大或未知的數量,尤其適用於不能把所有n個專案都存放到主記憶體的情況 拿起第i張牌時,只從它前面的牌隨機選出j,或從它後面的牌隨機選出j交換即可 1 class solution 67...