目录

算法背景

布隆过滤器–概念

布隆过滤器—原理

布隆过滤器—缺点

布隆过滤器—实现

布隆过滤器—应用

布隆过滤器—公式推导


算法背景


问题:

在开发中,经常要判断一个元素是否在一个集合中。

实现方案:

编程中通常使用集合来存储所有元素,然后通过hash值来确定元素是否存在。

如:java中的HashMap、HashSet等。

优点:快速准确

缺点:耗费存储空间

瓶颈:

当集合比较小时,这个问题不明显

当集合比较大时,散列表存储效率低的问题越明显

如:判断邮件地址是否是发送垃圾邮件的地址

采用散列表:将每一个Email地址对应成一个8字节的信息指纹,然后存入散列表,由于散列表的存储效率一般只有50%,因此一个Email地址需要16个字节。一亿Email约1.6GB内存,存储几十亿个地址约上百GB的内存。

布隆过滤器–概念


布隆过滤器(英语:Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询效率都远远超过一般的算法,缺点是有一定的误判率和删除困难。

布隆过滤器—原理


布隆过滤器的原理是:

当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们置为1。检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:如果这些点有任何一个0,则被检元素一定不在;如果都是1,则被检元素很可能在。这就是布隆过滤器的基本思想。

布隆过滤器—缺点


bloom filter之所以能做到在时间和空间上的效率比较高,是因为牺牲了判断的准确率、删除的便利性。

存在误判。可能要查到的元素并没有在容器中,但是hash之后得到的k个位置上值都是1。如果bloom filter中存储的是黑名单,可以通过建立一个白名单来存储可能会误判的元素。

删除困难。一个放入容器的元素映射到bit数组的k个位置上是1,删除的时候不能简单的直接置为0,可能会影响其它元素的判断。可以采用Counting Bloom Filter(计数布隆过滤器),将标准Bloom Filter位数组的每一位扩展为一个小的计数器(Counter)。

布隆过滤器—实现


在使用bloom filter时,绕不过的两点:

1)预估数据量n

2)期望的误判率fpp

在实现bloom filter时,绕不过的两点:

1)hash函数的选取

2)bit数组的大小

对于一个确定的场景,我们预估要存的数据量为n,期望的误判率为fpp,然后需要计算我们需要的Bit数组的大小m,以及hash函数的个数k,并选择hash函数。

(1)Bit数组大小选择

  根据预估数据量n以及误判率fpp,bit数组大小的m的计算方式:

(2)哈希函数选择

  由预估数据量n以及bit数组长度m,可以得到一个hash函数的个数k:

哈希函数的选择对性能的影响是很大的,一个好的哈希函数要能近似等概率的将字符串映射到各个Bit。选择k个不同的哈希函数比较麻烦,一种简单的方法是选择一个哈希函数,然后送入k个不同的参数。

