Bloom Filter(布隆過濾器)學習實現(C++實現)
非常感謝評論裡指出了我程式碼裡的小問題。以下程式碼修改了一下,主要是在第二次 HasH 的時候有小問題。
Bloom filter簡介
Bloom filter 是由 Howard Bloom 在 1970 年提出的二進位制向量資料結構,它具有很好的空間和時間效率,被用來檢測一個元素是不是集合中的一個成員。如果檢測結果為是,該元素不一定在集合中;但如果檢測結果為否,該元素一定不在集合中。因此Bloom filter具有100%的召回率。這樣每個檢測請求返回有“在集合內(可能錯誤)”和“不在集合內(絕對不在集合內)”兩種情況,可見 Bloom filter 是犧牲了正確率和時間以節省空間。
以上文字來源
Bloom Filter計算方法
如需要判斷一個元素是不是在一個集合中,我們通常做法是把所有元素儲存下來,然後通過比較知道它是不是在集合內,連結串列、樹都是基於這種思路,當集合內元素個數的變大,我們需要的空間和時間都線性變大,檢索速度也越來越慢。 Bloom filter 採用的是雜湊函式的方法,將一個元素對映到一個 m 長度的陣列上的一個點,當這個點是 1 時,那麼這個元素在集合內,反之則不在集合內。這個方法的缺點就是當檢測的元素很多的時候可能有衝突,解決方法就是使用 k 個雜湊 函式對應 k 個點,如果所有點都是 1 的話,那麼元素在集合內,如果有 0 的話,元素則不在集合內。
Bloom Filter優點缺點
優點
- 插入時間和查詢時間都是常數。
- 儲存的不是資料本身,安全性好。
缺點
- 插入的元素越多,錯判性越大。
- 不能刪除元素。
圖示說明
例如我們有一個簡單的Bloom Filter結構如下:(所有位都是0)
[ 0 0 0 0 0 0 0 0 0 0 ]
第一次插入a
用兩個雜湊函式,對映到1 4
位置上,變為1.
[ 1 0 0 1 0 0 0 0 0 0 ]
第二次插入b
同樣的hash函式,對映到1 8
位置上, 變為1.
[ 1 0 0 1 0 0 0 1 0 0 ]
這樣就存放了a b
兩個元素,當我們查詢a
是否在的時候,兩次hash找到1 4
a
存在。
但是假如我們查詢的d
雜湊後對映到4 8
位置,發現也同時為 1. 認為存在,這就出錯了,因為現在裡面只存放了a b
沒有d
。
下面寫下我用C++寫的程式碼實現,比較簡單的實現了下,具體的Hash演算法我都簡略的寫了。
首先是標頭檔案Header.h
//
// Header.h
// BloomFilter
//
// Created by Alps on 15/3/19.
// Copyright (c) 2015年 chen. All rights reserved.
//
#ifndef BloomFilter_Header_h
#define BloomFilter_Header_h
class BitMap{
public:
BitMap(){
bitmap = NULL;
size = 0;
}
BitMap(int size){
bitmap = NULL;
bitmap = new char[size];
if (bitmap == NULL) {
printf("ErroR In BitMap Constractor!\n");
}else{
memset(bitmap, 0x0, size * sizeof(char));
this->size = size;
}
}
int initBitMap(int size){
bitmap = NULL;
bitmap = new char[size];
if (bitmap == NULL) {
printf("ErroR In BitMap Constractor!\n");
return 0;
}else{
memset(bitmap, 0x0, size * sizeof(char));
this->size = size;
return this->size;
}
}
/*
* set the index bit to 1;
*/
int bitmapSet(int index){
int addr = index/8;
int addroffset = index%8;
unsigned char temp = 0x1 << addroffset;
if (addr > (size+1)) {
return 0;
}else{
bitmap[addr] |= temp;
return 1;
}
}
/*
* return if the index in bitmap is 1;
*/
int bitmapGet(int index){
int addr = index/8;
int addroffset = index%8;
unsigned char temp = 0x1 << addroffset;
if (addr > (size + 1)) {
return 0;
}else{
return (bitmap[addr] & temp) > 0 ? 1 : 0;
}
}
/*
* del the index from 1 to 0
*/
int bitmapDel(int index){
if (bitmapGet(index) == 0) {
return 0;
}
int addr = index/8;
int addroffset = index%8;
unsigned char temp = 0x1 << addroffset;
if (addr > (size + 1)) {
return 0;
}else{
bitmap[addr] ^= temp;
return 1;
}
}
private:
char *bitmap;
int size;
};
#endif
標頭檔案儲存好,放到工程路徑下。
下面是BloomFilter.cpp
檔案了。
//
// main.cpp
// BloomFilter
//
// Created by Alps on 15/3/18.
// Copyright (c) 2015年 chen. All rights reserved.
//
#include <iostream>
#include "Header.h"
using namespace std;
template <class Type> class BloomFilter{
public:
BloomFilter();
BloomFilter(int length){
bitmap.initBitMap(length);
this->length = length;
}
bool Add(const Type &T);
bool Contains(const Type &T);
int HasH(const Type &T);
int SecondHasH(const Type &T);
private:
BitMap bitmap;
int length;
};
template <class Type> int BloomFilter<Type>::HasH(const Type &T){
int temp = (int) T;
return temp%length;
}
template <class Type> int BloomFilter<Type>::SecondHasH(const Type &T){
int temp = (int) T;
return temp%9973;
//這裡直接選了一個大素數 請各位自己優化自己的 HasH函式
//不然會出現頻繁的雜湊衝突,並且把布隆過濾器的大小盡量放大一點幾百萬都可以
}
template <class Type> bool BloomFilter<Type>::Add(const Type &T){
int first = HasH(T);
int second = SecondHasH(first);
if (bitmap.bitmapSet(first) && bitmap.bitmapSet(second)) {
return true;
}else{
return false;
}
}
template <class Type> bool BloomFilter<Type>::Contains(const Type &T){
int first = HasH(T);
int second = SecondHasH(T);
if (bitmap.bitmapGet(first) && bitmap.bitmapGet(second)) {
return true;
}else{
return false;
}
}
int main(int argc, const char * argv[]) {
BloomFilter<int> bloom(10);
bloom.Add(3);
if (bloom.Contains(3)) {
printf("true\n");
}else{
printf("false\n");
}
if (bloom.Contains(2)) {
printf("true\n");
}else{
printf("false\n");
}
return 0;
}
這就是我的程式碼了。