布隆过滤器
原理
如果要判断一个数是不是在一个集合里,一半想到的是将所有的元素保存起来,然后通过比较确定。但是随着集合中元素的增加,需要的存储空间越来越大,检索速度自然会变慢。这时会有人想到使用哈希表,将元素通过哈希函数映射到一个位阵列中,将相应的比特位置为1,这样就可以判断这个元素是不是在集合之中了。
但是哈希有一个很严重的问题,那就是哈希冲突。针对这个问题,我们的解决方法是使用多个哈希函数,用多个位表示一个值,如果它们之中有一个说元素不在集合中,那么这个元素势必就不在;但是反过来,它们都说在,却是不一定在集合之中,因为有可能它们在说谎。
优缺点
布隆过滤器就是用于检索一个元素是否字一个集合之中,它的优点是空间效率和查询时间都由于其他一般的算法,缺点是有一定的几率识别错误,并且删除困难。
之所以会出现删除困难,是因为由于哈希冲突,可能一个位被多次置1,如果我们直接删除,那么就会出现错误。如果一定要实现删除功能的话,可以想到将位数组换成一般的数组。将其初始化为0,然后每增加一个元素,相应的位置加1,删除的时候相应的位置减1就可以了。
算法实现
1. 我们实现的布隆过滤器,底层搭载的是位图,关于位图的概念及实现,参考这里。
2. 关于哈希函数们可以参考这篇文章http://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html,这里介绍了很多哈希函数。
- //comm.hpp
- //这里的哈希函数都来自上面的文章中
- #pragma once
- #include <string>
- using namespace std;
- class KeyToInt1
- {
- public:
- size_t operator()(const string& s)
- {
- return BKDRHash(s.c_str());
- }
- private:
- size_t BKDRHash(const char *str)
- {
- register size_t hash = 0;
- while (size_t ch = (size_t)*str++)
- {
- hash = hash * 131 + ch;
- }
- return hash;
- }
- };
-
- class KeyToInt2
- {
- public:
- size_t operator()(const string& s)
- {
- return SDBMHash(s.c_str());
- }
- private:
- size_t SDBMHash(const char *str)
- {
- register size_t hash = 0;
- while (size_t ch = (size_t)*str++)
- {
- hash = 65599 * hash + ch;
- //hash = (size_t)ch + (hash << 6) + (hash << 16) - hash;
- }
- return hash;
- }
- };
-
-
- class KeyToInt3
- {
- public:
- size_t operator()(const string& s)
- {
- return RSHash(s.c_str());
- }
- private:
- size_t RSHash(const char *str)
- {
- register size_t hash = 0;
- size_t magic = 63689;
- while (size_t ch = (size_t)*str++)
- {
- hash = hash * magic + ch;
- magic *= 378551;
- }
- return hash;
- }
- };
-
-
- class KeyToInt4
- {
- public:
- size_t operator()(const string & s)
- {
- return APHash(s.c_str());
- }
- private:
- 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;
- }
- };
-
-
- class KeyToInt5
- {
- public:
- size_t operator()(const string& s)
- {
- return JSHash(s.c_str());
- }
- private:
- size_t JSHash(const char *str)
- {
- if (!*str) // 这是由本人添加,以保证空字符串返回哈希值0
- return 0;
- register size_t hash = 1315423911;
- while (size_t ch = (size_t)*str++)
- {
- hash ^= ((hash << 5) + ch + (hash >> 2));
- }
- return hash;
- }
- };
- //具体实现
- #include "comm.hpp"
- #include "bitMap.hpp"
- #include <iostream>
-
- template <class K,class KToInt1 = KeyToInt1,class KToInt2 = KeyToInt2,class KToInt3 = KeyToInt3,class KToInt4 = KeyToInt4,class KToInt5 = KeyToInt5>
- class BloomFilter
- {
- public:
- BloomFilter(size_t size)//size表示存储的元素个数,5个比特位表示一个元素
- : _bmp(size * 10),_size(0)
- {}
-
- bool Insert(const K& key)//插入元素
- {
- size_t bitCount = _bmp.Size();//位图能存储bitCount个比特位
- size_t index1 = KToInt1()(key) % bitCount;//字符串转换成整型,转换后的数字可能大于位图的位数,所以要取模
- size_t index2 = KToInt2()(key) % bitCount;
- size_t index3 = KToInt3()(key) % bitCount;
- size_t index4 = KToInt4()(key) % bitCount;
- size_t index5 = KToInt5()(key) % bitCount;
-
- _bmp.Set(index1);//整型插入到位图中
- _bmp.Set(index2);
- _bmp.Set(index3);
- _bmp.Set(index4);
- _bmp.Set(index5);
- _size++;
-
- cout << index1 << " " << index2 << " " << index3 << " " << index4 << " " << index5;//打印索引,做测试
- cout << endl;
- return true;
- }
-
- bool IsInBloomFilter(const K& key)//判断一个元素是否在集合中
- {
- size_t bitCount = _bmp.Size();
- size_t index1 = KToInt1()(key) % bitCount;
- size_t index2 = KToInt2()(key) % bitCount;
- size_t index3 = KToInt3()(key) % bitCount;
- size_t index4 = KToInt4()(key) % bitCount;
- size_t index5 = KToInt5()(key) % bitCount;
- if (!_bmp.Test(index1))
- return false;//index1表明key不在位图中,说明key一定不在其中
- if (!_bmp.Test(index2))
- return false;
- if (!_bmp.Test(index3))
- return false;
- if (!_bmp.Test(index4))
- return false;
- if (!_bmp.Test(index5))
- return false;
- return true;//五个索引表明都在,key才有可能在其中
- }
-
- private:
- BitMap _bmp;//位图存储元素
- size_t _size;//表示存了几个元素
- };
-
-
- void test()
- {
- BloomFilter<string> b(5);
- b.Insert("小明");
- b.Insert("小黄");
- b.Insert("小花");
- b.Insert("小兰");
- b.Insert("小熊");
- b.Insert("小泽");
- if (b.IsInBloomFilter("小熊"))
- cout << "小熊在名单之中" << endl;
- if (!b.IsInBloomFilter("小王"))
- cout << "小王不在名单之中" << endl;
- }
测试结果: