day25之布隆过滤器的实现和优缺点以及扩展

来源:互联网 发布:软件开发电脑配置 编辑:程序博客网 时间:2024/05/19 08:44

布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。

代码实现:

//bitMap.hclass BitMap{public:    BitMap()    {}    BitMap(size_t size)    {        _table.resize((size>>5)+1);    }    // 1    void Set(int data)    {        size_t byteNo = data>>5;        size_t bitNo = data%32;        _table[byteNo] |= (1 << bitNo);     }    // data所在字节的比特位置0    void ReSet(int data)    {        size_t byteNo = data>>5;        size_t bitNo = data%32;        _table[byteNo] &= ~(1<<bitNo);    }    // 检测是否存在    bool Test(int data)    {        size_t byteNo = data>>5;        size_t bitNo = data%32;        if ((1<<bitNo) & _table[byteNo])            return true;        return false;    }private:    std::vector<int> _table;};
//common.h 布隆过滤器用到的散列函数#pragma  once#include<string>#include<iostream>using namespace std;size_t GetNextPrim(size_t prev)//prev = 10{    const int _PrimeSize = 28;    static const unsigned long _PrimeList [_PrimeSize] =    {        53ul,         97ul,         193ul,       389ul,       769ul,        1543ul,       3079ul,       6151ul,      12289ul,     24593ul,        49157ul,      98317ul,      196613ul,    393241ul,    786433ul,        1572869ul,    3145739ul,    6291469ul,   12582917ul,  25165843ul,        50331653ul,   100663319ul,  201326611ul, 402653189ul, 805306457ul,        1610612741ul, 3221225473ul, 4294967291ul    };    for (size_t idx = 0; idx < _PrimeSize; ++idx)    {        if(prev < _PrimeList[idx])            return _PrimeList[idx];    }    return -1;}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);}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;}struct _HashFunc1{    size_t operator()(const string& key)    {        return BKDRHash(key.c_str());    }};struct _HashFunc2{    size_t operator()(const string& key)    {        return SDBMHash(key.c_str());    }};struct _HashFunc3{    size_t operator()(const string& key)    {        return RSHash(key.c_str());    }};struct _HashFunc4{    size_t operator()(const string& key)    {        return APHash(key.c_str());    }};struct _HashFunc5{    size_t operator()(const string& key)    {        return JSHash(key.c_str());    }};
#include "bitMap.h"#include "common.h"//哈希函数的用途就是将其他类型的元素转换为整型。template<class K=string, class Hash1 = _HashFunc1, class Hash2 = _HashFunc1,class Hash3 = _HashFunc3, class Hash4 = _HashFunc4, class Hash5 = _HashFunc5 >class BloomFilter{public:    BloomFilter(int size = 10)    {        _capacity = GetNextPrim(size);        _bitmap = new BitMap(_capacity);  //设置位图大小。    }    void Set(const K & key)    {        _bitmap->Set( Hash1()(key) % _capacity ); //可能通过哈希函数计算的整数超过了位图的位数,所以对求模。        _bitmap->Set( Hash2()(key) % _capacity ); //可能通过哈希函数计算的整数超过了位图的位数,所以对求模。        _bitmap->Set( Hash3()(key) % _capacity ); //可能通过哈希函数计算的整数超过了位图的位数,所以对求模。        _bitmap->Set( Hash4()(key) % _capacity ); //可能通过哈希函数计算的整数超过了位图的位数,所以对求模。        _bitmap->Set( Hash5()(key) % _capacity ); //可能通过哈希函数计算的整数超过了位图的位数,所以对求模。    }    //布隆过滤器只要有一个哈希函数计算到不在位图中,这个元素就不在,相反如果计算后都在也有可能不在    bool Find(const K & key)    {        if ( !_bitmap->Test(Hash1()(key) % _capacity))        {            return false;        }        if ( !_bitmap->Test(Hash2()(key) % _capacity))        {            return false;        }        if ( !_bitmap->Test(Hash3()(key) % _capacity))        {            return false;        }        if ( !_bitmap->Test(Hash4()(key) % _capacity))        {            return false;        }        if ( !_bitmap->Test(Hash5()(key) % _capacity))        {            return false;        }        return true;    }private:    BitMap *_bitmap;    int _capacity;       };

布隆过滤器的优点:
布隆过滤器存储空间 插入和查找都是O(1),另外, Hash函数相互之间没有关系,方便由硬件并行实现。布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势。
缺点:
误算率是其中之一。随着存入的元素数量增加,误算率随之增加
另外,一般情况下不能从布隆过滤器中删除元素

如何扩展BloomFilter使得它支持删除元素的操作?
因为一个布隆过滤器的key对应多个位,冲突的概率比较大,所以不支持删除,因为删除有可能影响到其他元素。如果要对其元素进行删除,就不得不对每一个位进行引用计数。

#include "common.h"//哈希函数的用途就是将其他类型的元素转换为整型。template<class K=string, class Hash1 = _HashFunc1, class Hash2 = _HashFunc1    ,class Hash3 = _HashFunc3, class Hash4 = _HashFunc4, class Hash5 = _HashFunc5 >class BloomFilterCount{public:    BloomFilterCount(int size = 10)    {        _capacity = GetNextPrim(size);        _refbm.resize(_capacity);    }    void Set(const K & key)    {        size_t hash1=( Hash1()(key) % _capacity ); //可能通过哈希函数计算的整数超过了位图的位数,所以对求模。        size_t hash2=( Hash2()(key) % _capacity ); //可能通过哈希函数计算的整数超过了位图的位数,所以对求模。        size_t hash3=( Hash3()(key) % _capacity ); //可能通过哈希函数计算的整数超过了位图的位数,所以对求模。        size_t hash4=( Hash4()(key) % _capacity ); //可能通过哈希函数计算的整数超过了位图的位数,所以对求模。        size_t hash5=( Hash5()(key) % _capacity ); //可能通过哈希函数计算的整数超过了位图的位数,所以对求模。        _refbm[hash1]++;  //对这个位引用计数加加。        _refbm[hash2]++;        _refbm[hash3]++;        _refbm[hash4]++;        _refbm[hash5]++;    }    void ReSit(const K &key)    {        size_t hash1=( Hash1()(key) % _capacity );        size_t hash2=( Hash2()(key) % _capacity );        size_t hash3=( Hash3()(key) % _capacity );        size_t hash4=( Hash4()(key) % _capacity );        size_t hash5=( Hash5()(key) % _capacity );        _refbm[hash1]--;  //对这个位引用计数减减。        _refbm[hash2]--;        _refbm[hash3]--;        _refbm[hash4]--;        _refbm[hash5]--;    }    //布隆过滤器只要有一个哈希函数计算到不在位图中,这个元素就不在    bool Find(const K & key)    {        size_t hash1=( Hash1()(key) % _capacity );        size_t hash2=( Hash2()(key) % _capacity );        size_t hash3=( Hash3()(key) % _capacity );        size_t hash4=( Hash4()(key) % _capacity );        size_t hash5=( Hash5()(key) % _capacity );        if (_refbm[hash1] <= 0)            return false;        if (_refbm[hash2] <= 0)            return false;        if (_refbm[hash3] <= 0)            return false;        if (_refbm[hash4] <= 0)            return false;        if (_refbm[hash5] <= 0)            return false;        return true;    }//我们都知道,位图非常的节省空间,但由于每一位都要引入一个int,所以空间浪费还是比较严重的,    //因此不得不放弃位图了private:       vector<size_t> _refbm;       size_t _capacity;};
原创粉丝点击