布隆过滤器【BloomFilter】

时间:2021-06-27 20:55:51

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

布隆过滤器一般是用来判断字符串是否在一个集合中;
布隆过滤器的原理是开辟一个位数组用每一位来标记一个数是否存在;这个数就是字符串经过哈希函数映射来的hash值,在用位图标记不重复的正数时,是不会出现哈希冲突的,但是在用位图标记字符串时,由于没有一个哈希函数能做到一个hash值只对应一个字符串;即就是说不同的字符串经过哈希映射以后可能出现相同的哈希值,这时就出现的哈希冲突,那么布隆过滤器解决哈希冲突的办法就是将一个字符串通过不通的哈希函数映射不同的哈希值,然后将这些哈希值对应到位图中的相应位置,用位记录他们的存在,然后在查找时,只有通知在位图中查找这五位哈希值都存在时,才说明这个元素存在,(所以用的哈希函数越多,判断存在的准确性越高,但是空间的利用就越多,查找的效率相应也会减慢)这比之前只用一个哈希函数判别元素是否存在准确性大大提升,但是两个不同的元素产生的哈希值可能有相同的;所以布隆过滤器判别元素存在是不确定的,但是当这个五位哈希值中有一位不存在,那么这个字符串肯定不存在,所以布隆过滤器判断不存在是确定的;

布隆的一个缺点就是不能删除元素,因为当两个元素映射的哈希值相同时,一个位只能表示0不存在和1存在,当着两个元素映射到同一个位时,只用一次标志作用,但是当删除一个元素时,会将重复的位置为0,那么这时另一个元素也不存在了,这显然是不正确的,所以要想布隆过滤器用可以删除,必须引用计数器;这又增加了空间的消耗;

布隆过滤器和位图在空间上的消耗不同,位图中的数组的大小取决于位图要记录的数据集合的最大值;而布隆过滤器中的位数组的大小开辟取决于要字符串集合中字符串的个数和使用的hashfun的个数;

应用:

网页URL的去重,
垃圾邮件的判别,
集合重复元素的判别,
查询加速(比如基于key-value的存储系统)等。

优点

它的优点是空间效率和查询时间都远远超过一般的算法,布隆过滤器存储空间和插入 / 查询时间都是常数O(k)。另外, 散列函数相互之间没有关系,方便由硬件并行实现。布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势。

缺点

但是布隆过滤器的缺点和优点一样明显。误算率是其中之一。随着存入的元素数量增加,误算率随之增加。但是如果元素数量太少,则使用散列表足矣。

(误判补救方法是:再建立一个小的白名单,存储那些可能被误判的信息。)

另外,一般情况下不能从布隆过滤器中删除元素. 我们很容易想到把位数组变成整数数组,每插入一个元素相应的计数器加 1, 这样删除元素时将计数器减掉就可以了。然而要保证安全地删除元素并非如此简单。首先我们必须保证删除的元素的确在布隆过滤器里面. 这一点单凭这个过滤器是无法保证的。另外计数器回绕也会造成问题。

(一)不带删除的布隆过滤器

#pragma once
#include<iostream>
#include"BitMap.h"
#include<string>
#include<vector>
using namespace std;

template<typename K>
struct _HashFunc1
{
size_t BKDRHash(const char* str)
{
register size_t hash = 0;
size_t ch = 0;
while(ch = (size_t)*str++)
{
hash = hash * 131 + ch;
}
return hash;
}
size_t operator()(const K& key)
{
return BKDRHash(key.c_str());
}
};

template<typename K>
struct _HashFunc2
{
size_t SDBMHash(const char* str)
{
register size_t hash = 0;
size_t ch = 0;
while(ch = (size_t)*str++)
{
hash = hash * 65599 + ch;
}
return hash;
}
size_t operator()(const K& key)
{
return SDBMHash(key.c_str());
}
};

template<typename K>
struct _HashFunc3
{
size_t RSHash(const char* str)
{
register size_t hash = 0;
size_t magic = 63689;
size_t ch = 0;
while(ch = (size_t)*str++)
{
hash = hash * magic + ch;
magic *= 378551;
}
return hash;
}
size_t operator()(const K& key)
{
return RSHash(key.c_str());
}
};

