恋上数据结构与算法 —— 布隆过滤器

2021/6/6 20:22:25

本文主要是介绍恋上数据结构与算法 —— 布隆过滤器,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

布隆过滤器

    • 用途
  • 实现
    • 实现过程
    • 布隆过滤器的误判率
    • 应用
    • 复杂度分析
  • 代码实现

Bloom Filter(布隆过滤器)。
布隆过滤器是一个很长的二进制向量和映射函数。

用途

布隆过滤器可以用于检索一个元素是否在一个集合中
优点是空间效率和查询时间都会远远超过一般的算法,缺点是有一定的误识别率(针对存在的情况)和删除困难。

实现

布隆过滤器由二进制位和映射函数(Hash函数)组成。

Hash函数: 每个元素经过哈希函数处理后都能生成一个索引位置。
二进制位: 经过hash函数所映射过来的相应索引为会 被置为1.

实现过程

在这里插入图片描述

布隆过滤器的插入值操作:

  1. 待插入的值经过映射函数映射后,得到对应下标。
  2. 在位数组中相应位置就要置为1。

判断某个元素是否在布隆过滤器中的操作:

  1. 当有值要判断在不在布隆过滤器中,只需经过 相同的映射计算,得到的下标。
  2. 如果对应下标位置有为值0的情况,那么该数一定不再布隆过滤器中,如果都是1,则可能存在布隆过滤器中。

出现误判的原因:
不同的字符串可能哈希出来的位置相同(可以适当增加位数组大小或者调整我们的哈希函数来降低概率),如下图所示,B就被误判了。
在这里插入图片描述

布隆过滤器的误判率

误判率受3个因素影响: 二进制位的个数m、哈希函数的个数k、数据规模n

在这里插入图片描述

应用

比特币
分布式系统

复杂度分析

添加、查询的时间复杂度都是O(k),k是哈希函数的个数。
空间复杂度为O(m),m是二进制位数。

代码实现

package filter;

public class BloomFilter<T> {

    private long[] bits;
    //二进制位的个数
    private long bitSize;
    //哈希函数个数
    private int hashSize;

    /**
     * 传入误判率(p)和数据规模(n)
     * 计算所需的二进制向量长度(filterSize)和哈希函数个数(hashSize),从而构造出布隆过滤器
     * @param p 误判率
     * @param n 数据规模
     */
    public BloomFilter(double p, long n){

        if( p <= 0 || p >= 1 || n <= 0) throw  new IllegalArgumentException("Wrong argument!");
        double ln2 = Math.log(2);

        bitSize = (long)   (- (n * Math.log(p)) / (ln2 * ln2));
        hashSize = (int) (bitSize / n * ln2);
        // 1个long型整数有 Long.SIZE位
        bits = new long[(int) ((bitSize + Long.SIZE - 1) / Long.SIZE)];

//        System.out.println( bitSize + " "+ hashSize);
    }

    /**
     * 往布隆过滤器中增加数据
     * @param value
     * @return
     */
    public  boolean put(T value) {

        int hash = value.hashCode();
        int hash1 = hash >>> 16; //取无符号高16位
        boolean result = false;
        for(int i = 1; i <= hashSize; i ++){
            int combinedHash  = hash + (hash1 * i);
            //计算的hash值可能为负数
            if( combinedHash < 0) combinedHash = ~ combinedHash;
//            System.out.println(combinedHash);
            //计算索引
            int index = (int) (combinedHash % bitSize);
            if( set(index) ) result = true;

        }
        return result;
    }

    private boolean set(int index) {
        //把bits数组的每一个元素看成一个桶
        //首先计算落在哪个桶中
        int i = index / Long.SIZE;

        //然后计算在这个桶的第几位
        int j = index % Long.SIZE;

        //保存这个桶的值
        long value = bits[i];
        int bitValue = (1 << j );//1
        bits[i]|= bitValue;
        //只当i桶第j位为0时 也就是没有哈希冲突时返回true
        return (value & bitValue) == 0;
    }

    /**
     * 判断是否在布隆过滤器中
     * @param value
     * @return
     */
    public boolean contains(T value){
        int hash = value.hashCode();
        int hash1 = hash >>> 16; //取无符号高16位
        boolean result = false;
        for(int i = 1; i <= hashSize; i ++){
            int combinedHash  = hash + (hash1 * i);
            //计算的hash值可能为负数
            if( combinedHash < 0) combinedHash = ~ combinedHash;

            //计算索引
            int index = (int) (combinedHash % bitSize);
            if( !get(index) ) return false;

        }
        return true;
    }

    /**
     * 判断index位是否为1
     * @param index
     * @return
     */
    private boolean get(int index) {
        int i = index / Long.SIZE;

        //然后计算在这个桶的第几位
        int j = index % Long.SIZE;
        int bitValue = (int) ((bits[i] >> j) & 1);
        return bitValue == 1;
    }
}




这篇关于恋上数据结构与算法 —— 布隆过滤器的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程