布隆过滤器(Bloom Filter)及 JavaAPI

1. Bloom Filter算法简介

Bloom-Filter,即布隆过滤器,1970年由Bloom中提出。它可以用于检索一个元素是否在一个集合中。

Bloom Filter(BF)是一种空间效率很高的随机数据结构,它利用位数组很简洁地表示一个集合,并能判断一个元素是否属于这个集合。它是一个判断元素是否存在集合的快速的概率算法。Bloom Filter有可能会出现错误判断,但不会漏掉判断。也就是Bloom Filter判断元素不再集合,那肯定不在。如果判断元素存在集合中,有一定的概率判断错误。因此,Bloom Filter”不适合那些“零错误的应用场合。而在能容忍低错误率的应用场合下,Bloom Filter比其他常见的算法(如hash,折半查找)极大节省了空间。 

它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。

2. Bloom Filter基本思想

 Bloom-Filter算法的核心思想就是利用多个不同的Hash函数来解决“冲突”。

 计算某元素x是否在一个集合中,首先能想到的方法就是将所有的已知元素保存起来构成一个集合R,然后用元素x跟这些R中的元素一一比较来判断是否存在于集合R中;我们可以采用链表等数据结构来实现。但是,随着集合R中元素的增加,其占用的内存将越来越大。试想,如果有几千万个不同网页需要下载,所需的内存将足以占用掉整个进程的内存地址空间。即使用MD5,UUID这些方法将URL转成固定的短小的字符串,内存占用也是相当巨大的。

于是,我们会想到用Hash table的数据结构,运用一个足够好的Hash函数将一个URL映射到二进制位数组(位图数组)中的某一位。如果该位已经被置为1,那么表示该URL已经存在。

Hash存在一个冲突(碰撞)的问题,用同一个Hash得到的两个URL的值有可能相同。为了减少冲突,我们可以多引入几个Hash,如果通过其中的一个Hash值我们得出某元素不在集合中,那么该元素肯定不在集合中。只有在所有的Hash函数告诉我们该元素在集合中时,才能确定该元素存在于集合中。这便是Bloom-Filter的基本思想。

1)位数组:

        假设Bloom Filter使用一个m比特的数组来保存信息,初始状态时,Bloom Filter是一个包含m位的位数组,每一位都置为0,即BF整个数组的元素都设置为0。

https://images2015.cnblogs.com/blog/567993/201603/567993-20160317225223334-1930594904.jpg

2)添加元素,k个独立hash函数

       为了表达S={x1, x2,…,xn}这样一个n个元素的集合,Bloom Filter使用k个相互独立的哈希函数(Hash Function),它们分别将集合中的每个元素映射到{1,…,m}的范围中。

         当我们往Bloom Filter中增加任意一个元素x时候,我们使用k个哈希函数得到k个哈希值,然后将数组中对应的比特位设置为1。即第i个哈希函数映射的位置hashi(x)就会被置为1(1≤i≤k)。

 注意,如果一个位置多次被置为1,那么只有第一次会起作用,后面几次将没有任何效果。在下图中,k=3,且有两个哈希函数选中同一个位置(从左边数第五位,即第二个“1“处)。   

https://images2015.cnblogs.com/blog/567993/201603/567993-20160317225223693-1768052613.jpg

 3)判断元素是否存在集合

    在判断y是否属于这个集合时,我们只需要对y使用k个哈希函数得到k个哈希值,如果所有hashi(y)的位置都是1(1≤i≤k),即k个位置都被设置为1了,那么我们就认为y是集合中的元素,否则就认为y不是集合中的元素。下图中y1就不是集合中的元素(因为y1有一处指向了“0”位)。y2或者属于这个集合,或者刚好是一个false positive。

https://images2015.cnblogs.com/blog/567993/201603/567993-20160317225223974-1196082220.jpg

一个Bloom Filter有以下参数:

m

bit数组的宽度(bit数)

