極度節約內存的算法 --位圖

位圖:
網頁爬蟲,獲取頁面中的url,進一步爬取內容。但是需要避免重複爬取,所以需要查詢是否已經爬取並保存到查詢記錄中。
如果有10億個url,怎麼處理?
hash -耗內存-> 位圖 -位太多-> 布隆  --> 多hash


散列表
10億Url 一個url 50字節,共500億字節。
50G內存:分片,分到10個機器,每個機器5G,可以接受。
分片 + 散列表。

如果用鏈表處理衝突,那麼:
1) 鏈表對CPu緩存不友好
2) 鏈表url和判重的url需要逐個比較,大小50字節的字符串比較,耗時

節約內存的方案-布隆過濾器,基於位圖
問題:1千萬個整數,整數範圍1-1億,如何快速查找該整數是否在1千萬個整數之中呢?
解決:申請1億,數據類型位boolean的數組;將該1千萬個整數作爲下標,對應的數組值設置爲true。如存在整數5,則array[5]=true;
當查詢k是否在1千萬整數之中時,去除array[k],如果值爲true,則存在,否則不存在

位圖:
boolean是1字節,本質上一個二進制位(bit)就可以了,如何用編程語言表示二進制呢?
假設用char來處理,一個char是2字節,那麼可以用來表示16個布爾值,如果想設置第n(1-16)位爲1,只需要
char |= 1>>n
public class BitMap{
    char[] bytes;//2字節
    int nBits;

    public BitMap(int nBits){
        this.nBits = nBits;
        bytes = new char[nBits/16 + 1]
    }

    public void set(n){
        if(n > nBits) return;
        int bitIndex = n%16;
        int byteIndex = n/16;
        char[byteIndex] |= (1<<bitIndex);
    }

    public boolean get(n){
        if(n > nBits) return;
        int bitIndex = n%16;
        int byteIndex = n/16;
        return char[byteIndex] & (1<<bitIndex) !=0;
    }
}

如果整數範圍不是1-1億,而是1-10億,那數組就會需要擴大10倍,會多浪費很多空間。
布隆過濾器用於解決剛剛的問題。
還是剛剛問題,但是數據範圍是1-10億,那布隆過濾器的做法是:
仍然使用1億個二進制大小的位圖,那此時會存在如下場景,數字1/和數字1億+1豈不是放到相同的位置了?
這裏布隆過濾器的做法是:
放入某值n:使用 K 個哈希函數,對同一個數字進行求哈希值,那會得到 K 個不同的哈希值,我們分別記作 X1​,X2​,X3​,…,XK​。我們把這 K 個數字作爲位圖中的下標,將對應的 BitMap[X1​],BitMap[X2​],BitMap[X3​],…,BitMap[XK​] 都設置成 true,也就是說,我們用K個二進制位,來表示一個數字的存在。
查詢值n是否存在:我們用同樣的 K 個哈希函數,對這個數字求哈希值,分別得到 Y1​,Y2​,Y3​,…,YK​。我們看這 K 個哈希值,對應位圖中的數值是否都爲 true,如果都是 true,則說明,這個數字存在,如果有其中任意一個不爲 true,那就說明這個數字不存在。
但是會存在誤判的場景,例如a、b、c三個值,
其中a的hash結果爲h1,h2,h3,b的hash結果爲h2,h3,h4;c的hash結果爲h1,h2,h4;
那麼僅放入a、b,不放入c,此時那c取判斷,會得到錯誤的結果:c已放入。
布隆的誤判僅僅有一個方面,未放入判定爲放入了,如果其結果是未放入,那該結果一定是準確的。

布隆過濾器適用場景:適用於判定結果不要求完全準確的大規模判重場景。

1億個整數,範圍1-10億,如何快速排序?
使用用大小爲10億的boolean數組,遍歷存在置爲true,
如果數據上面1億個數據有重複的怎麼辦?
出現兩次及以上的:
可以單獨維護一個散列表存儲數值,以及出現的次數。放入之前判斷現在是否存在,已存在則維護該散列表。

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