下面使用java来实现布隆过滤器:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.BitSet;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;/*** 布隆过滤器* * @author Administrator**/
public class BloomFilterDemo implements Serializable {private static final long serialVersionUID = -5221305273707291280L;private int dataCount;            // 预期数要存的数据量private double falsePositive;   // 期望的误判率private BitSet bits;           // 位数组,使用BitSet实现private int bitSize;            // 位数组大小private int hashFunctionCount;  // 散列函数的个数public BloomFilterDemo(int dataCount, double falsePositive) {super();this.dataCount = dataCount;this.falsePositive = falsePositive;this.init();}/*** 初始化*/private void init() {this.bitSize = (int) this.getNumOfBits(this.dataCount, this.falsePositive);this.hashFunctionCount = this.getNumOfHashFunctions(this.dataCount, this.bitSize);this.bits = new BitSet(this.bitSize);}/*** 往布隆过滤器添加数据标记* 如果不存在就进行记录并返回false,如果存在了就返回true* * @param data* @return* @throws NoSuchAlgorithmException */public boolean add(String data) throws NoSuchAlgorithmException {int[] indexs = new int[this.hashFunctionCount];// 先假定存在boolean exist = true;int index;for (int i = 0; i < this.hashFunctionCount; i++) {indexs[i] = index = hash(data, i);if (exist) {if (!this.bits.get(index)) {// 只要有一个不存在,就可以认为整个字符串都是第一次出现的exist = false;// 补充之前的信息for (int j = 0; j <= i; j++) {//将对应的位设置为truethis.bits.set(indexs[j], true);}}} else {//将对应的位设置为truethis.bits.set(index, true);}}return exist;}/*** 检查数据是否存在* * @param data* @return* @throws NoSuchAlgorithmException */public boolean check(String data) throws NoSuchAlgorithmException {for (int i = 0; i < this.hashFunctionCount; i++) {int index = hash(data, i);if (!this.bits.get(index)) {return false;}}return true;}/*** md5实现hash--目前测试,该hash算法的效果比较好* @param message* @param funNum* @return* @throws NoSuchAlgorithmException */private int hash(String message, int funNum) throws NoSuchAlgorithmException{MessageDigest md5 = MessageDigest.getInstance("md5");message  = message + String.valueOf(funNum);byte[] bytes = message.getBytes();md5.update(bytes);BigInteger bi = new BigInteger(md5.digest());return Math.abs(bi.intValue()) % this.bitSize;}/*** 获取Bit数组的大小* * @param n*            预估要存的数据量* @param p*            期望的误判率* @return*/private long getNumOfBits(long n, double p) {if (p == 0) {p = Double.MIN_VALUE;}// -1 * (n * log(p)) / (log(2) * log(2))return (long) (-n * Math.log(p) / (Math.log(2) * Math.log(2)));}/*** 获取hash函数的数量* * @param n*            预估要存的数据量* @param m*            Bit数组的大小m* @return*/private int getNumOfHashFunctions(long n, long m) {// (m / n) * log(2)return Math.max(1, (int) Math.round((double) m / n * Math.log(2)));}/*** 持久化布隆过滤器对象* * @param path*/public void saveFilterToFile(String path) {try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path))) {oos.writeObject(this);} catch (Exception e) {throw new RuntimeException(e);}}/*** 读取布隆过滤器对象* * @param path*/public static BloomFilterDemo readFilterFromFile(String path) {try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path))) {return (BloomFilterDemo) ois.readObject();} catch (Exception e) {throw new RuntimeException(e);}}public static void main(String[] args) {String file = "d:\\bloomFilter.obj";try {createFilter(file);readFilter(file);} catch (NoSuchAlgorithmException e) {e.printStackTrace();}}public static void createFilter(String file) throws NoSuchAlgorithmException {int count = 1000 * 10000;double fpp = 0.00001;System.out.println("start --- " + new Date());BloomFilterDemo bloomFilter = new BloomFilterDemo(count, fpp);int i = 0;while (i < count) {String msg = "时间:2018-10-01 10:00:00, 源IP:10.1.1.12,目标IP:192.1.1.205, 攻击类型:ddos攻击 -- " + i; bloomFilter.add(msg);i++;}bloomFilter.saveFilterToFile(file);System.out.println("end --- " + new Date());}public static void readFilter(String file) throws NoSuchAlgorithmException {BloomFilterDemo bloomFilter = readFilterFromFile(file);System.out.println("bitSize: " + bloomFilter.bitSize);System.out.println("hashFunctionCount: " + bloomFilter.hashFunctionCount);System.out.println("start --- " + System.currentTimeMillis());int existCount = 0;for (int i = 0, size = 10 * 10000; i < size; i++) {String msg = "时间:2018-10-01 10:00:00, 源IP:10.1.1.12,目标IP:192.1.1.205, 攻击类型:ddos攻击 -- " + i;msg += System.currentTimeMillis();if (bloomFilter.check(msg)) {existCount++;}}System.out.println("end --- " + System.currentTimeMillis());System.out.println(existCount);}}

布隆过滤器—应用


常见的几个应用场景:

(1)网页爬虫对URL的去重,避免爬取相同URL地址

(2)反垃圾邮件,从数十亿个垃圾邮件列表中判断某邮箱是否垃圾邮箱(同理,垃圾短信)

(3)缓存击穿,将已存在的缓存放到布隆中,当黑客访问不存在的缓存时迅速返回避免缓存及DB挂掉

布隆过滤器—公式推导


预估要存的数据量为:n

期望的误判率为:P

Bit数组的大小为:m

Hash函数的个数为:k

推导过程:

1)对某一特定bit位在一个元素由某特定hash函数插入时没有被置为1的概率为:

2)则k个hash函数都没有将其置为1概率为:

3)如果插入了n个元素,都未将其置为1的概率为:

4)反过来,则此位被置为1的概率为:

5)一个不在集合中的元素,被误判在集合中的概率:

6)根据自然常数公式: lim(1+1/x)^x, x→∞,得出:

7)k为何值时可以使得误判率最低。设误判率为k的函数:

8)设:

9)则简化为:

10)两边取对数:

11)两边对k求导:

12)下面求最值:

则误判率最低时,得出k值:

13)把k代入误判率公式,得出:

14)把k代入误判率公式,得出m值:

