位图和布隆过滤器
位图概念
所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在的。
位图的实现
class bitset
{
public:
bitset(size_t bitCount)
: _bit((bitCount>>5)+1), _bitCount(bitCount)
{}
// 将which比特位置1
void set(size_t which)
{
if(which > _bitCount)
return;
size_t index = which / 32;
size_t pos = which % 32;
_bit[index] |= (1 << pos);
}
// 将which比特位置0
void reset(size_t which)
{
if(which > _bitCount)
return;
size_t index = which / 32;
size_t pos = which % 32;
_bit[index] &= ~(1<<pos);
}
// 检测位图中which是否为1
bool test(size_t which)
{
if(which > _bitCount)
return false;
size_t index = which / 32;
size_t pos = which % 32;
return _bit[index] & (1<<pos);
}
// 获取位图中比特位的总个数
size_t size()const{ return _bitCount;}
private:
vector<int> _bit;
size_t _bitCount;
};
位图可以快速查找某个数据是否在一个集合中,还能求两个集合的交集、并集等。
布隆过滤器概念
布隆过滤器是由布隆(Burton Howard Bloom)在1970年提出的 一种紧凑型的、比较巧妙的概率型数据结构,特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”,它是用多个哈希函数,将一个数据映射到位图结构中。此种方式不仅可以提升查询效率,也可以节省大量的内存空间。
布隆过滤器的实现
#include<bitset>
#include<string>
struct HashFuncBKDR
{
// BKDR
size_t operator()(const string& s)
{
size_t hash = 0;
for (auto ch : s)
{
hash *= 131;
hash += ch;
}
return hash;
}
};
struct HashFuncAP
{
// AP
size_t operator()(const string& s)
{
size_t hash = 0;
for (size_t 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 HashFuncDJB
{
// DJB
size_t operator()(const string& s)
{
size_t hash = 5381;
for (auto ch : s)
{
hash = hash * 33 ^ ch;
}
return hash;
}
};
template<size_t N,
class K = string,
class Hash1 = HashFuncBKDR,
class Hash2 = HashFuncAP,
class Hash3 = HashFuncDJB>
class BloomFilter
{
public:
void Set(const K& key)
{
size_t hash1 = Hash1()(key) % M;
size_t hash2 = Hash2()(key) % M;
size_t hash3 = Hash3()(key) % M;
_bs->set(hash1);
_bs->set(hash2);
_bs->set(hash3);
}
bool Test(const K& key)
{
size_t hash1 = Hash1()(key) % M;
if (_bs->test(hash1) == false)
return false;
size_t hash2 = Hash2()(key) % M;
if (_bs->test(hash2) == false)
return false;
size_t hash3 = Hash3()(key) % M;
if (_bs->test(hash3) == false)
return false;
return true; // 存在误判(有可能3个位都是跟别人冲突的,所以误判)
}
private:
static const size_t M = 10 * N;
std::bitset<M>* _bs = new std::bitset<M>;
};
布隆过滤器的优点:增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关; 哈希函数相互之间没有关系,方便硬件并行运算; 布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势;在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势;数据量很大时,布隆过滤器可以表示全集,其他数据结构不能。
布隆过滤器的缺点: 有误判率,即存在假阳性(False Position),即不能准确判断元素是否在集合中(补救方法:再建立一个白名单,存储可能会误判的数据);不能获取元素本身;一般情况下不能从布隆过滤器中删除元素。
海量数据面试题
哈希切割:给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址?
与上题条件相同,如何找到top K的IP?如何直接用Linux系统命令实现?
位图应用:
-
给定100亿个整数,设计算法找到只出现一次的整数?
-
给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
-
位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数
布隆过滤器:
-
给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法
-
如何扩展BloomFilter使得它支持删除元素的操作