n

加入其中的key的数量

k

使用的hash函数的个数

f

False Positive的比率

 

Bloom Filter的f满足下列公式:

https://images2015.cnblogs.com/blog/567993/201603/567993-20160317225224396-508919770.png

 

在给定mn时,能够使f最小化的k值为:

https://images2015.cnblogs.com/blog/567993/201603/567993-20160317225224756-1964138351.png

此时给出的f为:

https://images2015.cnblogs.com/blog/567993/201603/567993-20160317225225271-1692743268.png

根据以上公式,对于任意给定的f,我们有:

 

n = m ln(0.6185) / ln(f) 

 

同时,我们需要k个hash来达成这个目标:

 

k = - ln(f) / ln(2) 

 

由于k必须取整数,我们在Bloom Filter的程序实现中,还应该使用上面的公式来求得实际的f:

 

f = (1 – e-kn/m)k  

 

以上3个公式是程序实现Bloom Filter的关键公式。

4. BloomFilter的应用

    1.黑名单

       比如邮件黑名单过滤器,判断邮件地址是否在黑名单中

    2.排序(仅限于BitSet)

       仔细想想,其实BitSet在set(int value)的时候,“顺便”把value也给排序了。

    3.网络爬虫

        判断某个URL是否已经被爬取过

    4.K-V系统快速判断某个key是否存在

       典型的例子有Hbase,Hbase的每个Region中都包含一个BloomFilter,用于在查询时快速判断某个key在该region中是否存    在,如果不存在,直接返回,节省掉后续的查询。

4. JavaAPI

public class BloomFilter {
    private static final int BIT_SIZE = 2 << 28 ;//二进制向量的位数,相当于能存储1000万条url左右,误报率为千万分之一
    private static final int[] seeds = new int[]{3, 5, 7, 11, 13, 31, 37, 61};//用于生成信息指纹的8个随机数,最好选取质数

    private BitSet bits = new BitSet(BIT_SIZE);
    private Hash[] func = new Hash[seeds.length];//用于存储8个随机哈希值对象

    public BloomFilter2(){
        for(int i = 0; i < seeds.length; i++){
            func[i] = new Hash(BIT_SIZE, seeds[i]);
        }
    }

    /**
     * 像过滤器中添加字符串
     */
    public void addValue(String value)
    {
        //将字符串value哈希为8个或多个整数,然后在这些整数的bit上变为1
        if(value != null){
            for(Hash f : func)
                bits.set(f.hash(value), true);
        }

    }

    /**
     * 判断字符串是否包含在布隆过滤器中
     */
    public boolean contains(String value)
    {
        if(value == null)
            return false;

        boolean ret = true;

        //将要比较的字符串重新以上述方法计算hash值,再与布隆过滤器比对
        for(Hash f : func)
            ret = ret && bits.get(f.hash(value));
        return ret;
    }

    /**
     * 随机哈希值对象
     */

    public static class Hash{
        private int size;//二进制向量数组大小
        private int seed;//随机数种子

        public Hash(int cap, int seed){
            this.size = 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 (size - 1) & result;
        }
    }

    public static void main(String[] args) {
        String[] values = {
                "www.baidu.com", "www.iqiyi.com", "weibo.com", "mail.163.com", "www.baidu.com""
        };
        BloomFilter2 bf = new BloomFilter2();
        for (int i = 0; i < values.length; i++) {
            System.out.println("\nURL: " + values[i]);
            System.out.println("加入前");
            System.out.println("是否已经存在:" + bf.contains(values[i]));
            bf.addValue(values[i]);
            System.out.println("加入后");
            System.out.println("是否已经存在:" + bf.contains(values[i]));
        }
//		Arrays.stream(values).forEach(str -> {
//			for(int i = 0; i < str.length(); i++){
//				System.out.println(str+": hashcode"+i+":"+(int)(str.charAt(i)));
//			}
//		});
    }

}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值