布隆过滤器(Bloom Filter)- 原理、实现和推导相关推荐

  1. 布隆过滤器(Bloom Filter)原理及优缺点剖析

    直观的说,bloom算法类似一个hash set,用来判断某个元素(key)是否在某个集合中. 和一般的hash set不同的是,这个算法无需存储key的值,对于每个key,只需要k个比特位,每个存储 ...

  2. mysql布隆过滤器源码_布隆过滤器(Bloom Filter)的原理和实现

    什么情况下需要布隆过滤器? 先来看几个比较常见的例子 字处理软件中,需要检查一个英语单词是否拼写正确 在 FBI,一个嫌疑人的名字是否已经在嫌疑名单上 在网络爬虫里,一个网址是否被访问过 yahoo, ...

  3. js 数组 实现 完全树_Flink实例(六十八):布隆过滤器(Bloom Filter)的原理和实现 - 秋华...

    什么情况下需要布隆过滤器? 先来看几个比较常见的例子 字处理软件中,需要检查一个英语单词是否拼写正确 在 FBI,一个嫌疑人的名字是否已经在嫌疑名单上 在网络爬虫里,一个网址是否被访问过 yahoo, ...

  4. Redis缓存穿透“新杀招“:布隆过滤器Bloom Filter

    场景分析 这篇文章来讲述缓存穿透的补充解决方案. 为什么要用补充来形容呢? 在之前的文章中,我们提到缓存穿透的解决方案时,我是这么说的: 关于缓存穿透,我们可以在用户访问数据库后将null值存入Red ...

  5. 布隆过滤器速度_布隆过滤器(Bloom Filter)详解

    布隆过滤器[1](Bloom Filter)是由布隆(Burton Howard Bloom)在1970年提出的.它实际上是由一个很长的二进制向量和一系列随机映射函数组成,布隆过滤器可以用于检索一个元 ...

  6. 布隆过滤器+布隆过滤器(Bloom Filter)详解

    布隆过滤器+布隆过滤器(Bloom Filter)详解 程序 = 数据结构 + 算法 -- 图灵奖得主,计算机科学家N.Wirth(沃斯) A Bloom filter is a space effi ...

  7. 布隆过滤器(Bloom Filter)的原理和实现

    布隆过滤器使用场景 之前在<数学之美>里面看到过布隆过滤器的介绍.那么什么场景下面需要使用布隆过滤器呢? 看下下面几个问题 字处理软件中,需要检查一个英语单词是否拼写正确 在 FBI,一个 ...

  8. HBase学习笔记(三)——布隆过滤器(Bloom Filter)的原理

    文章目录 布隆过滤器介绍 布隆过滤器原理 布隆过滤器的优缺点与用途 布隆过滤器使用场景 布隆过滤器介绍 布隆过滤器(Bloom Filter)由 Burton Howard Bloom 在 1970 ...

  9. 布隆过滤器(Bloom Filter)详解——基于多hash的概率查找思想

    转自:http://www.cnblogs.com/haippy/archive/2012/07/13/2590351.html   布隆过滤器[1](Bloom Filter)是由布隆(Burton ...

  10. 布隆过滤器Bloom Filter简介

    背景: 如果在平时我们要判断一个元素是否在一个集合中,通常会采用查找比较的方法,下面分析不同的数据结构查找效率: 采用线性表存储,查找时间复杂度为O(N) 采用平衡二叉排序树(AVL.红黑树)存储,查 ...

最新文章

  1. android模拟器的数据存放,Android模拟器在哪里存储SQLite数据库?
  2. C++实现huffman哈夫曼编码的算法(附完整源码)
  3. jzoj4245-er【dp,贪心】
  4. 用Python的smtp模块发邮件的示例
  5. linux查看nec进程状态,【linux】 /proc/PID/stat
  6. 一文了解十大 Java 开发者必备测试框架!
  7. jquery各类资源整理
  8. 在Markdown中更改图像大小
  9. jmeter分布式部署
  10. 分享我本人打造微信公众号吸粉引流的恶搞方法
  11. 非战之罪,从永中Office谈起
  12. 安装科来-csnas_tech_12.0.5.12506_x64
  13. 网页中那些遇到过的导航选中状态actived selected
  14. word每行字符数上限_word怎么限制每行字数
  15. [转载] 七龙珠第一部——第036话 恐怖的玛斯鲁塔
  16. 高级办公自动化试题及答案(一)
  17. OSGi 规范和框架 OSGi框架类加载机制 Java默认类加载器机制和OSGI类加载器机制比较
  18. 物联网是如何让世界变得更好
  19. 00 全国计算机技术与软件专业技术资格(软考)考试介绍
  20. labelme安装,win10-anaconda-python35-labelme及遇到的错误解决

热门文章

  1. 二分法的样例 题解
  2. Astar2007初赛第一场的题目2
  3. [Objective-C语言教程]数组(14)
  4. Color.js增强你对颜色的控制
  5. HDU——1272小希的迷宫(并查集+拓扑排序)
  6. Cosmos OpenSSD--greedy_ftl1.2.0(二)
  7. Leetcode - 广度优先遍历专题
  8. 检验密码强度的JS类(from thin's blog)
  9. PHP Lumen Laravel 解决validate方法自定义message无效的问题
  10. error: failed to push some refs to ‘......‘解决方案