位图概念
位图本质上是一个数组,每个位置上存储的是0和1。
给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。
首先我们首先想到的是遍历数组,但是这里会遇到一个问题,40亿个整数会开多大的空间?
1个整数4个字节,也就是需要内存160亿字节,大约14.9GB
1GB = 1024 MB, 1MB = 1024KB, 1KB = 1024Byte
现如今电脑16GB内存就很不错了,所以这里就需要位图来解决
无符号整数范围是(0,2^32-1),每个数对应一个位置,如果那个位置存在就是1,不存在就是0
需要40亿个比特位,也就是512MB就可以存下
vector中每个值的有32个比特位
位图实现
template<size_t N>
class bitset
{
public:
bitset()
{
_a.resize(N / 32 + 1);
}
//把这个数对应比特位标记位0
void set(size_t x)
{
size_t i = x / 32;
size_t j = x % 32;
_a[i] |= (1 << j);
}
//标记为1
void reset(size_t x)
{
size_t i = x / 32;
size_t j = x % 32;
_a[i] &= (~(1 << j));
}
bool test(size_t x)
{
size_t i = x / 32;
size_t j = x % 32;
return _a[i] & (1 << j);
}
private:
vector<int> _a;
};
给定100亿个整数,设计算法找到只出现一次的整数?
整数最大值就是42亿左右,所以不会出现空间不足的问题,会有数据重复出现。
出现0次: 00
出现1次: 01
出现两次及以上: 10
template<size_t N>
class twobitset
{
public:
void set(size_t x)
{
//00->01
if (!_bs1.test(x) && !_bs2.test(x))
{
_bs2.set(x);
}
else if (!_bs1.test(x) && _bs2.test(x))//01->10
{
_bs1.set(x);
_bs2.reset(x);
}
}
bool is_once(size_t x)
{
return _bs1.test(x) && !_bs2.test(x);
}
private:
bitset<N> _bs1;
bitset<N> _bs2;
};
布隆过滤器
是二进制向量和一系列随机映射函数组成。
一个值映射到几个位置,这几个映射值为1,表示这个元素存在,但是这会出现一个新的问题,当有几个元素映射到了相同几个位置就会出现误判。
使用不同的哈希函数将一个数映射到不同的位置
布隆过滤器的缺点:
删除元素,当有几个元素在同一个位置有映射值,如果删除的话,会影响其他元素。
struct BKDRHash
{
size_t operator()(const string& str)
{
size_t hash = 0;
for (auto ch : str)
{
hash = hash * 131 + ch;
}
//cout <<"BKDRHash:" << hash << endl;
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)));
}
}
//cout << "APHash:" << hash << endl;
return hash;
}
};
struct DJBHash
{
size_t operator()(const string& str)
{
size_t hash = 5381;
for (auto ch : str)
{
hash += (hash << 5) + ch;
}
//cout << "DJBHash:" << hash << endl;
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;
size_t hash2 = Hash2()(key) % N;
size_t hash3 = Hash3()(key) % N;
_bs.set(hash1);
_bs.set(hash2);
_bs.set(hash3);
}
bool test(const K& key)
{
size_t hash1 = Hash1()(key) % N;
if (!_bs.test(hash1))
return false;
size_t hash2 = Hash2()(key) % N;
if (!_bs.test(hash2))
return false;
size_t hash3 = Hash3()(key) % N;
if (!_bs.test(hash3))
return false;
return true;
}
private:
bitset<N> _bs;
};