1. 程式人生 > >【布隆過濾器】實現一個簡單的布隆過濾器

【布隆過濾器】實現一個簡單的布隆過濾器

原理

布隆過濾器(Bloom Filter)是1970年由布隆提出的。它實際上是一個很長的二進位制向量和一系列隨機對映函式。布隆過濾器可以用於檢索一個元素是否在一個集合中。它的優點是空間效率和查詢時間都遠遠超過一般的演算法,缺點是有一定的誤識別率和刪除困難。

Bloom Filter 是一種空間效率很高的隨機資料結構,Bloom filter 可以看做是對 bit-map 的擴充套件, 它的原理是:

當一個元素被加入集合時,通過 K 個 Hash 函式將這個元素對映成一個位陣列(Bit array)中的 K 個點,把它們置為 1。檢索時,我們只要看看這些點是不是都是 1 就(大約)知道集合中有沒有它了:

  • 如果這些點有任何一個 0,則被檢索元素一定不在;
  • 如果都是 1,則被檢索元素很可能在。

優缺點

優點

它的優點是空間效率和查詢時間都遠遠超過一般的演算法,布隆過濾器儲存空間和插入 / 查詢時間都是常數O(k)。另外, 雜湊函式相互之間沒有關係,方便由硬體並行實現。布隆過濾器不需要儲存元素本身,在某些對保密要求非常嚴格的場合有優勢。

缺點

但是布隆過濾器的缺點和優點一樣明顯。誤算率是其中之一。隨著存入的元素數量增加,誤算率隨之增加。但是如果元素數量太少,則使用散列表足矣。

(誤判補救方法是:再建立一個小的白名單,儲存那些可能被誤判的資訊。)

另外,一般情況下不能從布隆過濾器中刪除元素. 我們很容易想到把位陣列變成整數陣列,每插入一個元素相應的計數器加 1, 這樣刪除元素時將計數器減掉就可以了。然而要保證安全地刪除元素並非如此簡單。首先我們必須保證刪除的元素的確在布隆過濾器裡面. 這一點單憑這個過濾器是無法保證的。另外計數器迴繞也會造成問題。

應用

布隆過濾器在很多場合能發揮很好的效果,比如:網頁URL的去重,垃圾郵件的判別,集合重複元素的判別,查詢加速(比如基於key-value的儲存系統)等,下面舉幾個例子:
url去重
A,B 兩個檔案,各存放 50 億條 URL,每條 URL 佔用 64 位元組,記憶體限制是 4G,讓你找出 A,B 檔案共同的 URL。如果是三個乃至 n 個檔案呢?

分析 :如果允許有一定的錯誤率,可以使用 Bloom filter,4G 記憶體大概可以表示 340 億 bit。將其中一個檔案中的 url 使用 Bloom filter 對映為這 340 億 bit,然後挨個讀取另外一個檔案的 url,檢查是否與 Bloom filter,如果是,那麼該 url 應該是共同的 url(注意會有一定的錯誤率)。”

垃圾郵件

假定我們儲存一億個電子郵件地址,我們先建立一個十六億二進位制(位元),即兩億位元組的向量,然後將這十六億個二進位制全部設定為零。對於每一個電子郵件地址 X,我們用八個不同的隨機數產生器(F1,F2, …,F8) 產生八個資訊指紋(f1, f2, …, f8)。再用一個隨機數產生器 G 把這八個資訊指紋對映到 1 到十六億中的八個自然數 g1, g2, …,g8。現在我們把這八個位置的二進位制全部設定為一。當我們對這一億個 email 地址都進行這樣的處理後。一個針對這些 email 地址的布隆過濾器就建成了。

實現布隆過濾器

common.h檔案中

/雜湊函式
template<class K>
class HashFunDef
{
public:
    size_t operator()(const K& key)
    {
        return key;
    }
};

//string轉化為數字
static size_t BKDRHash(const char * str)
{
    unsigned int seed = 131; // 31 131 1313 13131 131313
    unsigned int hash = 0;
    while (*str)
    {
        hash = hash * seed + (*str++);
    }
    return (hash & 0x7FFFFFFF);
}


template<>
class HashFunDef<string>
{
public:
    size_t operator()(const string& key)
    {
        return BKDRHash(key.c_str());
    }
};


size_t SDBMHash(const char* str)
{
    register size_t hash = 0;
    while (size_t ch = (size_t)*str++)
    {
        hash = 65599 * hash + ch;
        //hash = (size_t)ch+(hash<<6)+ (hash<<16)-hash;
    }

    return hash;
}

size_t RSHash(const char *str)
{
    register size_t hash = 0;
    size_t magic = 63689;
    while (size_t ch = (size_t)*str++)
    {
        hash = hash * magic + ch;
        magic *= 378551;
    }

    return hash;
}

size_t APHash(const char* str)
{
    register size_t hash = 0;
    size_t ch;
    for (long i = 0; ch = (size_t)*str++; i++)
    {
        if (0 == (i & 1))
        {
            hash ^= ((hash << 7) ^ (hash >> 3));
        }
        else
        {
            hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));
        }
    }

    return hash;
}

size_t JSHash(const char* str)
{
    if (!*str)
        return 0;

    register size_t hash = 1315423911;
    while (size_t ch = (size_t)*str++)
    {
        hash ^= ((hash << 5) + ch + (hash >> 2));
    }

    return hash;
}

template<class K>
struct __HashFunc1
{
    size_t operator()(const K& key)
    {
        return BKDRHash(key.c_str());
    }
};

template<class K>
struct __HashFunc2
{
    size_t operator()(const K& key)
    {
        return SDBMHash(key.c_str());
    }
};

template<class K>
struct __HashFunc3
{
    size_t operator()(const K& key)
    {
        return RSHash(key.c_str());
    }
};

template<class K>
struct __HashFunc4
{
    size_t operator()(const K& key)
    {
        return APHash(key.c_str());
    }
};

template<class K>
struct __HashFunc5
{
    size_t operator()(const K& key)
    {
        return JSHash(key.c_str());
    }
};

實現布隆過濾器

template<class K, class HashFunc = __HashFunc1<string>>
class BloomFile
{
public:
    BloomFile(size_t size)
        :_map(size)
    {}
    bool Insert(string str)
    {
        size_t idx1 = __HashFunc1()(str);
        size_t idx2 = __HashFunc2()(str);
        size_t idx3 = __HashFunc3()(str);
        size_t idx4 = __HashFunc4()(str);
        size_t idx5 = __HashFunc5()(str);
        _map.Set(idx1); _map.Set(idx2);
        _map.Set(idx3); _map.Set(idx4);
        _map.Set(idx5);

    }
    bool Find(string str)
    {
        size_t idx1 = __HashFunc1()(str);
        size_t idx2 = __HashFunc2()(str);
        size_t idx3 = __HashFunc3()(str);
        size_t idx4 = __HashFunc4()(str);
        size_t idx5 = __HashFunc5()(str);
        if (!_map.Test(idx1))
            return false;
        if (!_map.Test(idx2))
            return false;
        if (!_map.Test(idx3))
            return false;
        if (!_map.Test(idx4))
            return false;
        if (!_map.Test(idx5))
            return false;
        return true;
    }
private:
    BitMap _map;
};