1. 程式人生 > >Flajolet-Martin演算法及其應用

Flajolet-Martin演算法及其應用

來源:http://www.pluscn.net/?p=1192

對應的程式碼(maybe):https://github.com/graphlab-code/graphlab/blob/master/toolkits/graph_analytics/approximate_diameter.cpp

近似計算,求解集合的大小

在實際應用中,我們經常碰到這種情況,即要統計某個物件或者事件獨立出現的次數。對於較小的資料量,這很容易解決,我們可以首先在記憶體中對序列進行排序,然後掃描有序序列統計獨立元素數目。其中排序時間複雜度為O(n*log(n)),掃描時間複雜度為O(n),所以總的時間複雜度為O(n*log(n))。當記憶體非常充裕時,我們還可以考慮使用雜湊,將時間複雜度降到O(n)。尤其是當元素只能取有限範圍的整數值時,我們還可以使用BitMap節約記憶體。但是在處理資料流序列時,比如,google的獨立訪問IP統計,由於序列非常長,元素取值範圍可能比較廣,單個元素佔用記憶體可能比較多,導致記憶體中無法容納整個序列,甚至無法容納整個獨立元素集合。此時,不論是基於排序還是基於雜湊的方法都不具備可行性。

Flajolet-Martin(FM)演算法能夠較好地解決估算資料流序列中獨立元素數目的問題。

假設我們有1萬個int型數字(可重複的),我們想找出這個數字集合中不重複的數字的個數。怎麼辦呢?很簡單,將這1萬個數字讀進記憶體,存放到hashset中,那麼hashset的size就是不重複數字的個數。接下來,問題變得更加的複雜,有100億個數字,怎麼辦? 全部讀取到記憶體中可能會有問題,如果這其中有1億個不重複的數字,那麼至少需要記憶體 100M * sizeof(int),記憶體也許不夠。 FM演算法就是為了解決這個問題。假設n個object,其中有m個唯一的,那麼FM演算法只需要log(m)的記憶體佔用(實際操作中會是k*log(m)),以及O(n)的運算時間。當然,FM的問題是,它的結果只是一個估計值,不是精確結果。

具體思路如下:

假定雜湊函式H(e)能夠把元素e對映到[0, 2^m-1]區間上;再假定函式TailZero(x)能夠計算正整數x的二進位制表示中末尾連續的0的個數,譬如TailZero(2) = TailZero(0010) = 1,TailZero(8) = TailZero(1000) = 3,TailZero(10) = TailZero(1010) = 1;我們對每個元素e計算TailZero(H(e)),並求出最大的TailZero(H(e))記為Max,那麼對於獨立元素數目的估計為2^Max。

這種估算的理論依據證明參見  原文

舉例來說,給定序列{e1, e2, e3, e2},獨立元素數目N = 3。假設給定雜湊函式H(e),有:

H(e1) = 2 = 0010,TailZero(H(e1)) = 1

H(e2) = 8 = 1000,TailZero(H(e2)) = 3

H(e3) = 10 = 1010,TailZero(H(e3)) = 1

第1步,將Max初始化為0;

第2步,對於序列中第1項e1,計算TailZero(H(e1)) = 1 > Max,更新Max = 1;

第3步,對於序列中第2項e2,計算TailZero(H(e2)) = 3 > Max,更新Max = 3;

第4步,對於序列中第3項e3,計算TailZero(H(e3)) = 1 ≤ Max,不更新Max;

第5步,對於序列中第4項e2,計算TailZero(H(e2)) = 3 ≤ Max,不更新Max;

第6步,估計獨立元素數目為N’ = 2^Max = 2^3 = 8。

在這個簡單例子中,實際值N = 3,估計值N’ = 8,誤差比較大。此外,估計值只能取2的乘方,精度不夠高。

在實際應用中,為了減小誤差,提高精度,我們通常採用一系列的雜湊函式H1(e), H2(e), H3(e)……,計算一系列的Max值Max1, Max2, Max3……,從而估算一系列的估計值2^Max1, 2^Max2, 2^Max3……,最後進行綜合得到最終的估計值。具體做法是:首先設計A*B個互不相同的雜湊函式,分成A組,每組B個雜湊函式;然後利用每組中的B個雜湊函式計算出B個估計值;接著求出B個估計值的算術平均數為該組的估計值;最後選取各組的估計值的中位數作為最終的估計值。

舉例來說,對於序列S,使用3*4 = 12個互不相同的雜湊函式H(e),分成3組,每組4個雜湊函式,使用12個H(e)估算出12個估計值:

第1組的4個估計值為<2, 2, 4, 4>,算術平均值為(2 + 2 + 4 + 4) / 4 = 3;

第2組的4個估計值為<8, 2, 2, 2>,算術平均值為(8 + 2 + 2 + 2) / 4 = 3.5;

第3組的4個估計值為<2, 8, 8, 2>,算術平均值為(2 + 8 + 8 + 2) / 4 = 5;

3個組的估計值分別為<3, 3.5, 5>,中位數為3.5;

因此3.5 ≈ 4即為最終的估計值。

分析FM演算法的時間複雜度。假定序列長度為N,雜湊函式H(e)的數目為K。初始化K個Max值的時間複雜度為O(K);對N個元素e使用K個雜湊函式H(e)計算TailZero(H(e))並更新Max值的時間複雜度為O(N*K);綜合K個Max值給出最終估計值的時間複雜度為O(K)。因此總的時間複雜度為O(N*K)。

分析FM演算法的空間複雜度。該演算法需要儲存K個Max值,而每個元素e在進行相關計算後就可以丟掉。因此總的空間複雜度為O(K)。

綜上所述,FM演算法的時間複雜度為O(N*K),空間複雜度為O(K)。一般來說K比較小,可以認為FM演算法的時間複雜度為O(N),空間複雜度為O(1)。

FM演算法可以用於估算獨立Cookie數目,獨立URL數目,獨立郵箱地址數目等等。

參考源: