1. 程式人生 > >點陣圖和布隆過濾器

點陣圖和布隆過濾器

給四十億個不重複的無符號整數,再給你一個無符號整數,如何快速
判斷一個數是否在這四十億個數中?

1byte=8bit;
1kB=1024byte;
1MB=1024KB;
1GB=1024MB;

1GB大約有10億多個位元組,而40億個無符號整數有160億個位元組,所以要存放下40億
個無符號整數大約需要16GB空間,然而我們的電腦記憶體沒有這麼大,我們需要想一種方法
來解決這個問題?

對於這種問題我們可以使用點陣圖,我們沒有必要用四個位元組來存放一個數,因為我們
只用判斷一個數是否在這40億個數中,我們可以把一個數存放在一個位元位上,1表示
存在,0表示不存在,這樣這40億個數就需要大約500M空間,當要判斷的時候我們
可以算出這個數在bitmap中的下標,1表示存在,0表示不存在,這樣我們就完成這個問題了。

//點陣圖
//點陣圖
#pragma once
class BitMap
{
public:
    BitMap(size_t range)
    {
        _Bitmap.resize(range / 8+1, 0); //設定點陣圖大小
    }
    void Set(size_t value)//置1
    {
        size_t index = value >> 3;
        size_t pos = value % 8;
        _Bitmap[index] |= (1 << pos);
    }
    void Reset(size_t value) //置0
{ size_t index = value >> 3; size_t pos = value % 8; _Bitmap[index] &= (~(1 << pos)); } bool Test(size_t value) { size_t index = value >> 3; size_t pos = value % 8; return _Bitmap[index] & (1 << pos); } protected
: vector<char> _Bitmap; }; void TestBitMap() { BitMap bt(-1); bt.Set(1); bt.Set(12); bt.Set(123); bt.Set(1234); bt.Set(12345); cout << bt.Test(0) << endl; cout << bt.Test(1) << endl; cout << bt.Test(12) << endl; cout << bt.Test(123) << endl; cout << bt.Test(1234) << endl; cout << bt.Test(12345) << endl; cout << bt.Test(321) << endl; bt.Reset(1234); bt.Reset(12345); cout << endl; cout << bt.Test(0) << endl; cout << bt.Test(1) << endl; cout << bt.Test(12) << endl; cout << bt.Test(123) << endl; cout << bt.Test(1234) << endl; cout << bt.Test(12345) << endl; cout << bt.Test(321) << endl; }

點陣圖只能用來快速判斷一個整數是否在一堆整數中,如果我們想要判斷一個字串
是否在一堆字串裡,那麼點陣圖就做不到了,因此布隆過濾器就出現了。

布隆過濾器:布隆過濾器其實是結合了點陣圖與雜湊表,先將字串用字串雜湊演算法
對映到雜湊表中,但是由於雜湊衝突,我們可以一個字串使用多個不同的字串雜湊
演算法同時對映在這個雜湊表中,判斷一個字串是否在這堆字串中,我們可以算出
這個字串的位置,當且僅當這個字串每個對映位置都是1才表示存在,只要有一個
位置為0,就表示不存在;

#pragma once
#include "BitMap.h"

struct __HashFunc1
{
    size_t BKDRHash(const char *str)//雜湊演算法
    {          //這裡的雜湊演算法都是取至連結當中
        register size_t hash = 0;
        while (size_t ch = (size_t)*str++)
        {
            hash = hash * 131 + ch;
        }
        return hash;
    }
    size_t operator()(const string& s)
    {
        return BKDRHash(s.c_str());
    }
};
struct __HashFunc2
{
    size_t SDBMHash(const char *str)
    {
        register size_t hash = 0;
        while (size_t ch = (size_t)*str++)
        {
            hash = 65599 * hash + ch;

        }
        return hash;
    }
    size_t operator()(const string& s)
    {
        return SDBMHash(s.c_str());
    }
};
struct __HashFunc3
{
    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 operator()(const string& s)
    {
        return RSHash(s.c_str());
    }
};
struct __HashFunc4
{
    size_t APHash(const char *str)
    {
        register size_t hash = 0;
        size_t ch;
        for (long i = 0; ch = (size_t)*str++; i++)
        {
            if ((i & 1) == 0)
            {
                hash ^= ((hash << 7) ^ ch ^ (hash >> 3));
            }
            else
            {
                hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));
            }
        }
        return hash;
    }
    size_t operator()(const string& s)
    {
        return  APHash(s.c_str());
    }
};
struct __HashFunc5
{
    size_t JSHash(const char *str)
    {
        if (!*str)        // 這是由本人新增,以保證空字串返回雜湊值0  
            return 0;
        register size_t hash = 1315423911;
        while (size_t ch = (size_t)*str++)
        {
            hash ^= ((hash << 5) + ch + (hash >> 2));
        }
        return hash;
    }
    size_t operator()(const string& s)
    {
        return JSHash(s.c_str());
    }
};

template<class K=string
    ,class HashFunc1=__HashFunc1
    , class HashFunc2=__HashFunc2
    , class HashFunc3=__HashFunc3
    ,class HashFunc4=__HashFunc4
    ,class HashFunc5=__HashFunc5>
class BloomFilter
{
public:
    BloomFilter(size_t size)
        :_bitmap(size * 5) 
        , range(size * 5)
    {}
    void Setb(const K& key)
    {
        size_t hash1 = HashFunc1()(key) % range;
        size_t hash2 = HashFunc2()(key) % range;
        size_t hash3 = HashFunc3()(key) % range;
        size_t hash4 = HashFunc4()(key) % range;
        size_t hash5 = HashFunc5()(key) % range;
        //為了減少誤判,提高精度,一個數對映到5個位置
        _bitmap.Set(hash1);
        _bitmap.Set(hash2);
        _bitmap.Set(hash3);
        _bitmap.Set(hash4);
        _bitmap.Set(hash5);
    }
    bool Test(const K& key)
    {
        size_t hash1 = HashFunc1()(key) % range;
        size_t hash2 = HashFunc2()(key) % range;
        size_t hash3 = HashFunc3()(key) % range;
        size_t hash4 = HashFunc4()(key) % range;
        size_t hash5 = HashFunc5()(key) % range;
        if (_bitmap.Test(hash1) == false)
            return false;
        if (_bitmap.Test(hash2) == false)
            return false;
        if (_bitmap.Test(hash3) == false)
            return false;
        if (_bitmap.Test(hash4) == false)
            return false;
        if (_bitmap.Test(hash5) == false)
            return false;
        return true;
    }
protected:
    BitMap _bitmap;
    size_t range;
};

void TestBloomFilter()
{
    BloomFilter<> bf(1024);
    bf.Setb("abcd");
    bf.Setb("1234");
    bf.Setb("qwer");
    bf.Setb("zxcv");
    bf.Setb("mlop");
    cout << bf.Test("0123") << endl;
    cout << bf.Test("abcd") << endl;
    cout << bf.Test("1234") << endl;
    cout << bf.Test("zxcv") << endl;
    cout << bf.Test("mlop") << endl;
    cout << bf.Test("4569") << endl;
}

上邊這個布隆演算法節省空間但是不支援刪除演算法,因為上邊那個演算法有可能一個位置
映射了幾個數,刪除了一個數可能會影響到別的數;

如果我們想要使用刪除演算法,我們可以使用引用計數的方法,那麼存放一個數的位置
就不能用一個位元位了,而是可以用一個無符號整數來存放,當刪除一個數的時候,
如果它對映到的每個位置都大於0,就表明這個數存在,那麼就讓這幾個數同時減1;

因為這個演算法為了使用刪除演算法,浪費了一些空間,適用於少量的資料

#pragma once

