目录
[2.1 set](#2.1 set)
[2.2 test](#2.2 test)
1.布隆过滤器的概念
布隆过滤器是用多个哈希函数 ,将一个数据映射到位图中的概率型数据结构,
特点是高效地插入和查询 ,可以用来告诉你 "某样东西一定不存在或者可能存在"
此种方式不仅可以提升查询效率(O(K),K极小),也 可以节省大量的内存空间(比特位存储)

上图就是将一个字符串通过不同的哈希函数映射到不同的哈希地址当中
不同字符串的映射位置难免有交叉
2.布隆过滤器的实现
struct BKDRHash
{
size_t operator()(const string& str)
{
size_t hash = 0;
for(auto ch : str)
{
hash = hash * 131 + ch;
}
return hash;
}
};
struct SDBMHash
{
size_t operator()(const string& str)
{
size_t hash = 0;
for (auto ch : str)
{
hash = 65599 * hash + ch;
//hash = (size_t)ch + (hash << 6) + (hash << 16) - hash;
}
return hash;
}
};
struct RSHash
{
size_t operator()(const string& str)
{
size_t hash = 0;
size_t magic = 63689;
for (auto ch : str)
{
hash = hash * magic + ch;
magic *= 378551;
}
return hash;
}
};
template<size_t N,
class T = string,
class Hash1 = BKDRHash,
class Hash2 = SDBMHash,
class Hash3 = RSHash>
class Bloomfilter
{
public:
private:
bitset<N> _bs;
};
(1)布隆过滤器常用于字符串的映射,参数默认为string
(2)Hash1等是不同哈希字符串算法的仿函数
(1)编译器会在布隆过滤器生成的时候自动去调用biset的构造函数,这里不用显示写
2.1 set
void set(const string& str)
{
int hash1 = Hash1()(str) % N;
int hash2 = Hash2()(str) % N;
int hash3 = Hash3()(str) % N;
_bs.set(hash1);
_bs.set(hash2);
_bs.set(hash3);
}
根据不同的哈希函数计算的哈希地址进行映射
2.2 test
bool test(const string& str)
{
int hash1 = Hash1()(str) % N;
if (_bs.test(hash1) == false)
return false;
int hash2 = Hash2()(str) % N;
if (_bs.test(hash2) == false)
return false;
int hash3 = Hash3()(str) % N;
return _bs.test(hash3);
}
字符串所映射的比特位中,有一个为0,就说明该字符串一定不存在
全为1,只能说明,该字符串可能存在
这是因为该字串所映射的比特位可能与存在的字符串映射的比特位交叉重叠了
3.布隆过滤器的查找
分别计算每个哈希值对应的比特位置存储的是否为零,只要有一个为零,代表该元素一定不在,否则可能存在。
注意:布隆过滤器如果说某个元素不存在时,该元素一定不存在,如果该元素存在时,该元素可 能存在,因为有些哈希函数存在一定的误判。
比如:在布隆过滤器中查找"alibaba"时,假设3个哈希函数计算的哈希值为:1、3、7,刚好和其 他元素的比特位重叠,此时布隆过滤器告诉该元素存在,但实该元素是不存在的
4.布隆过滤器的删除
标准布隆过滤器的实现不支持删除操作,这是因为删除操作是将比特位1置为0,但是我们不知道删除数据所对应的比特位涉及哪些其他的数据,贸然删除会影响其它数据的存在性
一种支持删除的方法:将布隆过滤器中的每个比特位扩展成一个小的计数器(如8位整数),插入元素时,给k个计 数器(k个哈希函数计算出的哈希地址)加一,
删除元素时,给k个计数器减一,
查询元素时,若所有k个位置的计数器均>0,则元素"可能存在";否则"绝对不存在"
通过多占用几倍存储 空间的代价来增加删除操作。
缺陷: 1. 无法确认元素是否真正在布隆过滤器中
- 存在计数回绕(8位整数的范围0-255,若已经有了255次,再加1即溢出为0)
5.布隆过滤器优点
增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无 关
哈希函数相互之间没有关系,方便硬件并行运算
布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势
在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势
数据量很大时,布隆过滤器可以表示全集,其他数据结构不能
使用同一组散列函数的布隆过滤器可以进行交、并运算
- 并集:位数组按位或操作,时间复杂度O(m)
- 交集:位数组按位与操作,时间复杂度O(m)
6.布隆过滤器缺陷
- 有误判率,即不能准确判断元素是否在集合中
补救方法:再 建立一个白名单,存储可能会误判的数据
不能获取元素本身
一般情况下不能从布隆过滤器中删除元素
如果采用计数方式删除,可能会存在计数回绕问题
7.布隆过滤器的应用及哈希切分
1.给两个文件,分别有100亿个query,我们只有1G内存,
如何找到两个文件交集?分别给出 精确算法和近似算法
近似算法:用布隆过滤器a标记A文件的query,遍历文件B的Query,若布隆过滤器a返回"可能存在",则记录为交集(可能有重复的query,当然也存在误判)
精确算法:按照相同的哈希函数,将两个文件A、B切分成若干个小文件,将对应的小文件堆到内存中使用unordered_set进行精确查找哈希切分就是通过哈希函数将大文件切分为小文件的过程
相同的query一定会被分配到对应的小文件中(A0-B0、A1-B1、A2-B2......)当然,这里并不是平均切分,某些小文件仍可能会过大
文件为5G,有两种情况:
1、4G数据相同、1G冲突
2、大多数数据冲突
怎么区分这两种情况呢?
解决方法:使用unordered_set存储数据,当程序抛出bad_alloc异常的时候,
说明是情况2,需要再切分,否则不需要
2.给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址? 与上题条件相同,如何找到top K的IP?
问题一:
哈希切分后,使用unordered_map统计文件中的Ip地址的出现次数,统计完一个文件,使用变量标记该IP地址及其次数,持续进行统计次数、更新变量的操作
问题二:
哈希切分后,使用unordered_map统计文件中的Ip地址的出现次数,边统计边更新小堆,持续进行统计次数、更新小堆的操作