位图和布隆过滤器
- 一、位图
- 1. 引入
- 2. 概念
- 3. 代码实现
- set
- reset
- 完整代码
- 4. 位图的应用
- 二、布隆过滤器
- 1. 引入
- 2. 概念
- 3. 逻辑结构
- 4. 特点
- 5. 代码实现
- 6. 布隆过滤器的应用
- 三、哈希切割
一、位图
1. 引入
当面对海量数据需要处理时,内存不足以加载这些数据,这时普通的方法就不适用了。如果在这海量的数据是否存在,那么只判断状态只需要一个bit位即可,0就是不存在,1就是存在。
2. 概念
每一位都用来存放某种状态,适用于海量的数据,数据无重复的场景。通常是判断某个数据是否存在。
3. 代码实现
位操作
- |
1 | 0 = 1
1 | 1 = 1
0 | 1 = 1
0 | 0 = 0- &
1 & 0 = 0
1 & 1 = 1
0 & 1 = 0
0 & 0 = 0
set
//把x映射的位置设为1
void set(size_t x)
{int i = x / 32;int j = x % 32;_a[i] |= (1 << j);
}
reset
//把x映射的位置设为0
void reset(size_t x)
{int i = x / 32;int j = x % 32;_a[i] &= ~(1 << j);
}
完整代码
namespace kpl
{template<size_t N>class bitset{public:bitset(){_a.resize(N / 32 + 1);}//把x映射的位置设为1void set(size_t x){int i = x / 32;int j = x % 32;_a[i] |= (1 << j);}//把x映射的位置设为0void reset(size_t x){int i = x / 32;int j = x % 32;_a[i] &= ~(1 << j);}bool test(size_t x){return _a[x / 32] & (1 << (x % 32));}private:vector<int> _a;};
}
4. 位图的应用
问题1:给定100亿个整数,计算只出现一次的数
问题2:找出现次数超过两次的所以整数
解答:可以使用两个位图控制,或者一个位图两个标志位控制
两个位图代码的实现:
namespace kpl
{
template<size_t N>class twobitset{public://把x映射的位置设为1void set(size_t x){//00 --> 01if (!_bs1.test(x) && !_bs2.test(x)){_bs2.set(x);}//01 --> 10else if (!_bs1.test(x) && _bs2.test(x)){_bs1.set(x);_bs2.reset(x);}}bool is_one(size_t x){return !_bs1.test(x) && _bs2.test(x);}private:bitset<N> _bs1;bitset<N> _bs2;};
}
二、布隆过滤器
1. 引入
客户端推荐新内容,每次推荐要过滤掉已经存在的历史记录。如果使用哈希表,太浪费空间。单独使用位图又不能除了字符串。
所以采用位图和哈希结合的方法即布隆过滤器。
2. 概念
布隆过滤器是一种概率性数据结构,使用多个哈希函数,将一个数据用多个哈希函数映射到一个位图结构中,因此被映射的位置的比特位一定为1。
- 查找
分别计算每个哈希值对应的比特位存储是否为0,只要一个为0,则该元素一定不存在,否则可能存在在哈希表中(布隆过滤器对存在有误判)- 删除
不能直接支持删除工作,因为可能会影响其他的元素
可以通过计数器来增加这一删除操作,但是会增加几倍的存储空间,同时因为不确定该元素是否存在,可能会误删。
3. 逻辑结构
4. 特点
优点:
- 增加和查询元素的时间复杂度为O(K)(K为哈希函数的个数)
- 哈希函数相互之间没有关系
- 布隆过滤器不需要存储元素本身,保密工作更好
- 有很大大的空间优势
缺点
- 存在误判,不能准确判断元素是否在集合中。(再建立白名单,保存不确定数据)
- 不能获取元素本身
- 一般不能删除元素
5. 代码实现
#include <bitset>
#include <string>
#include <vector>//哈希函数
struct BKDRHash
{size_t operator()(const string& str){size_t hash = 0;for (auto ch : str){hash = hash * 131 + ch;}return hash;}
};struct APHash
{size_t operator()(const string& str){size_t hash = 0;for (size_t i = 0; i < str.size(); i++){size_t ch = str[i];if ((i & 1) == 0){hash ^= ((hash << 7) ^ ch ^ (hash >> 3));}else{hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));}}return hash;}
};struct DJBHash
{size_t operator()(const string& str){size_t hash = 5381;for (auto ch : str){hash += (hash << 5) + ch;}return hash;}
};//布隆过滤器实现
template<size_t N,class K = string,class Hash1 = BKDRHash,class Hash2 = APHash,class Hash3 = DJBHash>
class BloomFilter
{
public:void Set(const K& key){size_t hash1 = Hash1()(key) % N;_bs.set(hash1);size_t hash2 = Hash2()(key) % N;_bs.set(hash2);size_t hash3 = Hash3()(key) % N;_bs.set(hash3);}//存在误判bool Test(const K& key){return _bs.test(Hash1()(key) % N) && _bs.test(Hash2()(key) % N) && _bs.test(Hash3()(key) % N);}private:bitset<N> _bs;
};
6. 布隆过滤器的应用
三、哈希切割
给两个文件,分别有100亿个query,我们只要1G内存,如何寻找两个文件的交集?
答: