布隆过滤器
假如现在有40亿个ip地址(string类型),然后给你一个ip地址,让你查找这个ip地址在不在这40亿个ip地址里?我们应该怎么做呢?
- 如果用哈希表来处理的话,这里有40亿的数据,数据量太大,因此太占用空间
- 如果用位图来处理的话,这里因为是字符串,有可能不同的字符串映射的是同一个位,会有哈希冲突的问题,导致误判
- 因此我们采用的是哈希+位图的方法,也就是布隆过滤器
布隆过滤器是由布隆(Burton Howard Bloom)在1970年提出的一种紧凑型的、比较巧妙的概率型数据结构,特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”,它是用多个哈希函数,将一个数据映射到位图结构中。
布隆过滤器的优点:
- 查询效率高:使用了哈希的思想,一个数据映射了多个位
- 节省大量的内存空间:采用了位图
布隆过滤器的缺点:
布隆过滤器如果判断出该数据没有,那么这个数据一定没有,因为只要有一个映射的位不存在时,那么该数据就不存在。但是布隆过滤器如果判断出该数据有,那么其实是有可能出现误差的,因为字符串的计算难免会导致某两个字符串映射的几个位置都一样,因为我们写的哈希映射算法是固定的,字符串的随机排列组合是无限多的,所以如果两个字符串计算出来的映射的位一样时,就有可能导致误判。(也就是说这个字符串并不存在,但是由于映射的位和之前的字符串相同,所以就会判断出这个字符串也存在)
布隆过滤器的实现
布隆过滤器支持set接口,test接口,可以复用位图的set和test接口。但是布隆过滤器不能支持删除,因为通过多个哈希算法一个值会映射多个位,而且不同的值有可能会映射相同的位,如果删除的这个值所对应的位会变成0,那如果其他的值也映射这个位的话,就会导致其他的那个值被判定为不存在,但其实你并没有删除它。因此不支持删除。
那如果一定要实现删除操作呢?那就只能使用引用计数,但是采用引用计数删除的话就需要进行储存这个计数,需要额外的空间,会增大占用内存的大小。使用引用计数就是有多个值同时映射这个位,就把引用计数++,删除时就–,然后判断是否存在则是看值是否大于0。
具体代码如下:
BloomFilter.h
#pragma once
#include <iostream>
#include <string>
#include <vector>
#include "bitset.h"
using namespace std;
template <class K, class HashFunc1, class HashFunc2, class HashFunc3>
class BloomFilter
{
public:
BloomFilter(size_t keynum)//开多少位呢?
:_bs(keynum * 5)
{
}
void set(const K& key)
{
//如果是字符串,假如开了50个位,那么你映射的位不一定就在这50个位之间,因此要模上bitnum
//使计算出来的位都在这50个位里进行映射
size_t index1 = HashFunc1()(key) % _bs.GetBitNum();
size_t index2 = HashFunc2()(key) % _bs.GetBitNum();
size_t index3 = HashFunc3()(key) % _bs.GetBitNum();
_bs.set(index1)