首页 > 其他分享 >数据结构之布隆过滤器

数据结构之布隆过滤器

时间:2023-04-23 10:36:12浏览次数:33  
标签:之布隆 int 布隆 value 二进制位 哈希 过滤器 数据结构


布隆过滤器

如果要经常判断某个元素是否存在,你会怎么做?很容易想到使用哈希表(HashSet、HashMap),将元素作为key去查找。时间复杂度为O(1),但是空间利用率不高,需要占用比较多的内存资源。

如果需要编写一个网络爬虫去爬10亿个网站数据,为了避免爬到重复的网站,如何判断某个网站是否爬过?很显然,HashSet、HashMap并不是非常好的选择。

是否存在时间复杂度低、占用内存较少的方案?就是下面要学习的布隆过滤器。

布隆过滤器(Bloom Filter)

布隆过滤器(Bloom Filter)在1970年由布隆提出,它是一个空间效率高的概率型数据结构,可以用来告诉你:一个元素一定不存在或者可能存在。

它实质上是由一个很长的二进制向量和一系列随机映射函数(Hash函数)组成。

优点:空间效率和查询时间都远远超过一般的算法。

缺点:有一定的误判率、删除困难。

应用场景:网页黑名单系统、垃圾邮件过滤系统、爬虫的网址判重系统、解决缓存穿透问题。

开源实现:google的guava中com.google.common.hash.BloomFilter。

布隆过滤器的原理

假设布隆过滤器由20位二进制、3个哈希函数组成,每个元素经过哈希函数处理都能生成一个索引位置。

  • 添加元素(put):将每一个哈希函数生成的索引位置都设为1
  • 查询元素是否存在(contains):如果有一个哈希函数生成的索引位置不为1,就代表不存在(100%准确),如果每一个哈希函数生成的索引位置都为1,就代表可能存在(存在一定的误判率)。

数据结构之布隆过滤器_bloom filter

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

布隆过滤器的误判率

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

已知误判率p、数据规模n,求二进制位的个数m、哈希函数的个数k,可以使用下面的公式:

数据结构之布隆过滤器_java_02

布隆过滤器的实现

数据结构

private long[] bits; // 二进制位

    private int bitSize; // 二进制位数

    private int hashSize; // 哈希函数的个数

    /**
     * @param n 数据规模
     * @param p 误判率
     */
    public BloomFilter(int n, double p) {
        if (n <= 0 || p <= 0 || p >= 1) {
            throw new IllegalArgumentException("wrong n or p");
        }

        double ln2 = Math.log(2); // ln2

        bitSize = (int) (- (n * Math.log(p)) / ln2 / ln2); // 二进制位的个数

        // (bitSize + Long.SIZE - 1) / Long.SIZE = ceil(bitSize / Long.SIZE)
        bits = new long[(bitSize + Long.SIZE - 1) / Long.SIZE]; // 初始化二进制位

        hashSize = (int) (bitSize * ln2 / n); // 哈希函数的个数
    }

put

/**
     * 添加一个value
     *
     * @param value
     * @return
     */
    public boolean put(T value) {
        nullCheck(value);

        int hash1 = value.hashCode();
        int hash2 = value.hashCode() >> 16;

        boolean result = false;
        for (int i = 1; i <= hashSize; i++) {
            int combinedHash = hash1 + (i * hash2);
            if (combinedHash < 0) {
                combinedHash = ~combinedHash;
            }

            int index = combinedHash % bitSize;
            if (set(index)) {
                result = true;
            }
        }

        return result;
    }

    public boolean set(int index) {

        int bitsIndex = index / Long.SIZE; // 定位到bits数组中long元素的索引

        long value = bits[bitsIndex]; // 定位到long元素

        int mask = 1 << (index % Long.SIZE);

        long bit = value & mask; // 取出第[index % Long.SIZE]位

        bits[bitsIndex] = value | mask; // 将第[index % Long.SIZE]位设置为1

        return bit == 0;
    }

    private void nullCheck(T value) {
        if (value == null) {
            throw new IllegalArgumentException("Value must not be null.");
        }
    }

contains