template<typename K>
struct _HashFunc4
{
size_t RSHash(const char* str)
{
register size_t hash = 0;
size_t magic = 63689;
size_t ch = 0;
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 K& key)
{
return RSHash(key.c_str());
}
};

template<typename K>
struct _HashFunc5
{
size_t RSHash(const char* str)
{
if(!*str)
return 0;
register size_t hash = 1315423911;
size_t magic = 63689;
size_t ch = 0;
while(ch = (size_t)*str++)
{
hash ^= ((hash << 5) + ch + (hash >> 2));
}
return hash;
}
size_t operator()(const K& key)
{
return RSHash(key.c_str());
}
};
不带删除的布隆过滤器:位数组(位图)+(多个)字符串哈希函数/用位表示存在与否
template<typename K=string,
typename HashFun1=_HashFunc1<K>,
typename HashFun2=_HashFunc2<K>,
typename HashFun3=_HashFunc3<K>,
typename HashFun4=_HashFunc4<K>,
typename HashFun5=_HashFunc5<K>>
class BloomFilter
{
public:
BloomFilter(size_t num)
:_bm(num*5)
,_sz(num*5)
{}
void Set(const K& key)//向布隆过滤器插入元素
{
size_t hash1=HashFun1()(key)%_sz;
size_t hash2=HashFun2()(key)%_sz;
size_t hash3=HashFun3()(key)%_sz;
size_t hash4=HashFun4()(key)%_sz;
size_t hash5=HashFun5()(key)%_sz;

_bm.Set(hash1);
cout<<hash1<<endl;
_bm.Set(hash2);
cout<<hash2<<endl;
_bm.Set(hash3);
cout<<hash3<<endl;
_bm.Set(hash4);
cout<<hash4<<endl;
_bm.Set(hash5);
cout<<hash5<<endl;
}
bool Test(const K& key)//在布隆过滤器中查看某个元素是否存在---不确定性判别(不存在是确定的,存在是不确定的)
{

查找的时候,有一个不存在,代表该元素不存在,所有hash值都存在,便是该元素可能存在
size_t hash1=HashFun1()(key)%_sz;
if (_bm.Test(hash1)==false)
return false;
size_t hash2=HashFun2()(key)%_sz;
if (_bm.Test(hash2)==false)
return false;
size_t hash3=HashFun3()(key)%_sz;
if (_bm.Test(hash3)==false)
return false;
size_t hash4=HashFun4()(key)%_sz;
if (_bm.Test(hash4)==false)
return false;
size_t hash5=HashFun5()(key)%_sz;
if (_bm.Test(hash5)==false)
return false;

return true;
}
private:
BitMap _bm;//位图
size_t _sz;//元素
};
using namespace std;
void BloomFilterTest1()
{
BloomFilter<> bf(10);
bf.Set("BloomFilterTest1");
cout<<endl;
bf.Set("return false");
cout<<endl;
bf.Set("HashFunc3");
cout<<endl;
bf.Set("sort");
cout<<endl;

cout<<bf.Test("BloomFilterTest1")<<endl;
cout<<bf.Test("return false")<<endl;
cout<<bf.Test("jggjhasgjh")<<endl;

}
int main()
{

BloomFilterTest1();
return 0;
}

