海量数据去重-由BitMap引出的布隆过滤器

布隆过滤器(图文无关)

本文参考资源:

那些惊艳的算法们(一)——布隆过滤器_C/C++_xinzhongtianxia的博客-CSDN博客

详解布隆过滤器的原理、使用场景和注意事项 - 简书

概述

昨天学习了BitMap这种数据结构(算法),链接:海量数据算法-BitMap介绍和实现

然而BitMap也存在着一些问题:

  1. 当样本分布极度不均匀的时候,BitMap会造成很大空间上的浪费。

举个例子,比如你有10个数,分别是1、2、3、4、5、6、7、8、99999999999;那么你不得不用99999999999个bit位去实现你的BitMap,而这个BitMap的中间绝大多数位置都是0,并且永远不会用到,这显然是极度不划算的。

  1. 当元素不是整型的时候,BitMap就不适用了。
    想想看,你拿到的是一堆url,然后如果你想用BitMap做去重的话,先得把url转换成int型,在转换的过程中难免某些url会计算出相同的int值,于是BitMap的准确性就会降低。

那针对这两种情况有没有解决办法呢?

第一种分布不均匀的情况可以通过hash函数,将元素都映射到一个区间范围内,减少大段区间闲置造成的浪费,这很简单,取模就好了,难的是取模之后的值保证不相同,即不发生hash冲突。

第二种情况,把字符串映射成整数是必要的,那么唯一要做的就是保证我们的hash函数尽可能的减少hash冲突,一次不行我就多hash几次,hash还是容易碰撞,那我就扩大数组的范围,使hash值尽可能的均匀分布,减少hash冲突的概率。

基于这种思想,BloomFilter(布隆过滤器)诞生了。

原理

比如现在有10000个字符串,要进行去重操作,如果用bitmap的方法将字符串的哈希值对应到一个bit,由于字符串的哈希值不是唯一的,可能出现哈希冲突,而若两个字符串哈希冲突了,就会对应到同一个bit上,bitmap就会误判这两个字符串相等。

解决的方法就是,使用多个哈希函数,例如使用三个哈希函数,每个字符串可以计算出三个哈希映射,将所有映射的位标为1,而判断有没有重复的方法是,只要得到的三个映射位不是都已为1了,就认为该字符串没有重复。

海量数据去重-由BitMap引出的布隆过滤器

海量数据去重-由BitMap引出的布隆过滤器

可见布隆过滤器相较于BitMap功能较单一,无法根据已有的位倒推回原始数据,只能用作查询去重功能。

但这也不意味着布隆过滤器就不会误判,假如一个新的无重复的字符串,映射的位在之前就被其他字符串得到的哈希映射都标为了1,那么布隆过滤器也认为它是重复的。不过误判的概率较小(比BitMap小多了),在大部分生产环境下是可以接受的(因为有的时候不重复的字符串没多少,反而重复的字符串占了大多数,主要目的只是不用反复处理重复的字符串)。

参数调优

由上面的原理可知,对于布隆过滤器性能最重要的参数有两个:

  • bit数组的大小,bit越多且哈希映射分布均匀的条件下,哈希冲突的概率是越低的。
  • 哈希函数的个数,哈希函数的个数如果太少,更容易冲突,而如果哈希函数的个数太多,则将bit数组内的元素标为1的进度也会加快,从而造成哈希冲突。所以哈希函数的个数需要均衡。

误判率和数组bit个数的关系

关于具体的误判率计算维基百科给出了计算过程:Bloom filter - Wikipedia

对于一个期望的误判率p,期望的插入元素个数n,最佳数组bit个数m和哈希函数数量k的计算公式如下:

最佳数组bit个数

最佳哈希函数数量

所以当构造一个布隆过滤器的时候并且希望误判率可控时通常要传入期望的误判率、期望的插入元素个数等参数。

谷歌有java中BloomFilter的实现,有兴趣的可以自己去研究一下:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>25.1-jre</version>
</dependency>

应用

  • 重复URL的过滤
  • 邮箱黑名单的去重
  • 推荐去重

Redis4.0后开始以插件的形式支持布隆过滤器(安装后使用指令bf.addbf.exist

Redis防止缓存穿透就可以使用布隆过滤器,日后再谈。

原创文章,作者:彭晨涛,如若转载,请注明出处:https://www.codetool.top/article/%e6%b5%b7%e9%87%8f%e6%95%b0%e6%8d%ae%e5%8e%bb%e9%87%8d-%e7%94%b1bitmap%e5%bc%95%e5%87%ba%e7%9a%84%e5%b8%83%e9%9a%86%e8%bf%87%e6%bb%a4%e5%99%a8/

(3)
彭晨涛彭晨涛管理者
上一篇 2020年2月28日
下一篇 2020年2月28日

相关推荐

  • leetcode771-宝石与石头

    原题 给定字符串J 代表石头中宝石的类型,和字符串 S代表你拥有的石头。 S 中每个字符代表了一种你拥有的石头的类型,你想知道你拥有的石头中有多少是宝石。 J 中的字母不重复,J …

    算法 2019年12月26日
    0360
  • leetcode123-买卖股票的最佳时机III

    原题 给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。 设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。 注意: 你不能同时参与多笔交易(你…

    算法 2020年6月16日
    01540
  • leetcode779-第K个语法符号

    原题 在第一行我们写上一个 0。接下来的每一行,将前一行中的0替换为01,1替换为10。 给定行数 N 和序数 K,返回第 N 行中第 K个字符。(K从1开始) 例子: 输入: N…

    算法 2020年1月22日
    0110
  • leetcode98-验证二叉搜索树

    原题 给定一个二叉树,判断其是否是一个有效的二叉搜索树。 假设一个二叉搜索树具有如下特征: 节点的左子树只包含小于当前节点的数。 节点的右子树只包含大于当前节点的数。 所有左子树和…

    算法 2020年1月15日
    0120
  • leetcode974-和可被K整除的子数组

    原题 给定一个整数数组 A,返回其中元素之和可被 K 整除的(连续、非空)子数组的数目。 示例: 输入: A = [4,5,0,-2,-3,1], K = 5 输出: 7 解释: …

    算法 2020年5月27日
    0170
  • leetcode210-课程表II

    原题 现在你总共有 n 门课需要选,记为 0 到 n-1。 在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0…

    算法 2020年5月17日
    0130
  • leetcode24-两两交换链表中的节点

    原题 给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。 示例: 给定 1->2->3->…

    算法 2020年1月20日
    0200
  • leetcode445-两数相加II

    原题 给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。 你可以假设除了数字 0 之外,这两个数字都…

    算法 2020年4月14日
    0290
  • 使用递归函数逆序一个栈

    原题(来源:牛客网) 一个栈依次压入1,2,3,4,5那么从栈顶到栈底分别为5,4,3,2,1。将这个栈转置后,从栈顶到栈底为1,2,3,4,5,也就是实现了栈中元素的逆序,请设计…

    算法 2020年4月19日
    0250
  • leetcode611-有效三角形的个数

    原题 给定一个包含非负整数的数组,你的任务是统计其中可以组成三角形三条边的三元组个数。 示例1: 输入: [2,2,3,4] 输出: 3 解释: 有效的组合是: 2,3,4 (使用…

    2020年2月8日
    070

发表回复

登录后才能评论