布隆过滤器的简单实现
布隆过滤器(BloomFilter)是1970年由布隆提出的,它实际上是一个很长的二进制向量和一系列随机映射函数
布隆过滤器可以用于检索一个元素是否在一个集合中,它的优点是空间效率和查询时间都远远超过一般的算法,缺点
是有一定的误识别率和删除困难。
布隆过滤器(Bloom Filter)的适用范围如下:
实现数据字典,数据的判重,或者集合求交集
基本原理如下:
它的内容是,一个位图加上k个独立的hash函数,通过k个hash函数,可以得到khash函数对应的值,
然后把这些hash函数对应的值存入位图中(把具体的某一位置为1),然后查找时,通过判断k个hash
函数对应的位是不是为1,如果有一个为1,就代表该元素不存在,如果全为1,则该元素很可能存在
结构体代码如下:
#define HASHFUNCMAXSIZE 2
#define BitMapCapacity 1024
typedef struct BitMap{
uint64_t* data;
size_t capacity;
}BitMap;
/*字符串哈希函数*/
typedef size_t(*HashFunc)(const char*);
typedef struct BloomFilter{
BitMap bitmap;
HashFunc hash_func[HASHFUNCMAXSIZE];
}BloomFilter;
接下来看具体怎么实现:
初始状态时,有一个包含m位的数组每一位都设置为0:
初始化代码如下:
/*哈希函数*/
size_t hashfunc0(const char* str) {
assert(str);
size_t hash = 0;
size_t ch = 0;
while (ch = (size_t)*str++) {
hash = hash * 131 + ch;
}
return hash;
}
size_t hashfunc1(const char* str) {
assert(str);
size_t hash = 0;
size_t ch = 0;
while (ch = (size_t)*str++) {
hash = hash * 65599 + ch;
}
return hash;
}
/*初始化布隆过滤器*/
void BloomFilterInit(BloomFilter* bf) {
if (bf == NULL) {
return;
}
/*初始化位图*/
BitMapInit(&bf->bitmap, BitMapCapacity);
/*初始化哈希函数*/
bf->hash_func[0] = hashfunc0;
bf->hash_func[1] = hashfunc1;
}
我这里只用了两个哈希函数,比较少,容易出现哈希冲突
继续解释布隆过滤器,为了表达S={x1, x2,…,xn}这样一个n个元素的集合,Bloom Filter使用k个相互独立的哈希函数(Hash Function),它们分别将集合中的每个元素映射到{1,…,m}的范围中,如图:
对任意一个元素x,第i个哈希函数映射的位置hi(x)就会被置为1(1≤i≤k)。注意,如果一个位置多次被置为1,那么只有第一次会起作用,后面几次将没有任何效果。
我实现的代码如下:
/*插入*/
void BloomFilterInsert(BloomFilter* bf, const char* str) {
assert(bf);
assert(str);
/*
**通过哈希函数,把要存储的字符串转换成一个哈希值,
**然后在位图中再把这个值得位置为1
**只有两个哈希函数,所以和其他值出现哈希冲突的可能性比较大
**所以可以多设置一些哈希函数
*/
int i = 0;
for (; i < HASHFUNCMAXSIZE; ++i) {
size_t offset = bf->hash_func[i](str) % BitMapCapacity;
BitMapSet(&bf->bitmap, offset);
}
return;
}
我的代码如下:
int BloomFilterExit(BloomFilter* bf, const char* str) {
if (bf == NULL || str == NULL) {
return 0;
}
int i = 0;
for (; i < HASHFUNCMAXSIZE; ++i) {
size_t offset = bf->hash_func[i](str) % BitMapCapacity;
int n = BitMapTest(&bf->bitmap, offset);
if (n == 0) {
/*
**表示在k个哈希函数中对应的位中
**有一个位不是1,那么,这个元素肯定不存在
*/
return 0;
}
}
return 1;
}
注意,在使用Bloom Filter判断一个元素是否属于某个集合时,会有一定的错误率,虽然如果哈希函数很多的话,
这个错误率会很低,但是也是会有的,所以布隆过滤器不适合那种需要零错误的情况
全部代码如下:
BloomFilter.h
#pragma once
#include<stddef.h>
#include"BitMap.h"
#define HASHFUNCMAXSIZE 2
#define BitMapCapacity 1024
/*字符串哈希函数*/
typedef size_t(*HashFunc)(const char*);
typedef struct BloomFilter{
BitMap bitmap;
HashFunc hash_func[HASHFUNCMAXSIZE];
}BloomFilter;
/*初始化布隆过滤器*/
void BloomFilterInit(BloomFilter* bf);
/*插入*/
void BloomFilterInsert(BloomFilter* bf, const char* str);
int BloomFilterExit(BloomFilter* bf, const char* str);
void BloomFilterDestory(BloomFilter* bf);
/*按照当前的设计是不允许删除的*/
BitMap.h
#pragma once
#include<stdint.h>
#include<stddef.h>
typedef struct BitMap{
uint64_t* data;
size_t capacity;
}BitMap;
/*初始化*/
void BitMapInit(BitMap* bm, size_t capacity);
/*把index位设置为1*/
void BitMapSet(BitMap* bm, size_t index);
/*把index位设置为0*/
void BitMapUnSet(BitMap* bm, size_t index);
/*测试index为1还是为0,如果是1,就返回1,否则返回0*/
int BitMapTest(BitMap* bm, size_t index);
/*把这个位图所有位都设置为1*/
void BitmapFill(BitMap* bm);
/*把整个位图所有位都设置为0*/
void BitMapClear(BitMap* bm);
/*销毁*/
void BitMapDestory(BitMap* bm);
BitMap.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"BitMap.h"
#include<stdlib.h>
#include<stdint.h>
#include<stddef.h>
#include<assert.h>
#include<string.h>
size_t DataSize(size_t capacity) {
/*sizeof(uint64_t)是8个字节, capacity表示需要存储的数据类型*/
return capacity / (sizeof(uint64_t)* 8) + 1;
}
/*初始化*/
void BitMapInit(BitMap* bm, size_t capacity) {
if (bm == NULL) {
return;
}
bm->capacity = capacity;
/*得到的size表示需要容量的几个64位的存储空间*/
size_t size = DataSize(capacity);
bm->data = (uint64_t*)malloc(sizeof(uint64_t)* size);
memset(bm->data, 0, sizeof(uint64_t)* size);
}
size_t GetOffset(size_t index, size_t* n, size_t* offset) {
assert(n);
assert(offset);
*n = index / (sizeof(uint64_t)* 8);
*offset = index % (sizeof(uint64_t)* 8);
}
/*把index位设置为1*/
/*
**用65为例子
**n = index / (sizeof(uint64_t) * 8); -> 1
**offset = index % (sizeof(uint64_t) * 8) -> 1
**data[n] |= (1lu << offset)
*/
void BitMapSet(BitMap* bm, size_t index) {
if (bm == NULL) {
return;
}
if (index >= bm->capacity) {
/*表示已经超过现在所能存储的数据容量 */
return;
}
size_t n = 0;
size_t offset = 0;
GetOffset(index, &n, &offset);
bm->data[n] |= (1lu << offset);
}
/*把index位设置为0*/
void BitMapUnSet(BitMap* bm, size_t index) {
if (bm == NULL) {
return;
}
size_t n, offset;
GetOffset(index, &n, &offset);
/*
**1左移offest位,然后求补,再与bm->data[n]一与
*/
bm->data[n] &= ~(1lu << offset);
}
/*测试index为1还是为0,如果是1,就返回1,否则返回0*/
int BitMapTest(BitMap* bm, size_t index) {
if (bm == NULL) {
return;
}
size_t n, offset;
GetOffset(index, &n, &offset);
int i = bm->data[n] & (1lu << offset);
return i != 0 ? 1 : 0;
}
/*把这个位图所有位都设置为1*/
void BitmapFill(BitMap* bm) {
if (bm == NULL) {
return;
}
/*这里的size是总共有多少个字节*/
size_t size = sizeof(uint64_t)* DataSize(bm->capacity);
memset(bm->data, 0xff, size);
}
/*把整个位图所有位都设置为0*/
void BitMapClear(BitMap* bm) {
if (bm == NULL) {
return;
}
size_t size = sizeof(uint64_t)* DataSize(bm->capacity);
memset(bm->data, 0, size);
}
/*销毁*/
void BitMapDestory(BitMap* bm) {
if (bm == NULL) {
return;
}
free(bm->data);
bm->capacity = 0;
}
BloomFilter.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"BloomFilter.h"
#include"BitMap.h"
#include<assert.h>
/*哈希函数*/
size_t hashfunc0(const char* str) {
assert(str);
size_t hash = 0;
size_t ch = 0;
while (ch = (size_t)*str++) {
hash = hash * 131 + ch;
}
return hash;
}
size_t hashfunc1(const char* str) {
assert(str);
size_t hash = 0;
size_t ch = 0;
while (ch = (size_t)*str++) {
hash = hash * 65599 + ch;
}
return hash;
}
/*初始化布隆过滤器*/
void BloomFilterInit(BloomFilter* bf) {
if (bf == NULL) {
return;
}
/*初始化位图*/
BitMapInit(&bf->bitmap, BitMapCapacity);
/*初始化哈希函数*/
bf->hash_func[0] = hashfunc0;
bf->hash_func[1] = hashfunc1;
}
int BloomFilterExit(BloomFilter* bf, const char* str) {
if (bf == NULL || str == NULL) {
return 0;
}
int i = 0;
for (; i < HASHFUNCMAXSIZE; ++i) {
size_t offset = bf->hash_func[i](str) % BitMapCapacity;
int n = BitMapTest(&bf->bitmap, offset);
if (n == 0) {
/*
**表示在k个哈希函数中对应的位中
**有一个位不是1,那么,这个元素肯定不存在
*/
return 0;
}
}
return 1;
}
void BloomFilterDestory(BloomFilter* bf) {
if (bf == NULL) {
return;
}
BitMapDestory(&bf->bitmap);
int i = 0;
for (; i < HASHFUNCMAXSIZE; ++i) {
bf->hash_func[i] = NULL;
}
}
///////////////////////////////////////////////////////////
//一下为测试代码
///////////////////////////////////////////////////////////
#if 1
#include<stdio.h>
#include<stdlib.h>
#define TESTHEAD printf("------------%s-------------\n",__FUNCTION__)
void TestInit() {
BloomFilter bf;
TESTHEAD;
BloomFilterInit(&bf);
printf("expect 1024, actual:%d\n", bf.bitmap.capacity);
printf("expect %p, actual:%p\n", hashfunc0, bf.hash_func[0]);
printf("expect %p, actual:%p\n", hashfunc1, bf.hash_func[1]);
}
void TestInsert() {
BloomFilter bf;
TESTHEAD;
BloomFilterInit(&bf);
BloomFilterInsert(&bf, "hello");
BloomFilterInsert(&bf, "world");
size_t offset1 = bf.hash_func[0]("hello") % BitMapCapacity;
size_t offset2 = bf.hash_func[1]("world") % BitMapCapacity;
int n1 = BitMapTest(&bf.bitmap, offset1);
int n2 = BitMapTest(&bf.bitmap, offset2);
printf("expect 1, actual:%d\n", n1);
printf("expect 1, actual:%d\n", n2);
}
void TestExit() {
BloomFilter bf;
TESTHEAD;
BloomFilterInit(&bf);
BloomFilterInsert(&bf, "hello");
BloomFilterInsert(&bf, "world");
int n1 = BloomFilterExit(&bf, "hello");
int n2 = BloomFilterExit(&bf, "world");
int n3 = BloomFilterExit(&bf, "bit");
printf("expect 1, acutal:%d\n", n1);
printf("expect 1, acutal:%d\n", n2);
printf("expect 0, acutal:%d\n", n3);
}
int main() {
TestInit();
TestInsert();
TestExit();
system("pause");
return 0;
}
#endif