(2带删除的布隆过滤器

#pragma once
#include<iostream>
#include"BitMap.h"
#include<string>
#include<vector>
using namespace std;

template<typename K>
struct _HashFunc1
{
size_t BKDRHash(const char* str)
{
register size_t hash = 0;
size_t ch = 0;
while(ch = (size_t)*str++)
{
hash = hash * 131 + ch;
}
return hash;
}
size_t operator()(const K& key)
{
return BKDRHash(key.c_str());
}
};

template<typename K>
struct _HashFunc2
{
size_t SDBMHash(const char* str)
{
register size_t hash = 0;
size_t ch = 0;
while(ch = (size_t)*str++)
{
hash = hash * 65599 + ch;
}
return hash;
}
size_t operator()(const K& key)
{
return SDBMHash(key.c_str());
}
};

template<typename K>
struct _HashFunc3
{
size_t RSHash(const char* str)
{
register size_t hash = 0;
size_t magic = 63689;
size_t ch = 0;
while(ch = (size_t)*str++)
{
hash = hash * magic + ch;
magic *= 378551;
}
return hash;
}
size_t operator()(const K& key)
{
return RSHash(key.c_str());
}
};

template<typename K>
struct _HashFunc4
{
size_t RSHash(const char* str)
{
register size_t hash = 0;
size_t magic = 63689;
size_t ch = 0;
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 K& key)
{
return RSHash(key.c_str());
}
};

template<typename K>
struct _HashFunc5
{
size_t RSHash(const char* str)
{
if(!*str)
return 0;
register size_t hash = 1315423911;
size_t magic = 63689;
size_t ch = 0;
while(ch = (size_t)*str++)
{
hash ^= ((hash << 5) + ch + (hash >> 2));
}
return hash;
}
size_t operator()(const K& key)
{
return RSHash(key.c_str());
}
};

//待删除的布隆过滤器:整型数组+多个哈希函数(用整型数组的值记录该位出现的次数)
template<typename K=string,
typename HashFun1=_HashFunc1<K>,
typename HashFun2=_HashFunc2<K>,
typename HashFun3=_HashFunc3<K>,
typename HashFun4=_HashFunc4<K>,
typename HashFun5=_HashFunc5<K>>
class BloomFilter
{
public:
BloomFilter(size_t num)//num代表要存储的字符串的个数
{
_v.resize(num*5);
_sz=num*5;//一个字符串产生5个hash值
}
void Set(const K& key)//向布隆过滤器插入元素
{
//计算元素对应的五个哈希值
size_t hash1=HashFun1()(key)%_sz;
size_t hash2=HashFun2()(key)%_sz;
size_t hash3=HashFun3()(key)%_sz;
size_t hash4=HashFun4()(key)%_sz;
size_t hash5=HashFun5()(key)%_sz;

_v[hash1]++;
cout<<hash1<<endl;
_v[hash2]++;
cout<<hash2<<endl;
_v[hash3]++;
cout<<hash3<<endl;
_v[hash4]++;
cout<<hash4<<endl;
_v[hash5]++;
cout<<hash5<<endl;
}

bool ReSet(const K& key)//删除布隆过滤器中的牧歌元素
{
size_t hash1=HashFun1()(key)%_sz;
size_t hash2=HashFun2()(key)%_sz;
size_t hash3=HashFun3()(key)%_sz;
size_t hash4=HashFun4()(key)%_sz;
size_t hash5=HashFun5()(key)%_sz;
//元素不存在与布隆过滤器
if (_v[hash1]==0||_v[hash2]==0||_v[hash3]==0||_v[hash4]==0||_v[hash5]==0)
{
return false;
}
//减少对应hash位的值
_v[hash1]--;
_v[hash2]--;
_v[hash3]--;
_v[hash4]--;
_v[hash5]--;
}
bool Test(const K& key)//在布隆过滤器中查看某个元素是否存在---不确定性判别(不存在是确定的,存在是不确定的)
{
//查找的时候,有一个不存在,代表该元素不存在,所有hash值都存在,便是该元素可能存在
size_t hash1=HashFun1()(key)%_sz;
if (_v[hash1]==0)
return false;
size_t hash2=HashFun2()(key)%_sz;
if (_v[hash2]==0)
return false;
size_t hash3=HashFun3()(key)%_sz;
if (_v[hash3]==0)
return false;
size_t hash4=HashFun4()(key)%_sz;
if (_v[hash4]==0)
return false;
size_t hash5=HashFun5()(key)%_sz;
if (_v[hash5]==0)
return false;

return true;
}
private:
vector<size_t> _v;
size_t _sz;//字符串产生hash值的总数
};
using namespace std;
void BloomFilterTest1()
{
BloomFilter<> bf(10);
bf.Set("BloomFilterTest1");
cout<<endl;
bf.Set("return false");
cout<<endl;
bf.Set("HashFunc3");
cout<<endl;
bf.Set("sort");
cout<<endl;

cout<<bf.Test("BloomFilterTest1")<<endl;
cout<<bf.Test("return false")<<endl;
cout<<bf.Test("jggjhasgjh")<<endl;

}
int main()
{

BloomFilterTest1();
return 0;
}