布隆过滤器

这名词有没有听着好像很 挺高大上的,的确,它也是一种很重要的结构,下面一起看看:

一:说说历史:

(Bloom Filter)是由布隆(Burton Howard Bloom)在1970年提出的。它实际上是由一个很长的二进制向量和一系列随机映射函数组成,布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率(假正例False
positives,即Bloom Filter报告某一元素存在于某集合中,但是实际上该元素并不在集合中)和删除困难,但是没有识别错误的情形(即假反例False negatives,如果某个元素确实没有在该集合中,那么Bloom Filter 是不会报告该元素存在于集合中的,所以不会漏报)。

在日常生活中,包括在设计计算机软件时,我们经常要判断一个元素是否在一个集合中。比如在字处理软件中,需要检查一个英语单词是否拼写正确(也就是要判断 它是否在已知的字典中);

在 FBI,一个嫌疑人的名字是否已经在嫌疑名单上;在网络爬虫里,一个网址是否被访问过等等。最直接的方法就是将集合中全部的元素存在计算机中,遇到一个新 元素时,将它和集合中的元素直接比较即可。一般来讲,计算机中的集合是用哈希表(hash
table)来存储的。它的好处是快速准确,缺点是费存储空间。当集合比较小时,这个问题不显著,但是当集合巨大时,哈希表存储效率低的问题就显现出来 了。

比如说,一个象 Yahoo,Hotmail 和 Gmai 那样的公众电子邮件(email)提供商,总是需要过滤来自发送垃圾邮件的人(spamer)的垃圾邮件。一个办法就是记录下那些发垃圾邮件的 email 地址。由于那些发送者不停地在注册新的地址,全世界少说也有几十亿个发垃圾邮件的地址,将他们都存起来则需要大量的网络服务器。如果用哈希表,每存储一亿
个 email 地址, 就需要 1.6GB 的内存(用哈希表实现的具体办法是将每一个 email 地址对应成一个八字节的信息指纹,然后将这些信息指纹存入哈希表,由于哈希表的存储效率一般只有 50%,因此一个 email 地址需要占用十六个字节。一亿个地址大约要 1.6GB, 即十六亿字节的内存)。因此存贮几十亿个邮件地址可能需要上百 GB 的内存。除非是超级计算机,一般服务器是无法存储的[2]。(该段引用谷歌数学之美:http://www.google.com.hk/ggblog/googlechinablog/2007/07/bloom-filter_7469.html)

二:概念:

如果想判断一个元素是不是在一个集合里,一般想到的是将所有元素保存起来,然后通过比较确定。链表,树等等数据结构都是这种思路.但是随着集合中元素的增加,我们需要的存储空间越来越大,检索速度也越来越慢。不过世界上还有一种叫作散列表(又叫哈希表,Hash
table)的数据结构。它可以通过一个Hash函数将一个元素映射成一个位阵列(Bit Array)中的一个点(关于位阵列,即数据结构位图,详见位图见我另一篇博客:位图BitMap)。这样一来,我们只要看看这个点是不是
1就知道可以集合中有没有它了。这就是布隆过滤器的基本思想。

Hash面临的问题就是冲突。假设 Hash 函数是良好的,如果我们的位阵列长度为 m 个点,那么如果我们想将冲突率降低到例如 1%, 这个散列表就只能容纳 m/100 个元素。显然这就不叫空间有效了(Space-efficient)。解决方法也简单,就是使用多个
Hash(如下图所示),如果它们有一个说元素不在集合中,那肯定就不在。如果它们都说在,虽然也有一定可能性它们在说谎,不过直觉上判断这种事情的概率是比较低的。

代码:

bitmap.h

  1. #ifndef _BIT_MAP_H
  2. #define _BIT_MAP_H
  3.  
  4. #include<iostream>
  5. #include<vector>
  6. using namespace std;
  7.  
  8. /*
  9. *一个数据32位,40亿个整数,每个整数需用一位表示,40亿位就完事
  10. */
  11.  
  12. class BitMap
  13. {
  14. public:
  15. BitMap()
  16. :_size(0)
  17. {}
  18.  
  19. BitMap(size_t size)
  20. :_size(0)
  21. {
  22. _array.resize((size>>5)+1); //多少个数据,一个数据占32位,加一是至少一个数据
  23. }
  24.  
  25. bool Set(size_t num)
  26. {
  27. size_t index = num >> 5; //计算在哪个数据上
  28. size_t n = num % 32;
  29.  
  30. if (_array[index] & (1 << (31 - n))) //移位问题
  31. {
  32. cout << "有数据" << endl;
  33. return false;
  34. }
  35. else
  36. {
  37. size_t a = 1 << (31 - n);
  38. _array[index] |= a;
  39. ++_size;
  40. return true;
  41. }
  42. }
  43.  
  44. bool ReSet(size_t num) //删除一个数 之后重置
  45. {
  46. size_t index = num >> 5;
  47. size_t n = num % 32;
  48.  
  49. if (_array[index] & (1 << (31 - n))) //数存在 删除
  50. {
  51. _array[index] &= (~(1 << (31 - n)));
  52. --_size;
  53. return true;
  54. }
  55. else
  56. {
  57. return false; //不存在这个数
  58. }
  59. }
  60.  
  61. private:
  62. vector<size_t> _array; //数组
  63. size_t _size; //位图中数据个数
  64. };
  65.  
  66. #endif
  67.  
  68. void Test()
  69. {
  70. BitMap bm(65);
  71.  
  72. for (int i = 0; i < 32; ++i)
  73. {
  74. bm.Set(i);
  75. }
  76.  
  77. bm.ReSet(0);
  78. }

HashFun.h

  1. #pragma once
  2. template<class T> //各类哈希函数
  3. size_t BKDRHash(const char *str)
  4. {
  5. register size_t hash = 0;
  6. while (size_t ch = (size_t)*str++)
  7. {
  8. hash = hash * 131 + ch;
  9. }
  10. return hash;
  11. }
  12.  
  13. template<class T>
  14. size_t SDBMHash(const char *str)
  15. {
  16. register size_t hash = 0;
  17. while (size_t ch = (size_t)*str++)
  18. {
  19. hash = 65599 * hash + ch;
  20. }
  21. return hash;
  22. }
  23.  
  24. template<class T>
  25. size_t RSHash(const char * str)
  26. {
  27. size_t hash = 0;
  28. size_t magic = 63689;
  29. while (size_t ch = (size_t)*str++)
  30. {
  31. hash = hash * magic + ch;
  32. magic *= 378551;
  33. }
  34. return hash;
  35. }
  36.  
  37. template<class T>
  38. size_t APHash(const char *str)
  39. {
  40. register size_t hash = 0;
  41. size_t ch;
  42. for (long i = 0; ch = (size_t)*str++; i++)
  43. {
  44. if ((i & 1) == 0)
  45. {
  46. hash ^= ((hash << 7) ^ ch ^ (hash >> 3));
  47. }
  48. else
  49. {
  50. hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));
  51. }
  52. }
  53. return hash;
  54. }
  55.  
  56. template<class T>
  57. size_t JSHash(const char* str)
  58. {
  59. if (!*str)
  60. {
  61. return 0;
  62. }
  63. size_t hash = 1315423911;
  64. while (size_t ch = (size_t)*str++)
  65. {
  66. hash ^= ((hash << 5) + ch + (hash >> 2));
  67. }
  68. return hash;
  69. }

