布隆过滤器的实现(c++)
简要介绍一下布隆过滤器:
如果想要判断一个元素是不是在一个集合里,一般想到的是将所有元素保存起来,然后通过比较确定。链表,树等等数据结构都是这种思路. 但是随着集合中元素的增加,我们需要的存储空间越来越大,检索速度也越来越慢(O(n),O(logn))。不过世界上还有一种叫作散列表(又叫哈希表,Hash table)的数据结构。它可以通过一个Hash函数将一个元素映射成一个位阵列(Bit array)中的一个点。这样一来,我们只要看看这个点是不是1就可以知道集合中有没有它了。这就是布隆过滤器的基本思想。(摘自百度百科)
实现:
1.计算需要的哈希函数个数k及需要申请的内存长度m(m是二进制长度 ,申请时可以申请((m/32)+1 个int 类型),
2.将文件中的每一个样本经k个哈希函数求得k个哈希值 ,并对每一个哈希值取模(哈希值%m),并将申请的内存中对应位置1(如果本来是1,则不改变),
3.判断某样本是否存在,只需重复第二步,根据哈希函数计算每一个哈希值,取模并看内存中的每个对应位是否为1。
查找的时间复杂度为O(1)。
且并不能保证零失误率(本来在集合里的元素一定可以判断正确,但是不在集合中的元素也有可能判断为在集合中)。
下图为m,k的计算公式,其中n为样本数量,p为样本失误率,k为需要哈希函数的个数,m为需要申请的内存长度(注意是二进制)。
P真为 真实的失误率。
代码如下:
#ifndef _BLOOMFILTER_
#define _BLOOMFILTER_
#include<math.h>
#include<vector>
using namespace std;
unsigned int SDBMHash(const char *str);
unsigned int RSHash(const char *str);
unsigned int JSHash(const char *str);
unsigned int PJWHash(const char *str);
unsigned int APHash(const char *str);
unsigned int DJBHash(const char *str);
unsigned int ELFHash(const char *str);
unsigned int BKDRHash(const char *str);
class Bloomfilter{
public:
Bloomfilter(double err_rate,int num,char* path); //传入样本的文档路径,样本个数,期望的失误率,注意计算得到的哈希函数个数k需要不大于hashtable的size
~Bloomfilter();
bool is_contain(const char* str); //查看字符串是否在样本中存在
int hashnum(); //返回k
// double real_precision(); //返回真实的失误率
int sizeofpool(); //返回len
void filter_init(); //初始化布隆过滤器
private:
void listinit(); //打开path路径的文档,计算每一行样本到内存bitpool中
int hashtable_init(); //把几个哈希函数加入到vector<unsigned (*)(const char*)> hastable容器中,必须大于k
int len;
char* mypath; //文件的路径,通过构造函数传入路径
Bloomfilter()=delete;
double precision;
int *bitpool; //需要内存的长度,在构造函数中申请
int bitpoollen; //需要的二进制位数m
int hashfuncnum; //需要的哈希函数的个数k, k <=hashtable.size();
int samplenum; //样本个数,构造函数传入
vector<unsigned int (*)(const char*)> hashtable; //存放计算字符串哈希值的哈希函数
};
#endif
#include<iostream>
#include<string>
#include<math.h>
#include<stdio.h>
#include"bloomfilter.h"
#include<fstream>
double lg2(double n)
{
return log(n)/log(2);
}
using namespace std;
int Bloomfilter::hashtable_init()
{
hashtable.push_back(*PJWHash);
hashtable.push_back(*JSHash);
hashtable.push_back(*RSHash);
hashtable.push_back(*SDBMHash);
hashtable.push_back(*APHash);
hashtable.push_back(*DJBHash);
hashtable.push_back(*BKDRHash);
hashtable.push_back(*ELFHash);
return hashtable.size();
}
Bloomfilter::Bloomfilter(double err_rate,int num,char* path)
{
mypath=path;
samplenum=num;
bitpoollen=-((samplenum*log(err_rate))/(log(2)*log(2)));
hashfuncnum=0.7*(bitpoollen/samplenum);
len=bitpoollen/32+1;
bitpool=new int[len];
}
int Bloomfilter::hashnum()
{
return hashfuncnum;
}
int Bloomfilter::sizeofpool()
{
return len;
}
void Bloomfilter::filter_init()
{
hashtable_init();
if(hashfuncnum>hashtable.size())
{
cout<<"哈系表中的函数不足,请添加"<<endl;
exit(0);
}
listinit();
}
bool Bloomfilter::is_contain(const char* str)
{
int hashval;
for(int i=0;i!=hashfuncnum;i++)
{
hashval=hashtable[i](str);
//cout<<hashval<<" "; //test
hashval=hashval%(len*32); //len*32为bitpool的总位数
if(bitpool[hashval/32]&(0x1<<(hashval%32)))
continue;
else
return false;
}
return true;
}
void Bloomfilter::listinit()
{
FILE* fp;
char* buf;
size_t length=0;
fp=fopen(mypath,"r+");
int hashval;
char* p;
while(getline(&buf,&length,fp)!=EOF)
{
p=buf;
while(*p!='\n')
{
p++;
}
*p='\0';
for(int i=0;i!=hashfuncnum;i++)
{
hashval=hashtable[i](buf);
// cout<<hashval<<" "; //test
hashval=hashval%(len*32);
bitpool[hashval/32]|=(0x1<<(hashval%32));
}
}
fclose(fp);
}
Bloomfilter::~Bloomfilter()
{
delete []bitpool;
}
测试代码:
#include<iostream>
#include"bloomfilter.h"
using namespace std;
int main()
{
Bloomfilter mybloom(0.01,100,"redlist.txt");
mybloom.filter_init();
cout<<"需要的哈希函数的个数 :"<<mybloom.hashnum()<<endl;
cout<<"需要申请多少个int :"<<mybloom.sizeofpool()<<endl;
cout<<"www.dubai.com在我的集合中吗 :"<<(mybloom.is_contain("www.dubai.com")?"在":"不在")<<endl;
cout<<"www.qq.com在我的集合中吗 :"<<(mybloom.is_contain("www.qq.com")?"在":"不在")<<endl;
}
测试结果:
ps(篇幅有限,哈希函数的实现只把声明贴上了,定义自行百度);
谢谢阅读,欢迎指出错误!!