/**
     * 判断value是否存在,如果返回false,表示一定不存在,如果返回true,表示可能存在
     *
     * @param value
     * @return
     */
    public boolean contains(T value) {
        nullCheck(value);

        int hash1 = value.hashCode();
        int hash2 = value.hashCode() >> 16;

        for (int i = 0; i < hashSize; i++) {
            int combinedHash = hash1 + (i * hash2);
            if (combinedHash < 0) {
                combinedHash = ~combinedHash;
            }

            int index = combinedHash % bitSize;
            if (!get(index)) {
                // 只要有一个为0,则返回false
                return false;
            }
        }

        return true;
    }

    private boolean get(int index) {
        long value = bits[index / Long.SIZE]; // 定位到long元素
        int mask = 1 << (index % Long.SIZE);
        long bit = value & mask; // 取出第[index % Long.SIZE]位
        return bit != 0;
    }

更多精彩内容关注本人公众号:架构师升级之路

数据结构之布隆过滤器_java_03


标签:之布隆,int,布隆,value,二进制位,哈希,过滤器,数据结构
From: https://blog.51cto.com/u_6784072/6216461

相关文章

  • 数据结构中的集合
    原文点此跳转什么是集合?集合是一种无序且唯一的数据结构,其中的唯一是指集合中的元素。在ES6中新增了一种数据结构Set就是集合。实现功能new()实例化一个集合add()添加元素delete()删除元素has()判断是否存在元素size()获取集合大小应用场景去重判断某元素是否在集合中求两......
  • 数据结构题解
    W1#怪兽训练计划1##题目描述小明有一个怪兽训练计划。初始时,怪兽充满能量,能量值为8800。如果训练怪兽,每分钟损耗能量值400;如果让怪兽休息,每分钟增加能量值200。能量的损耗和增加都是均匀变化的。小明打算让怪兽训练一分钟、休息一分钟、再训练一分钟、再休息一分钟……如此......
  • SpringSecurity过滤器之HeaderWriterFilter
    HeaderWriterFilter用于对当前的HttpServletResponse添加某些浏览器保护的响应头。HeaderWriterFilter由HeadersConfigurer配置,在执行HeadersConfigurer#configure时调用createHeaderWriterFilter创建HeaderWriterFilter,同时添加了HeaderWriter集合:privateList<HeaderWriter>ge......
  • 【中级软件设计师】—(针对上午题)数据结构(二十八)
    【中级软件设计师】—(针对上午题)数据结构(二十八)一、时间复杂度二、空间复杂度123456递归的时间复杂度和空间复杂度......
  • redis数据结构
    ZipListziplist是一种特殊的“双向链表”,由一系列特殊编码的连续内存组成,可以在任意一端进行压入和弹出。ZipList的结构ZipListEntry的结构entry并不像普通双向链表节点用两个指针指向前后节点,为了节省空间。previous_entry_length:前一个节点的长度,占1个或5个字节如果......
  • 【中级软件设计师】—(针对上午题)数据结构(二十九)
    【中级软件设计师】—(针对上午题)数据结构(二十九)一、查找平均查找长度二、顺序查找三、折半查找(二分查找)1234567......
  • SpringSecurity过滤器之ExceptionTranslationFilter
    ExceptionTranslationFilter是处理AuthenticationException(身份认证异常)和AccessDeniedException(权限异常)。ExceptionTranslationFilter用法和源码分析参考一文搞定SpringSecurity异常处理机制!。 AuthenticationEntryPoint是处理AuthenticationException,默认实现是LoginUrl......
  • lua变量、数据类型、if判断条件和数据结构table以及【lua 函数】
    一、lua变量【全局变量和局部变量和表中的域】Lua变量有三种类型:全局变量和局部变量和表中的域。▪全局变量:默认情况下,Lua中所有的变量都是全局变量。▪局部变量:使用local显式声明在函数内的变量,以及函数的参数,都是局部变量。在函数外即使用local去声明,它的作用域也是当前的整......
  • 扎实打牢数据结构算法根基,从此不怕算法面试系列之002 week01 02-02 线性查找法
    1、线性查找法什么是线性查找法?举例:在一沓试卷中,找到属于自己的那张试卷。第1张:不是第2张:不是第3张:不是……第n张:是,找到了!第n+1张:不找了……这个解决问题的思路和过程体现就是线性查找法的思想。2、线性查找法思路梳理线性查找法,就是在线性的数据结构中来完成。例如:在data数......
  • 扎实打牢数据结构算法根基,从此不怕算法面试系列之001 week01 02-01 什么是算法?
    1、什么是算法?为了明确什么是算法,我们会从简单的查找功能开始讲起。查找其实一个一个非常简单的算法,但我们会为这个查找功能的算法做如下工作:让查找的功能适应更多的数据类型通过查找的例子讲解如何编写正确的程序?为查找算法性能测试对一些常见算法做复杂度分析2、定义算法Algorit......