struct __HashFunc1
{
    size_t BKDRHash(const char *str)
    {
        register size_t hash = 0;
        while (size_t ch = (size_t)*str++)
        {
            hash = hash * 131 + ch;
        }
        return hash;
    }
    size_t operator()(const string& s)
    {
        return BKDRHash(s.c_str());
    }
};
struct __HashFunc2
{
    size_t SDBMHash(const char *str)
    {
        register size_t hash = 0;
        while (size_t ch = (size_t)*str++)
        {
            hash = 65599 * hash + ch;

        }
        return hash;
    }
    size_t operator()(const string& s)
    {
        return SDBMHash(s.c_str());
    }
};
struct __HashFunc3
{
    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 operator()(const string& s)
    {
        return RSHash(s.c_str());
    }
};
struct __HashFunc4
{
    size_t APHash(const char *str)
    {
        register size_t hash = 0;
        size_t ch;
        for (long i = 0; ch = (size_t)*str++; i++)
        {
            if ((i & 1) == 0)
            {
                hash ^= ((hash << 7) ^ ch ^ (hash >> 3));
            }
            else
            {
                hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));
            }
        }
        return hash;
    }
    size_t operator()(const string& s)
    {
        return  APHash(s.c_str());
    }
};
struct __HashFunc5
{
    size_t JSHash(const char *str)
    {
        if (!*str)        // 這是由本人新增,以保證空字串返回雜湊值0  
            return 0;
        register size_t hash = 1315423911;
        while (size_t ch = (size_t)*str++)
        {
            hash ^= ((hash << 5) + ch + (hash >> 2));
        }
        return hash;
    }
    size_t operator()(const string& s)
    {
        return JSHash(s.c_str());
    }
};

template<class K = string
    , class HashFunc1 = __HashFunc1
    , class HashFunc2 = __HashFunc2
    , class HashFunc3 = __HashFunc3
    , class HashFunc4 = __HashFunc4
    , class HashFunc5 = __HashFunc5>
class BoolmFilter
{
public:
    BoolmFilter(size_t range)
    {
        _bitmap.resize(range * 5, 0);
    }
    void Setb(const K& value)
    {
        size_t range = _bitmap.size();
        size_t hash1 = HashFunc1()(value) % range;
        size_t hash2 = HashFunc2()(value) % range;
        size_t hash3 = HashFunc3()(value) % range;
        size_t hash4 = HashFunc4()(value) % range;
        size_t hash5 = HashFunc5()(value) % range;
        _bitmap[hash1]++;
        _bitmap[hash2]++;
        _bitmap[hash3]++;
        _bitmap[hash4]++;
        _bitmap[hash5]++;
    }
    bool Reset(const K& value)
    {
        size_t range = _bitmap.size();
        size_t hash1 = HashFunc1()(value) % range;
        size_t hash2 = HashFunc2()(value) % range;
        size_t hash3 = HashFunc3()(value) % range;
        size_t hash4 = HashFunc4()(value) % range;
        size_t hash5 = HashFunc5()(value) % range;
        if (_bitmap[hash1] == 0
            || _bitmap[hash2] == 0
            || _bitmap[hash3] == 0
            || _bitmap[hash4] == 0
            || _bitmap[hash5] == 0)
        {
            return false; //如果這幾個位置任意一個為0表示不存在不能刪除
        }
        _bitmap[hash1]--;
        _bitmap[hash2]--;
        _bitmap[hash3]--;
        _bitmap[hash4]--;
        _bitmap[hash5]--;
        return true;
    }
    bool Test(const K& value)
    {
        size_t range = _bitmap.size();
        size_t hash1 = HashFunc1()(value) % range;
        size_t hash2 = HashFunc2()(value) % range;
        size_t hash3 = HashFunc3()(value) % range;
        size_t hash4 = HashFunc4()(value) % range;
        size_t hash5 = HashFunc5()(value) % range;
        if (_bitmap[hash1] != 0
            && _bitmap[hash2] != 0
            && _bitmap[hash3] != 0
            && _bitmap[hash4] != 0
            && _bitmap[hash5] != 0)
        {
            return true;
        }
        return false;
    }
protected:
    vector<size_t> _bitmap;  //存放無符號整形
};

void TestBloomFilter()
{
    BoolmFilter<> bf(1024);
    bf.Setb("abcd");
    bf.Setb("1234");
    bf.Setb("qwer");
    bf.Setb("zxcv");
    bf.Setb("mlop");
    cout << bf.Test("0123") << endl;
    cout << bf.Test("abcd") << endl;
    cout << bf.Test("1234") << endl;
    cout << bf.Test("zxcv") << endl;
    cout << bf.Test("mlop") << endl;
    cout << bf.Test("4569") << endl;
    bf.Reset("zxcv");
    bf.Reset("mlop");
    cout << endl;
    cout << bf.Test("0123") << endl;
    cout << bf.Test("abcd") << endl;
    cout << bf.Test("1234") << endl;
    cout << bf.Test("zxcv") << endl;
    cout << bf.Test("mlop") << endl;
    cout << bf.Test("4569") << endl;
}