Bloom_Filter.h

  1. #pragma once
  2.  
  3. #include"BitMap.h"
  4. #include"HashFun.h"
  5.  
  6. template<class T>
  7. struct __HashFun1 //5种哈希函数对应的仿函数
  8. {
  9. size_t operator()(const T& key)
  10. {
  11. return BKDRHash<T>(key.c_str());
  12. }
  13. };
  14.  
  15. template<class T>
  16. struct __HashFun2
  17. {
  18. size_t operator()(const T& key)
  19. {
  20. return SDBMHash<T>(key.c_str());
  21. }
  22. };
  23.  
  24. template<class T>
  25. struct __HashFun3
  26. {
  27. size_t operator()(const T& key)
  28. {
  29. return RSHash<T>(key.c_str());
  30. }
  31. };
  32.  
  33. template<class T>
  34. struct __HashFun4
  35. {
  36. size_t operator()(const T& key)
  37. {
  38. return APHash<T>(key.c_str());
  39. }
  40. };
  41.  
  42. template<class T>
  43. struct __HashFun5
  44. {
  45. size_t operator()(const T& key)
  46. {
  47. return JSHash<T>(key.c_str());
  48. }
  49. };
  50.  
  51. template<class K = string,
  52. class HashFun1 = __HashFun1<K>,
  53. class HashFun2 = __HashFun2<K>,
  54. class HashFun3 = __HashFun3<K>,
  55. class HashFun4 = __HashFun4<K>,
  56. class HashFun5 = __HashFun5<K>>
  57. class Bloom_Filter
  58. {
  59. public:
  60. Bloom_Filter(size_t size)
  61. :_capacity(size)
  62. {
  63. _bitmap._array.resize((size >> 5) + 1);
  64. }
  65.  
  66. void _Set(const K& key)
  67. {
  68. _bitmap.Set(HashFun1()(key) % _capacity);
  69. _bitmap.Set(HashFun2()(key) % _capacity);
  70. _bitmap.Set(HashFun3()(key) % _capacity);
  71. _bitmap.Set(HashFun4()(key) % _capacity);
  72. _bitmap.Set(HashFun5()(key) % _capacity);
  73. }
  74.  
  75. bool _IsIn(const K& key)
  76. {
  77. if (!_bitmap.Test(HashFun1()(key) % _capacity))
  78. return false;
  79. if (!_bitmap.Test(HashFun1()(key) % _capacity))
  80. return false;
  81. if (!_bitmap.Test(HashFun1()(key) % _capacity))
  82. return false;
  83. if (!_bitmap.Test(HashFun1()(key) % _capacity))
  84. return false;
  85. if (!_bitmap.Test(HashFun1()(key) % _capacity))
  86. return false;
  87. return true;
  88. }
  89. private:
  90. BitMap _bitmap;
  91. size_t _capacity;
  92. };

三、布隆过滤器优缺点:

1.优点:

相比于其它的数据结构,布隆过滤器在空间和时间方面都有巨大的优势。布隆过滤器存储空间和插入/查询时间都是常数。另外, Hash 函数相互之间没有关系,方便由硬件并行实现。布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势。布隆过滤器可以表示全集,其它任何数据结构都不能;k
和 m 相同,使用同一组 Hash 函数的两个布隆过滤器的交并差运算可以使用位操作进行。

2.缺点

但是布隆过滤器的缺点和优点一样明显。误算率(False Positive)是其中之一。随着存入的元素数量增加,误算率随之增加。但是如果元素数量太少,则使用散列表足矣。另外,一般情况下不能从布隆过滤器中删除元素. 我们很容易想到把位列阵变成整数数组,每插入一个元素相应的计数器加1,
这样删除元素时将计数器减掉就可以了。然而要保证安全的删除元素并非如此简单。首先我们必须保证删除的元素的确在布隆过滤器里面. 这一点单凭这个过滤器是无法保证的。另外计数器回绕也会造成问题。

赐教!

