[C++] 位图与布隆过滤器的相关介绍
位图
给40亿个不重复的无符号整数,没排过序。如何快速判断一个数是否在这40亿个数中?
首先分析问题:
- 40亿个无符号整数, 1个无符号整数4字节, 40亿个就是160亿字节
如果将这40亿个整数存放到内存中, 则需要 16GB 的内存
所以为了解决这类问题将所有数据存储到内存中, 不太现实
- 题目要求要快速判断, 所以遍历的方法首先就可以被OUT
那么可以想到的比较快速的查找方法就是:二分查找
但是 二分查找的前提条件是:1. 数据必须有序 2. 数据必须存储在连续的内存中
第一个条件可以用外排序解决, 但是第二个问题一般是没有办法解决的, 因为没有办法保证如此多的内存都是连续的
而位图就可以不浪费空间的解决这样的问题
什么是位图
位图的实现
位图的结构
vector<char>
作为位图实现的底层, 那么每一个vector单元就是8个比特位(如果用int, 则每个单元就是32个比特位)class bitset {
private:
vector<char> _bit;
}
位图的构造函数
class bitset {
pubilc:
bitset(size_t N) {
_bit.resize(N/8 + 1, 0); // 需要开辟N/8+1个空间, +1是为了保证比特位足够, 最多也就浪费8个比特位
}
private:
vector<char> _bit;
}
数据在位图中的映射
set:
void set(size_t x) { size_t i = x / 8; // 算出x映射在第i个单元 size_t j = x % 8; // 算出x映射在第i个单元的第j个比特位上 _bit[i] |= (1 << j); // 将指定比特位设置为1 }
reset:
void reset(size_t x) { size_t i = x / 8; size_t j = x % 8; _bit[i] &= (~(1 << j)); // 将指定比特位设置为0 }
判断数据是否在位图中
test:
bool test(size_t x) {
size_t i = x / 8;
size_t j = x % 8;
return _bit[i] & (1 << j);
// 只要将整个单元数据 与上 只有第j位为1的数, 那么就可以根据结果判断出此位是否为1, 进而判断出数据是否在位图中
}
位图解决问题
当然, 位图的首次映射也可能是一个漫长的遍历过程, 但是只要位图构建完成, 今后的数据判断的时间复杂度都将只是
O(1)
构建INT_MAX位的位图, 至少需要使用256M-1bit=2^31-1bit
总结
布隆过滤器
当某个文件中存储有100亿个ip地址(可能相同)时, 怎么能将这些ip地址准确的映射到位图中呢?
答案显然是不可能的, 因为ip地址很明显是属于字符串数据的, 字符串的数据想要映射到位图中, 就先要将字符串数据通过哈希算法计算为整型数据, 然后再将对应的整型数据映射到位图中
布隆过滤器(BloomFilter)
最左按第0位算
当ip计算出的三个不同的哈希值再位图中映射同时存在时, 表示此ip存在
。并不能完全解决哈希冲突
同一个比特位可能被不同的原数据计算出的相同哈希值映射
, 这也就可能会出现下面这种情况:误报率
布隆过滤器的误报率, 与 哈希算法的个数、布隆过滤器比特位的个数以及需要映射的数据的个数都有关系
。但具体的关系不再介绍, 这里推荐另一篇文章:此文章中间部分通过数学与图例, 分析了布隆过滤器误报率相关因素的的影响关系
布隆过滤器究竟是什么?
针对处理非整型数据的位图
。对非整型数据使用多个哈希函数计算哈希值
, 再对计算出的所有哈希值在位图中进行映射。布隆过滤器的数据删除
- 删除一个数据, 需要删除多个哈希值映射
- 一个哈希值映射, 可能是多个数据的哈希值映射, 如果为了删除数据而直接删除此位置的哈希值映射可能会影响其他的数据
- 绿色IP 缺少了第12位的映射
- 蓝色IP 缺少了第11、15位的映射
- 黄色IP 缺少了第15位的映射
布隆过滤器的映射位添加一个计数的功能:
-
如何进行计数?是使用其他容器然后针对各映射位进行计数?还是在布隆过滤器本身进行计数?
布隆过滤器是为了处理海量数据节省内存而设计出来的, 如果再使用其他容器, 那么节省内存这个优势就没有了。
那么就应该在
布隆过滤器本身进行计数
Counting BloomFilter
Counting BloomFilter
的大致模型就应该是:Counting BloomFilter的弊端
为什么是过滤器
误报率
。即 布隆过滤器 使用多个哈希值进行映射减少了发生哈希冲突的概率, 但并不能完全避免哈希冲突。存在哈希冲突就有可能在查找时发生错误或误判, 即有一定的
误报率
找不到就一定不存在, 找到了却不一定存在
。这也是布隆过滤器的被称为过滤器的原因, 虽然不能准确的查找, 但是却可以过滤掉一部分数据海量数据的处理
哈希切割
我们都知道, 特定的哈希算法可以给 不同的数据 赋予各自可能唯一的哈希值。那么当海量的数据不能一下存储到内存中进行数据分析时, 可以使用特定的哈希算法计算出数据的哈希值, 并将哈希值相同的数据存放到另一个文件中, 思路像是将海量的数据按照哈希值进行分类, 将海量数据文件分为诺干小文件。此种方法就被称为
哈希切割
当然, 经过哈希切割的海量数据, 可能也会出现问题:
- 海量数据绝大部分或者全部都只是同一个数据, 经过哈希切割出来的文件也是非常大的文件, 依旧存在海量数据的情况
此种情况在统计的时候不会发生太大的问题, 因为在使用红黑树等容器遍历文件统计数量时, 容器中只会存储一个数据, 当遍历过程中再次遇到此数据时, 容器中并不会再次存储, 而只是进行统计 将计数进行++
所以, 不用担心此时会发生内存报错
- 发生哈希冲突, 即同一个文件中可能存在太多个不同的IP(太多表示依旧是海量数据)
当哈希切割之后, 同一个文件中依旧存在海量的不同IP, 又该怎么解决?
作者: 哈米d1ch 发表日期:2023 年 2 月 20 日