布隆过滤器
布隆过滤器是布隆于1970年提出的一种紧凑型、比较巧妙的概率型数据结构,其特点是
高效的地插入与查询,可用来表示某样东西一定不存在或者可能存在。
布隆过滤器是通过使用多个哈希函数,将同一个数据映射到位图结构中,这种方式不仅可以
提高查询效率,也可以节省大量的内存空间。
布隆过滤器能干什么?
小伙伴们有没有注意过像腾讯视频、抖音、bilibili等app在你手机通知栏里对你的推送?
推送一条后,你点进去看了或者清除掉后,等会它又推送一条,这一条一定和之前的推送
不一样,甚至从被安装到被卸载都没有推送过一模一样的东西。
这里面就是布隆过滤器在发挥作用。推送过的东西会被服务器加入布隆过滤器,等下次
推送时,服务器在过滤器中查询一下被推目标是否存在于过滤器中,不存在才会推送。
那么这个过滤器要使用布隆过滤器?不能使用其他数据结构吗?
1.使用哈希表记录推送记录,因为推送过或用户已经看过极多,每条不同的记录通过哈希记录,必然相当于直接存储记录,浪费空间。
2.使用位图记录推送记录,因为记录必然是通过哈希映射成某个数据,不存在一个哈希函数可以将所有不同的数据映射成不同的数据,一定存在映射值相同但原数据不同的现象,因此使用位图将导致哈希值相同但原数据不同的记录一同屏蔽掉,导致某些未推送过的被屏蔽,即位图不能处理哈希冲突。
3.布隆过滤器将哈希和位图结合,在一定程度上巧妙地解决了上述两个数据结构存在的问题。
既能尽可能处理哈希冲突,又能最大程度节省空间。
布隆过滤器的示意图
//假设布隆过滤器中元素类型为K,每个元素通过3个哈希函数映射至布隆过滤器中的位图索引,
//此处并没有编写哈希函数,使用仿函数写即可。
template<class K,class KeyToInt1 = Hash1,
class KeyToInt1 = Hash2,
class KeyToInt1 = Hash3>
class BloomFilter{
public:
BloomFilter(size_t size)//布隆过滤器中的元素数目
:_bmp(3*size),_size(0){}
bool Insert(const K& key){
size_t bitCount = _bmp.Size();
size_t index1 = KeyToInt1()(key)%bitCount;//哈希映射
size_t index2 = KeyToInt2()(key)%bitCount;
size_t index3 = KeyToInt3()(key)%bitCount;
_bmp.Set(index1);_bmp.Set(index2);_bmp.Set(index3);//位图记录
++_size;
return true;
}
private:
std::bitset _bmp;
size_t _size;
}
布隆过滤器的查找
布隆过滤器的基本思想是将一个元素通过多个哈希函数映射到位图中,因此被映射到位置的比特一定为1。
因此可以按照以下方式去查找某元素是否存在于布隆过滤器中:
分别计算该元素的多个哈希值,检测对应位置的比特是否为1,只要有一个为0,则说明该元素不存在于布隆过滤器中,否则可能存在于其中。
bool IsInBloomFilter(const K& key){
size_t bitCount = _bmp.size();
size_t index1 = KeyToInt1()(key)%bitCount;
if(!_bmp.test(index1))
return false;
size_t index2 = KeyToInt2()(key)%bitCount;
if(!_bmp.test(index2))
return false;
size_t index3 = KeyToInt3()(key)%bitCount;
if(!_bmp.test(index3))
return false;
return true;
}
布隆过滤器的删除
布隆过滤器不支持直接删除工作,因为每删除一个元素,可能会影响其他元素。例如删除重叠哈希值时,删一个相当于删一堆,得不偿失。
存在一种支持删除的方法,那就是将位图的每个比特扩展成一个计数器,插入元素时,给对应的哈希值通通+1,删除元素时,给对应的哈希值通通-1。这个办法属于消耗空间支持删除操作。
该方法仍存在缺陷:
- 无法确认元素是否真正存在于布隆过滤器中
- 存在计数回绕:一般来说,计数器类型被unsigned修饰,这样就能保证计数器一定非负,但这样 搞会使0减1后变为当前类型的上限(整数,比特位全为1),即计数器会对加减法形成闭环,称为计 数回绕。
布隆过滤器的缺陷
- 有误判率:不能准确判断一个元素是否存在于集合中,只能准确判断某元素是否不存在于集合中
- 不能获取元素本身
- 一般情况下不支持删除元素
- 若采用计数方式删除,可能存在计数回绕问题