C++布隆过滤器的更多相关文章

  1. 布隆过滤器的概述及Python实现

    布隆过滤器 布隆过滤器是一种概率空间高效的数据结构.它与hashmap非常相似,用于检索一个元素是否在一个集合中.它在检索元素是否存在时,能很好地取舍空间使用率与误报比例.正是由于这个特性,它被称作概 ...

  2. 【转】Bloom Filter布隆过滤器的概念和原理

    转自:http://blog.csdn.net/jiaomeng/article/details/1495500 之前看数学之美丽,里面有提到布隆过滤器的过滤垃圾邮件,感觉到何其的牛,竟然有这么高效的 ...

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

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

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

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

  5. 布隆过滤器(Bloom Filter)

    一.布隆过滤器介绍 Bloom Filter是一种空间效率很高的随机数据结构,Bloom Filter可以看做是对bit-map的扩展,它的原理如下: 当一个元素被加入集合时,通过K个Hash函数将这 ...

  6. 布隆过滤器的java实现

    package com.kaikeba.data.jobspider.util; import java.util.BitSet; public class Bloomfilter { private ...

  7. Bloom Filter(布隆过滤器)

    布隆过滤器用于测试某一元素是否存在于给定的集合中,是一种空间利用率很高的随机数据结构(probabilistic data structure),存在一定的误识别率(false positive),即 ...

  8. 布隆过滤器(BoomFilter)

    1.原理:           a.解决的问题:                判断一个元素是否在一个集合中             b.Hash表的特点:                i.快速准确 ...

  9. 简化布隆过滤器——BitMap

    简化布隆过滤器--BitMap 前言 前段开发项目试就发现,一部分的代码实现存在着一些性能上的隐患.但当时忙于赶进度和由于卡发中的不稳定因素,想了许多解决方案也没有机会实施.最近,正好趁个机会进行一系 ...

  10. [转载] 布隆过滤器(Bloom Filter)详解

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

随机推荐

  1. Python Web-第二周-正则表达式(Using Python to Access Web Data)

    0.课程地址与说明 1.课程地址:https://www.coursera.org/learn/python-network-data/home/welcome 2.课程全名:Using Python ...

  2. 【BZOJ2434】阿狸的打字机(AC自动机,树状数组)

    [BZOJ2434]阿狸的打字机(AC自动机,树状数组) 先写个暴力: 每次打印出字符串后,就插入到\(Trie\)树中 搞完后直接搭\(AC\)自动机 看一看匹配是怎么样的: 每次沿着\(AC\)自 ...

  3. Spring Boot会员管理系统——处理文件上传

    温馨提示 Spring Boot会员管理系统的中,需要涉及到Spring框架,SpringMVC框架,Hibernate框架,thymeleaf模板引擎.所以,可以学习下这些知识.当然,直接入门的话使 ...

  4. Windows Live Writer 2014版绿色版制作及主题获取

    前年才建好博客的时候就尝试用Windows Live Writer(WLW)写博客,用的是直接在网上找到的一个WLW 2009绿色美化版.但因为当时WLW获取的博客主题是主页的,预览的时候特别不爽,就 ...

  5. 【Java一看就懂】浅克隆和深克隆

    一.何为克隆 在Java的体系中,数据类型分为基本数据类型和引用数据类型. 基本数据类型包括byte,short,int,long,float,double,boolean,char 8种,其克隆可通 ...

  6. TypeScript入门(二)函数新特性

    一.TypeScript-Rest and Spread操作符 用来声明任意数量的方法参数  ...args中的...就是Rest and Spread操作符. 例1: 声明一个可以传任意数量的参数进 ...

  7. Sql Server 索引以及页和区

    索引(Index),相信大家都知道就是给表中的数据添加了一个目录,使我们可以快速检索到我们想要的数据,但这个目录是什么?SqlServer又是如何管理的?要搞明白这些,我们就要先了解sqlserver ...

  8. ASP.NET MVC编程——缓存

    Web缓存分为服务端缓存和客户端缓存. 1 服务端缓存 1.1请求域内的缓存:HttpContext.Items 类型: HttpContext.Items的类型为IDictionary,且键和值都是 ...

  9. 智齿客服网页端接入文档V2.3

    产品介绍 智齿客服网页端接入提供以下两种部署方式. 一.网页组件(推荐) 通过智齿客服网站咨询组件,企业的用户可快捷联系到企业客服获取帮助.智齿客服网页组件提供强大的用户行为采集能力和系统对接能力,支 ...

  10. java排序算法(十):桶式排序

    java排序算法(十):桶式排序 桶式排序不再是一种基于比较的排序方法,它是一种比较巧妙的排序方式,但这种排序方式需要待排序的序列满足以下两个特征: 待排序列所有的值处于一个可枚举的范围之类: 待排序 ...