一.位图




二.bit_set大致实现
cpp
#pragma once
#include <vector>
namespace ltw
{
template<size_t N>
class bit_set
{
public:
bit_set()
{
_bs.resize(N / 32 + 1);
}
//x映射的位标记为1
void set(size_t x)
{
}
//x映射的位标记为0
void reset(size_t x)
{
}
//返回x映射的位是否为1
bool test(size_t x)
{
}
private:
std::vector<int> _bs;
};
}
三.set实现
这里我们不用考虑大小端(因为这里只和我们的操作有关系,和底层的大小端没有关系)
cpp
//x映射的位标记为1
void set(size_t x)
{
size_t i = x / 32;
size_t j = x % 32;
_bs[i] |= (1 << j);
}
四.reset的实现
cpp
//x映射的位标记为0
void reset(size_t x)
{
size_t i = x / 32;
size_t j = x % 32;
_bs[i] &= (~(1 << j));
}
五.test的实现
cpp
//返回x映射的位是否为1
bool test(size_t x)
{
size_t i = x / 32;
size_t j = x % 32;
return (_bs[i] & (1 << j));
}
六.总结和测试
实现了这个位图,我们就能够处理42亿个数据了(节省32倍空间,和很多时间(位运算的高效))
cpp
#include <iostream>
using namespace std;
#include "BitSet.h"
int main()
{
ltw::bitset<100> bs;
bs.set(10);
bs.set(20);
bs.set(30);
cout << bs.test(10) << endl;
cout << bs.test(20) << endl;
cout << bs.test(30) << endl;
cout << bs.test(40) << endl;
ltw::bitset<UINT_MAX> bs2;
return 0;
}
七.C++标准bitset

但是,库里面是开不出来的


cpp
int main()
{
// std::bitset<100> bs;
// cout << sizeof(bs) << endl;
// bs.set(10);
// bs.set(20);
// bs.set(30);
// cout << bs.test(10) << endl;
// cout << bs.test(20) << endl;
// cout << bs.test(30) << endl;
// cout << bs.test(40) << endl;
std::bitset<-1>* ptr = new std::bitset<-1>();
return 0;
}
我们可以通过new,将其在堆上面开出来
八.位图的优缺点

九.查找

我们可以用两个位置,来表示一个数出现的次数
1.twobitset设计
cpp
template<size_t N>
class twobitset
{
public:
twobitset()
{
_bs1 = bitset<N>();
_bs2 = bitset<N>();
}
private:
bitset<N> _bs1;
bitset<N> _bs2;
};
2.set的实现
如果出现1次,就标记一位,直到,大于2次我们就不进行其他的操作了
cpp
void set(size_t x)
{
bool bit1 = _bs1.test(x);
bool bit2 = _bs2.test(x);
if(bit1 && !bit2)//00 -> 01
{
_bs2.set(x);
}
else if(!bit1 && bit2)
{
_bs1.set(x);
_bs2.reset();
}
else if(bit1 && !bit2)
{
_bs2.set(x);
}
}
3.reset的实现
cpp
void reset(size_t x)
{
_bs1.reset(x);
_bs2.reset(x);
}
4.get_count的实现
cpp
//0次,表示出现0次
//1次,表示出现1次
//2次,表示出现2次
//3次,表示出现2次及以上
int get_count(size_t x)
{
bool bit1 = _bs1.test(x);
bool bit2 = _bs2.test(x);
if(bit1 && !bit2)
{
return 1;
}
else if(!bit1 && bit2)
{
return 2;
}
else if(bit1 && bit2)
{
return 3;
}
else
{
return 0;
}
}
5.测试
cpp
#include <iostream>
#include <bitset>
using namespace std;
void test_bitset3()
{
int a1[] = { 5,7,9,2,5,99,5,5,7,5,3,9,2,55,1,5,6 };
int a2[] = { 5,3,5,99,6,99,33,66 };
bitset<100> bs1;
bitset<100> bs2;
for (auto e : a1)
{
bs1.set(e);
}
for (auto e : a2)
{
bs2.set(e);
}
for (size_t i = 0; i < 100; i++)
{
if (bs1.test(i) && bs2.test(i))
{
cout << i << endl;
}
}
}
十.布隆过滤器


十一.布隆过滤器误判率



十二.布隆过滤器代码大致实现
cpp
#pragma once
#include "BitSet.h"
template<class K,size_t N,size_t X,class Hash1,class Hash2,class Hash3>
class BloomFilter
{
public:
private:
static const size_t M = N * X;
ltw::bitset<M> _bs;
};
这里我们开的空间是N * X个位置
十三.set的实现
根据三个不同的hash函数,将我们的值进行映射
cpp
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);
}
十四.test的实现
cpp
bool test(const K& key)
{
size_t hash1 = Hash1()(key) % M;
if(!_bs.test(hash1))
{
return false;
}
size_t hash2 = Hash2()(key) % M;
if(!_bs.test(hash2))
{
return false;
}
size_t hash3 = Hash3()(key) % M;
return _bs.test(hash3);
}
十五.对应的hash函数
我们可以直接在网上进行查找对应的hash函数
cpp
#include <string>
using namespace std;
// BKDR Hash 算法
struct HashFuncBKDR
{
/// @detail 本算法由于在Brian Kernighan与Dennis Ritchie的《The C Programming Language》
/// 一书被展示而得名,是一种简单快捷的hash算法,也是Java目前采用的字符串的Hash算法(累乘因子为31)。
size_t operator()(const string& s)
{
size_t hash = 0;
for (auto ch : s)
{
hash *= 31;
hash += ch;
}
return hash;
}
};
// AP Hash 算法 (Arash Partow)
struct HashFuncAP
{
// 由Arash Partow发明的一种hash算法。
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;
}
};
// DJB Hash 算法 (Daniel J. Bernstein)
struct HashFuncDJB
{
// 由Daniel J. Bernstein教授发明的一种hash算法。
size_t operator()(const string& s)
{
size_t hash = 5381;
for (auto ch : s)
{
hash = hash * 33 ^ ch;
}
return hash;
}
};
或者也可以自己写(一般都是乘31,131等)
十六.测试
cpp
void TestBloomFilter()
{
BloomFilter<10> bf;
bf.Set("猪八戒");
bf.Set("孙悟空");
cout << bf.test("猪八戒") << endl;
cout << bf.test("孙悟空") << endl;
cout << bf.test("沙僧") << endl;
cout << bf.test("唐僧") << endl;
}

我们可以看到是没有什么错误的(但是我们测试的数据太少了)
如果我们想降低冲突率,是可以增加我们的X,来进行调整的
十七.误判率计算

这里只是用公式进行计算

误判率其实差不多(真实的和公式计算的)
十八.布隆过滤器的删除问题


默认的布隆过滤器是不支持删除的(删除一个值可能会影响另一个值)
如果使用引用计数,会导致我们的计数成本变高

所以,引用计数会导致数据不正确(我们就要定时重建布隆过滤器)
十九.布隆过滤器的应用


二十.海量数据处理







这本质就将A文件和B文件的相同的字符串,会被映射到同一个小空间(公用一个空间)


