首先,来看这样一个问题:在一个元素很多的集合中,判断某个元素是否在这个集合中。例如:有两个URL集合,每个集合中有1亿个URL字符串,每个字符串64字节,判断两个结合中重复的URL个数。如果使用Hash的方法,将所有的URL都存入hash表中,那么至多需要的内存是:2*1*10^8*64=12.8GB。这么大的空间需求是普通计算机的内存无法满足的。那么解决这个问题有两种方法:
(1)使用分治的思想,将两个URL集合F与G分成若干个小文件集合{f1、f2……fk}、{g1、g2……gk},先把f1放入内存中并存入hash表,随后依次将g1、g2……gk放入内存中,与f1构成的hash表进行比对,将重复的URL挑出来存入文件中。之后,再依次放f2……fk到内存,进行下一轮的比较。
(2)如果允许一定的误差,可以使用布隆过滤器的思想。
布隆过滤器是由布隆在1970年提出的,是一种空间效率很高的数据结构,实际上由k个hash函数和一个range范围的bitmap位图数组组成。布隆过滤器主要是解决这种使用Hash需要开辟超大空间而无法处理的问题。它的优先是时间和空间效率都很高,缺点是可能存在一定的误差且删除困难。实现方法如下:
1. 首先需要k个hash函数,每个函数可以把key散列成为1个整数
2. 初始化时,需要一个长度为range比特的位图数组,每个比特位初始化为0
3. 插入某个key到集合时,用k个hash函数计算出k个散列值,并把位图数组中对应的比特位置为1
4. 判断某个key是否在集合时,用k个hash函数计算出k个散列值,并查询数组中对应的比特位,如果所有的比特位都是1,则在集合中。
下面是布隆过滤器的简单实现:
#include<iostream> #include<bitset> #include<vector> using namespace std; const int maxrang = 2<<21; int seedsBuilder[10] = {3, 7, 11, 13, 31, 37, 61, 71, 79, 97}; //使用10个hash函数 class StandardBloomFilter { public: bitset<maxrang> bloomset; vector<int> seeds; int hashNum; StandardBloomFilter() { hashNum = 0; } StandardBloomFilter(int k) { hashNum = k; for(int i=0 ; i<hashNum; ++i) { seeds.push_back(seedsBuilder[i]); } } int gethashNum(string word, int n) { int res = 0; for(int i=0;i<word.size();++i) { res = seeds[n]*res + (int)word[i]; if(res > maxrang ) res = res % maxrang; } return res; } void add(string word) { for(int i=0; i<hashNum; ++i) { int val = gethashNum(word,i); bloomset.set(val, 1); } } bool IsContains(string &word) { for(int i=0; i<hashNum; ++i) { int val = gethashNum(word, i); if(bloomset[val] == 0) { cout<<"the URL "<<word<<" is not in the URL set. "; return false; } } cout<<"the URL "<<word<<" is not in the URL set. "; return true; } }; int main() { StandardBloomFilter bg(6); StandardBloomFilter bf(6); bf.add("https://blog.csdn.net/u010150046/article/details/77069550"); bf.add("http://blog.51cto.com/13449864/2082652"); bf.add("https://www.nowcoder.com/discuss"); bf.add("https://www.cnblogs.com/alantu2018/p/8464088.html"); bf.add("http://www.cnblogs.com/ladawn/"); string URL_Item = "http://www.cnblogs.com/ladawn/"; bf.IsContains(URL_Item); URL_Item = "http://www.baidu.com"; bf.IsContains(URL_Item); return 0; }
程序运行结果如下:
