作用:水塘抽樣演算法是一種抽樣演算法,對於乙個很大的集合,抽取的樣本值能夠保證隨機.
特點:其複雜度並不很高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...