布隆过滤器(Bloom Filter)

布隆过滤器(Bloom Filter)

一、什么是布隆过滤器

以前有过这种需求,抽象来说就是:两个集合S1和S2,相交,求如下ABC三部分分别有哪些元素?

输入图片说明

做法是:构造两个map,遍历。数据量小还可以,数据量大的话,就不可以了。直到看到“布隆过滤器”这个概念

1.1 定义

Bloom Filter是1970年由Bloom提出的。它实际上是一个很长的二进制向量和一系列随机映射函数(Hash函数)。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。Bloom Filter广泛的应用于各种需要查询的场合中,如Orocle的数据库,Google的BitTable也用了此技术。

1.2 特点

  1. 不存在漏报(False Negative),即某个元素在某个集合中,肯定能报出来。
  2. 可能存在误报(False Positive),即某个元素不在某个集合中,可能也被爆出来。
  3. 确定某个元素是否在某个集合中的代价和总的元素数目无关。

二、实现

2.1 java实现

import java.util.BitSet;

public class BloomFilter {

	// 位数组bits,长度为m,初值置为0
	// hash函数f,长度为k
	// 集合S,长度为n

  	// 布隆过滤器的比特长度 2^25
	private static final int DEFAULT_SIZE = 2 << 24;
    // 这里要选取质数,能很好的降低错误率
	private static final int[] seeds = { 3, 5, 7, 11, 13, 31, 37, 61 };
	
  	// 位数组
	private static BitSet bits = new BitSet(DEFAULT_SIZE); 
    // hash函数
	private static SimpleHash[] func = new SimpleHash[seeds.length]; 

	public static void addValue(String value) {
		// 将字符串value哈希为8个或多个整数,然后在这些整数的bit上变为1
      	for (SimpleHash f : func) {
			bits.set(f.hash(value), true);
        }
	}

	public static void add(String value) {
		if (value != null) {
			addValue(value);
		}
	}

	public static boolean contains(String value) {
		if (value == null) {
			return false;
		}
		boolean ret = true;
		for (SimpleHash f : func) { 
          	 // 这里其实没必要全部跑完,只要一次ret==false那么就不包含这个字符串
			ret = ret && bits.get(f.hash(value));
			if (!ret) {
				return ret;
			}
		}
		return ret;
	}

	public static void main(String[] args) {
		System.out.println(DEFAULT_SIZE);
		String value = "www..net";
		// 初始化hash函数
		for (int i = 0; i < seeds.length; i++) {
			func[i] = new SimpleHash(DEFAULT_SIZE, seeds[i]);
		}
		add(value);
		System.out.println(contains(value));
	}
}

class SimpleHash {// 这玩意相当于C++中的结构体
	private int cap;
	private int seed;

	public SimpleHash(int cap, int seed) {
		this.cap = cap;
		this.seed = seed;
	}

	public int hash(String value) {// 字符串哈希,选取好的哈希函数很重要
		int result = 0;
		int len = value.length();
		for (int i = 0; i < len; i++) {
			result = seed * result + value.charAt(i);
		}
		return (cap - 1) & result;
	}
}

2.2 c++实现

/*http://www.cnblogs.com/dolphin0520/archive/2012/11/10/2755089.html*/
/*布隆过滤器简易版本 2012.11.10*/

#include<iostream>
#include<bitset>
#include<string>
#define MAX 2<<24
using namespace std;

bitset<MAX> bloomSet;           //简化了由n和p生成m的过程 

int seeds[7]={3, 7, 11, 13, 31, 37, 61};     //使用7个hash函数 



int getHashValue(string str,int n)           //计算Hash值 
{
    int result=0;
    int i;
    for(i=0;i<str.size();i++)
    {
        result=seeds[n]*result+(int)str[i];
        if(result > 2<<24)
            result%=2<<24;
    }
    return result;
}


bool isInBloomSet(string str)                //判断是否在布隆过滤器中 
{
    int i;
    for(i=0;i<7;i++)
    {
        int hash=getHashValue(str,i);
        if(bloomSet[hash]==0)
            return false;
    }
    return true;
}

void addToBloomSet(string str)               //添加元素到布隆过滤器 
{
    int i;
    for(i=0;i<7;i++)
    {
        int hash=getHashValue(str,i);
        bloomSet.set(hash,1);
    }
}


void initBloomSet()                         //初始化布隆过滤器 
{
    addToBloomSet("http://www.baidu.com");
    addToBloomSet("http://www.cnblogs.com");
    addToBloomSet("http://www.google.com");
}


int main(int argc, char *argv[])
{
    
    int n;
    initBloomSet();
    while(scanf("%d",&n)==1)
    {
        string str;
        while(n--)
        {
            cin>>str;
            if(isInBloomSet(str))
                cout<<"yes"<<endl;
            else
                cout<<"no"<<endl;
        }
        
    }
    return 0;
}

三、应用场景

如何生成数千万不重复的固定长度的字符串?

一个实现

四、参考

网上大部分文章都是从下面这几篇文章中摘抄的。

  1. 数学之美系列二十一 - 布隆过滤器(Bloom Filter)
  2. 布隆过滤器-维基百科
  3. 布隆过滤器
  4. 布隆过滤器(Bloom Filter)的Java实现方法
  5. Hash和Bloom Filter

猜你喜欢

转载自my.oschina.net/yysue/blog/1618302