大数据排重算法-布隆算法(BloomFilter)

来源:互联网 发布:c语言源代码大全 编辑:程序博客网 时间:2024/05/16 14:36

前续:网页上已经有很多布隆过滤器很全的资料了,由于博主最近在做网页爬虫,遇到url防重问题,所以认真分析了布隆滤波器原理,也参考了相关博文。旨在给出不同人对其不同的理解,好给大家更全面的参考。

BloomFilter算法,是一种大数据排重算法。在一个数据量很大的集合里,能准确断定一个对象不在集合里;判断一个对象有可能在集合里,而且占用的空间不大。它不适合那种要求准确率很高的情况,零错误的场景。通过牺牲部分准确率达到高效利用空间的目的。

场景一:假如有一个很大的表,通过字段key查询数据,操作很重;业务方请求时,传过来的key有很大一部分是不存在的;这种不存在的key请求就会浪费我们的查询资源。针对这种情况,我们可以引人BloomFilter算法,在请求key查询之前,使用BloomFilter匹配。如果不存在,就不用去查询了(正确率百分之百);如果存在,走原来的查询流程(有可能不存在的key混进去了)。场景二:假如有一个很大的表,通过字段key判断是否存在,操作很重,如果存在就做一些操作,不存在就加入表中;可容许一定的误判。对应这种情况,我们也可以引入BloomFilter算法,通过key查询表判断存在否的方式可换成BloomFilter算法。如果存在,我们执行以前的逻辑(有一定的误判,业务也允许一定的错误);如果不存在,也执行以前的逻辑。 BloomFilter是由一个长度为n的bit数组S和k个hash算法组成。先使bit数组的初始值为0. 添加值M:M经过k个hash算法计算后,得到:M1, M2 … Mk; 然后,使S[M1]=1,S[M2]=2... S[Mk]=1 判断值Y:Y经过k个hash算法计算后,得到:Y1,Y2... Yk。 然后,判断S[Y1],S[Y2] … S[Yk] 是否都为1。如果有一个不为1,那这个Y就一定是不存在的,以前没添加过;如果都为1,那这个Y可能存在,也可能其他值添加后,影响了这次判断的结果。 我们要做的是尽量降低正确判断的误判率,资料显示, 当 k = ln(2)* m/n 时(k是hash函数个数,m是bit数组的长度,n是加入值的个数),出错概率是最小的。 

当然,如果我们要移除值,怎么办呢?当前的结构是没法实现的,我们可以通过在加一个等长的数据,存放每个bit位设置为1的次数,设置一次加1,取消一次减一。

 package com.ljt.algorithm;import java.util.BitSet;/** * BloomFilter算法,是一种大数据排重算法。在一个数据量很大的集合里,能准确断定一个对象不在集合里;判断一个对象有可能在集合里,而且占用的空间不大。它不适合那种要求准确率很高的情况,零错误的场景。通过牺牲部分准确率达到高效利用空间的目的。 *  * 场景一:假如有一个很大的表,通过字段key查询数据,操作很重;业务方请求时,传过来的key有很大一部分是不存在的;这种不存在的key请求就会浪费我们的查询资源。针对这种情况,我们可以引人BloomFilter算法,在请求key查询之前,使用BloomFilter匹配。如果不存在,就不用去查询了(正确率百分之百);如果存在,走原来的查询流程(有可能不存在的key混进去了)。 *  * 场景二:假如有一个很大的表,通过字段key判断是否存在,操作很重,如果存在就做一些操作,不存在就加入表中;可容许一定的误判。对应这种情况,我们也可以引入BloomFilter算法,通过key查询表判断存在否的方式可换成BloomFilter算法。如果存在,我们执行以前的逻辑(有一定的误判,业务也允许一定的错误);如果不存在,也执行以前的逻辑。 *  * BloomFilter是由一个长度为n的bit数组S和k个hash算法组成。先使bit数组的初始值为0. * 添加值M:M经过k个hash算法计算后,得到:M1, M2 … Mk; 然后,使S[M1]=1,S[M2]=2... S[Mk]=1 * 判断值Y:Y经过k个hash算法计算后,得到:Y1,Y2... Yk。 然后,判断S[Y1],S[Y2] … S[Yk] * 是否都为1。如果有一个不为1,那这个Y就一定是不存在的,以前没添加过;如果都为1,那这个Y可能存在,也可能其他值添加后,影响了这次判断的结果。 *  * 我们要做的是尽量降低正确判断的误判率,资料显示, 当 k = ln(2)* m/n * 时(k是hash函数个数,m是bit数组的长度,n是加入值的个数),出错概率是最小的。 *  * 当然,如果我们要移除值,怎么办呢?当前的结构是没法实现的,我们可以通过在加一个等长的数据,存放每个bit位设置为1的次数,设置一次加1,取消一次减一。 */public class SimpleBloomFilter {    public static final int BLOOMSIZE = 2 << 24; // 规定bloom的长度24bits    public static final int[] seeds = { 3, 5, 7, 11, 13, 31, 37, 61, 131 }; // 8个hashset函数    private BitSet bits = new BitSet(BLOOMSIZE); // 定义一个24位长的bit 所有位初始值都是false    // 把字符串加到布隆滤波器中,简而言之就是把该字符串的相应hashcode映射到bits上    public boolean add(String s) {        if (s.equals("") || (s == null)) {            return false;        }        for (int i = 0; i < seeds.length; i++) {            HashCodeGen hcg = new HashCodeGen(seeds[i]);            int codeGen = hcg.hashCodeGen(s);            bits.set(codeGen, true);        }        return true;    }    // 判断该字符串是否存在    public boolean contain(String s) throws Exception {        if (s.equals("") || (s == null)) { // 输入的字符串需要控制            throw new Exception("非法输入字符串");        }        boolean ret = true;        for (int i = 0; i < seeds.length; i++) {            HashCodeGen hcg = new HashCodeGen(seeds[i]);// 生成seeds长度的对象            ret = ret && bits.get(hcg.hashCodeGen(s));            if (ret == false)                break;        }        return ret;    }    public static void main(String[] args) {        SimpleBloomFilter bfn = new SimpleBloomFilter();        bfn.add("www.baidu.com");        try {            System.out.println(bfn.contain("www.baidu.com.cn"));        } catch (Exception e) {            e.printStackTrace();        }    }}class HashCodeGen {    private int seed = 0;    public HashCodeGen(int seed) {        this.seed = seed;    }    // 生成hash码    public int hashCodeGen(String s) {        int hash = 0;        for (int i = 0; i < s.length(); i++) {            hash = hash * seed + s.charAt(i);        }        return (hash & 0x7ffffff);    }}