布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
代码实现:
//bitMap.h
class 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<// 检测是否存在
bool Test(int data)
{
size_t byteNo = data>>5;
size_t bitNo = data%32;
if ((1<return true;
return false;
}
private:
std::vector<int> _table;
};
//common.h 布隆过滤器用到的散列函数
#pragma once
#include
#include
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 _refbm;
size_t _capacity;
};