布隆過濾器
他實際上是一個很長的二進制向量和一系列隨機映射函數,用於檢索一個元素是否在一個集合中,當一個元素被加入到集合中,通過K個hash函數將元素映射到位隊列的K個點中,即將對應的比特位置爲1,檢索時我們只需要知道這些點是不是1就能大約知道這個元素是不是再集合中。如果這些點中任何一個爲0,就一定不存在。如果這些點每個都爲1,那麼元素可能存在在集合中。實現布隆過濾器
bitMap.h
#pragma once
#include<iostream>
#include<stdlib.h>
#include<vector>
using namespace std;
class bitMap
{
public:
bitMap(size_t size = 100)
{
_vc.resize((size >> 5) + 1);
}
void set(size_t key)
{
size_t byte = key >> 5;
size_t bit = key % 32;
_vc[byte] |= (1 << bit);
}
void reset(size_t key)
{
size_t byte = key >> 5;
size_t bit = key % 32;
_vc[byte] &= ~(1 << bit);
}
bool Test(size_t key)
{
size_t byte = key >> 5;
size_t bit = key % 32;
if ((_vc[byte]) & (1 << bit))
return true;
return false;
}
private:
vector<int> _vc;
};
HashFun.h
template<class T> //各類哈希字符串轉換函數
size_t BKDRHash(const char *str)
{
register size_t hash = 0;
while (size_t ch = (size_t)*str++)
{
hash = hash * 131 + ch;
}
return hash;
}
template<class T>
size_t SDBMHash(const char *str)
{
register size_t hash = 0;
while (size_t ch = (size_t)*str++)
{
hash = 65599 * hash + ch;
}
return hash;
}
template<class T>
size_t RSHash(const char * str)
{
size_t hash = 0;
size_t magic = 63689;
while (size_t ch = (size_t)*str++)
{
hash = hash * magic + ch;
magic *= 378551;
}
return hash;
}
template<class T>
size_t APHash(const char *str)
{
register size_t hash = 0;
size_t ch;
for (long i = 0; ch = (size_t)*str++; i++)
{
if ((i & 1) == 0)
{
hash ^= ((hash << 7) ^ ch ^ (hash >> 3));
}
else
{
hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));
}
}
return hash;
}
template<class T>
size_t JSHash(const char* str)
{
if (!*str)
{
return 0;
}
size_t hash = 1315423911;
while (size_t ch = (size_t)*str++)
{
hash ^= ((hash << 5) + ch + (hash >> 2));
}
return hash;
}
Bloom_Filter.h
#pragma once
#include"bitMap.hpp"
#include"HashFun.h"
#include<string>
template <typename T>
struct HashFun1
{
size_t operator()(const T& str)
{
return BKDRHash<T>(str.c_str());
}
};
template <typename T>
struct HashFun2
{
size_t operator()(const T& str)
{
return SDBMHash<T>(str.c_str());
}
};
template <typename T>
struct HashFun3
{
size_t operator()(const T& str)
{
return RSHash<T>(str.c_str());
}
};
template <typename T>
struct HashFun4
{
size_t operator()(const T& str)
{
return APHash<T>(str.c_str());
}
};
template <typename T>
struct HashFun5
{
size_t operator()(const T& str)
{
return JSHash<T>(str.c_str());
}
};
template<typename T = string,
typename HashFun1 = HashFun1<T>,
typename HashFun2 = HashFun2<T>,
typename HashFun3 = HashFun3<T>,
typename HashFun4 = HashFun4<T>,
typename HashFun5 = HashFun5<T>>
class Bloom_Filter
{
public:
Bloom_Filter(int size)
:_map(size)
, _range(size)
{}
void set(const T& str)
{
_map.set(HashFun1()(str)%_range);
_map.set(HashFun2()(str) % _range);
_map.set(HashFun3()(str) % _range);
_map.set(HashFun4()(str) % _range);
_map.set(HashFun5()(str) % _range);
}
bool test(const T& str)
{
if (!_map.Test(HashFun1()(str) % _range))
return false;
if (!_map.Test(HashFun2()(str) % _range))
return false;
if (!_map.Test(HashFun3()(str) % _range))
return false;
if (!_map.Test(HashFun4()(str) % _range))
return false;
if (!_map.Test(HashFun5()(str) % _range))
return false;
return true;
}
private:
bitMap _map;
size_t _range;
};
Bloom_Filter.cpp
#include"Bloom_Filter.h"
int main()
{
Bloom_Filter<string> _bf(1024);
_bf.set("hjku");
bool ret = _bf.test("hjku");
ret = _bf.test("dfg");
return 0;
}
- 優點:一般判斷一個元素是不是在集合裏,我們需要將元素保存起來,在通過比較來判斷,這樣的話元素數量增大時我們需要很大的內存來存儲這些元素,並且檢索的速度也會越來越慢,布隆過濾器不需要存儲元素本身,並且採用哈希來確定元素的插入位置和查找元素,所以空間和時間效率都非常高,各個哈希函數之間沒有關係,方便硬件併發運行,而且不用存儲元素本身適合一些對保密有要求的場合。
- 缺點:由於hash函數特性以及位圖數組長度有限,不同的對象可能在某些位上有重疊,這些位上的1未必是該元素之前設置的,有可能是別的元素所設置的,所以會造成一些誤判,即原本不在bloom filter中的一些元素也被判別在bloom filter中。誤算率就是它的一個缺點,並且隨着元素的增加,誤算率也會越來越高。由於元素存儲是由多個hash函數決定的,所以一般情況下無法從中刪除元素,我們很容易想到把位列陣變成整數數組,每插入一個元素相應的計數器加1,這樣刪除元素時將計數器減掉就可以了。然而要保證安全的刪除元素並非如此簡單。首先我們必須保證刪除的元素的確在布隆過濾器裏面. 這一點單憑這個過濾器是無法保證的。另外計數器迴繞也會造成問題。
- 布隆過濾器的應用。
Google Chrome瀏覽器使用Bloom filter識別惡意鏈接
(能用較小的存儲空間表示較大的數據集合,簡單想就是把 每一個URL都可以映射成bit)
並且誤判率在萬分之一以下。