位图与布隆过滤器
一、简单实现位图
1.什么是位图
位图,就是利用比特位0和1的特性,用来映射数据的状态表示,适用于海量数据,以及无重复数据的处理。一般用于表示数据的存在与否。
2.位图的实现
表示数据是整型类型的情况
#pragma once
#include <vector>
namespace bit
{
// 使用无符号整型防止模板参数为负数
template<size_t N>
class bitset
{
public:
bitset()
{
_n.resize(N / 32 + 1);
}
// 映射指定值到位图中
void set(size_t x)
{
int i = x / 32;
int j = x % 32;
_n[i] |= (1 << j);
}
// 取消映射值
void reset(size_t x)
{
int i = x / 32;
int j = x % 32;
_n[i] &= (~(1 << j));
}
// 判断是否存在
bool test(size_t n)
{
int i = n / 32;
int j = n % 32;
return _n[i] & (1 << j);
}
private:
vector<int> _n;
};
}
测试用例与效果:
#include <iostream>
using namespace std;
#include "BitSet.h"
int main()
{
bit::bitset<1000> bs;
bs.set(1);
bs.set(10);
bs.set(100);
cout << bs.test(1) << endl;
cout << bs.test(10) << endl;
cout << bs.test(100) << endl;
cout << bs.test(999) << endl<<endl;
bs.set(999);
bs.reset(10);
cout << bs.test(1) << endl;
cout << bs.test(10) << endl;
cout << bs.test(100) << endl;
cout << bs.test(999) << endl << endl;
//bit::bitset<-1> bs1;
//bit::bitset<0xffffffff> bs2;
return 0;
}
二、 布隆过滤器
1.什么是布隆过滤器
布隆过滤器是布隆在1970年提出的一种紧凑的、比较巧妙地概率型数据结构,有着高效的插入、查询,以及快速获取数据存在与否的特点。它是由多个哈希函数,将一个数据映射到多个位图结构中,相比与寻常的数据结构,它可以节省大量对内存的负担。
其中,最好理解的就是字符串的状态表示了。比如一个游戏中,玩家游玩需要注册名字且与其他玩家不能重复,当然公司中肯定会存储各位玩家的数据,但如果玩家特别特别多,这将导致取名字时响应时间过长影响玩家的游戏体验。此时我们就需要使用布隆过滤器了,将字符串(名字)映射到多个位图上,如果都存在,那么再去海量数据中去查找;不存在,直接返回结果即可。这无疑是一个对数据查找大大的优化。
2.布隆过滤器的实现
通过布隆过滤器实现对大量字符串类型的映射,其中对于字符串的映射参考于多位算法大佬(本文只引用了三种算法)。
// 布隆过滤器
struct BKDRHash
{
size_t operator()(const string& s)
{
// BKDR
size_t value = 0;
for (auto ch : s)
{
value *= 31;
value += ch;
}
return value;
}
};
struct APHash
{
size_t operator()(const string& s)
{
size_t hash = 0;
for (long i = 0; i < s.size(); i++)
{
if ((i & 1) == 0)
{
hash ^= ((hash << 7) ^ s[i] ^ (hash >> 3));
}
else
{
hash ^= (~((hash << 11) ^ s[i] ^ (hash >> 5)));
}
}
return hash;
}
};
struct DJBHash
{
size_t operator()(const string& s)
{
size_t hash = 5381;
for (auto ch : s)
{
hash += (hash << 5) + ch;
}
return hash;
}
};
// 使N个数据乘以X倍有利于布隆过滤器中数据重复情况的减少(X倍数越大重复越少)
template<size_t N, size_t X = 5, class K = string,
class HashFunc1 = BKDRHash,
class HashFunc2 = APHash,
class HashFunc3 = DJBHash>
class BloomFilter
{
public:
void Set(const K& key)
{
size_t len = X * N;
size_t index1 = HashFunc1(key) % len;
size_t index2 = HashFunc2(key) % len;
size_t index3 = HashFunc3(key) % len;
/* cout << index1 << endl;
cout << index2 << endl;
cout << index3 << endl<<endl;*/
_bs.set(index1);
_bs.set(index2);
_bs.set(index3);
}
bool Test(const K& key)
{
size_t len = X * N;
size_t index1 = HashFunc1(key) % len;
if (_bs.test(index1) == false)
return false;
size_t index2 = HashFunc2(key) % len;
if (_bs.test(index2) == false)
return false;
size_t index3 = HashFunc3(key) % len;
if (_bs.test(index3) == false)
return false;
return true;
}
// 不支持删除,删除可能会影响其他值。
void Reset(const K& key);
private:
bitset<X* N> _bs;
};
3.布隆过滤器的优点
1.查询速度极快,为O(k),其中的k为哈希函数的个数;
2.在允许误差的范围内,可以节省大量的内存消耗;
3.布隆过滤器没有存储元素本身,有一定的保密性。
4.布隆过滤器的缺点
1.一般不能删除映射的元素,相当于一次性用品;
2.不能查找到元素本身,只能获取其一定的状态表示;
3.仍然存在误判的可能性。