一、问题概述

布隆过滤器是由布隆提出来的,是由一个很长的二进制序列和一系列的映射函数组成。主要用于检测一个元素是否在一个集合中。当然在设计计算机软件时,我们也经常会判断一个元素是否在一个集合中。比如:在字处理软件中,需要检查一个英语单词是否拼写正确,(即是否在字典中),在网络爬虫里,这个网站是否被访问过。最直接的方法就是将元素都存入计算机中,遇到一个新元素时,将它和元素进行比较即可。一般是用哈希表存储的,因为它的查询速度快,就是比较浪费空间。集合小的时候存储效率还好,当集合大的时候,存储效率低的问题就显现出来了。再比如,对于一个像 Yahoo,Hotmail 和 Gmai 那样的公众电子邮件提供商,总是需要过滤来自发送垃圾邮件的人的垃圾邮件。这时,我们就得记录那些发垃圾邮件的Email地址,由于那些发送者不停地在注册新的地址,全世界少说也有几十亿个发垃圾邮件的地址,将他们都存起来则需要大量的网络服务器。如果用哈希表,每存储一亿个email地址,就需要1.6GB 的内存,因此存贮几十亿个邮件地址可能需要上百GB的内存。除非是超级计算机,一般服务器是无法存储的。所以在这里我们就引入了布隆过滤器来处理这些问题。

二、布隆过滤器的基本思想

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

布隆过滤器是一种空间效率很高的数据结构。也可以看做是位图BitMap的扩展(位图链接):

当一个元素加入集合中时,通过k个不同的哈希函数将这个元素映射成一个位数组的k个点,把它们置为1。当我们检测看一个元素存不存在时,只需要看那k个位是否为1就可以了。主要分为两点:

1、如果这k个位中,只要有一个位为0,就说明此元素不在集合中;

2、如果k个位都为1的话,表明此元素可能存在集合中。

第二点又体现了布隆过滤器的一个缺点:存在一定的误判率。但是为了尽可能的降低这种误判率,我们采用上述多个哈希函数检测的方式。经研究表明,可将误判率降低到万分之一以下。

同时,布隆过滤器的优点也是非常显著的。它的空间效率和查询时间都远远超过一般的算法。存储空间和插入/查询时间都是常数(O(k))。

还有重要的一点是,一般情况下,布隆过滤器不支持删除操作,起初,有人会想到使用计数方式将位++或--来删除元素。但是由于布隆过滤器的误判,你可能会把错误的元素删除。(下节我们会分析到这类问题哦)

三、实现代码(vs2013)

//HashFunc.h

#pragma once
#include<string>
#include<iostream>
using namespace std;/// @brief BKDR Hash Function  /// @detail 本 算法由于在Brian Kernighan与Dennis Ritchie的《The C Programming Language》一书被展示而得 名,是一种简单快捷的hash算法,也是Java目前采用的字符串的Hash算法(累乘因子为31)。  template<class T>  size_t BKDRHash(const T *str)  {  register size_t hash = 0;  while (size_t ch = (size_t)*str++)  {         hash = hash * 131 + ch;   // 也可以乘以31、131、1313、13131、131313..  // 有人说将乘法分解为位运算及加减法可以提高效率,如将上式表达为:hash = hash << 7 + hash << 1 + hash + ch;  // 但其实在Intel平台上,CPU内部对二者的处理效率都是差不多的,  // 我分别进行了100亿次的上述两种运算,发现二者时间差距基本为0(如果是Debug版,分解成位运算后的耗时还要高1/3);  // 在ARM这类RISC系统上没有测试过,由于ARM内部使用Booth's Algorithm来模拟32位整数乘法运算,它的效率与乘数有关:  // 当乘数8-31位都为1或0时,需要1个时钟周期  // 当乘数16-31位都为1或0时,需要2个时钟周期  // 当乘数24-31位都为1或0时,需要3个时钟周期  // 否则,需要4个时钟周期  // 因此,虽然我没有实际测试,但是我依然认为二者效率上差别不大          }  return hash;  }  /// @brief SDBM Hash Function  /// @detail 本算法是由于在开源项目SDBM(一种简单的数据库引擎)中被应用而得名,它与BKDRHash思想一致,只是种子不同而已。  template<class T>  size_t SDBMHash(const T *str)  {  register size_t hash = 0;  while (size_t ch = (size_t)*str++)  {  hash = 65599 * hash + ch;         //hash = (size_t)ch + (hash << 6) + (hash << 16) - hash;  }  return hash;  }  /// @brief RS Hash Function  /// @detail 因Robert Sedgwicks在其《Algorithms in C》一书中展示而得名。  template<class T>  size_t RSHash(const T *str)  {  register size_t hash = 0;  size_t magic = 63689;     while (size_t ch = (size_t)*str++)  {  hash = hash * magic + ch;  magic *= 378551;  }  return hash;  }  /// @brief AP Hash Function  /// @detail 由Arash Partow发明的一种hash算法。  template<class T>  size_t APHash(const T *str)  {  register size_t hash = 0;  size_t ch;  for (long i = 0; ch = (size_t)*str++; i++)  {  if ((i & 1) == 0)  {  hash ^= ((hash << 7) ^ ch ^ (hash >> 3));  }  else  {  hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));  }  }  return hash;  }  /// @brief JS Hash Function  /// 由Justin Sobel发明的一种hash算法。  template<class T>  size_t JSHash(const T *str)  {  if(!*str)        // 这是由本人添加,以保证空字符串返回哈希值0  return 0;  register size_t hash = 1315423911;  while (size_t ch = (size_t)*str++)  {  hash ^= ((hash << 5) + ch + (hash >> 2));  }  return hash;  }  /// @brief DEK Function  /// @detail 本算法是由于Donald E. Knuth在《Art Of Computer Programming Volume 3》中展示而得名。  template<class T>  size_t DEKHash(const T* str)  {  if(!*str)        // 这是由本人添加,以保证空字符串返回哈希值0  return 0;  register size_t hash = 1315423911;  while (size_t ch = (size_t)*str++)  {  hash = ((hash << 5) ^ (hash >> 27)) ^ ch;  }  return hash;  }  /// @brief FNV Hash Function  /// @detail Unix system系统中使用的一种著名hash算法,后来微软也在其hash_map中实现。  template<class T>  size_t FNVHash(const T* str)  {  if(!*str)   // 这是由本人添加,以保证空字符串返回哈希值0  return 0;  register size_t hash = 2166136261;  while (size_t ch = (size_t)*str++)  {  hash *= 16777619;  hash ^= ch;  }  return hash;  }  /// @brief DJB Hash Function  /// @detail 由Daniel J. Bernstein教授发明的一种hash算法。  template<class T>  size_t DJBHash(const T *str)  {  if(!*str)   // 这是由本人添加,以保证空字符串返回哈希值0  return 0;  register size_t hash = 5381;  while (size_t ch = (size_t)*str++)  {  hash += (hash << 5) + ch;  }  return hash;  }  /// @brief DJB Hash Function 2  /// @detail 由Daniel J. Bernstein 发明的另一种hash算法。  template<class T>  size_t DJB2Hash(const T *str)  {  if(!*str)   // 这是由本人添加,以保证空字符串返回哈希值0  return 0;  register size_t hash = 5381;  while (size_t ch = (size_t)*str++)  {  hash = hash * 33 ^ ch;  }  return hash;  }  /// @brief PJW Hash Function  /// @detail 本算法是基于AT&T贝尔实验室的Peter J. Weinberger的论文而发明的一种hash算法。  template<class T>  size_t PJWHash(const T *str)  {static const size_t TotalBits       = sizeof(size_t) * 8;  static const size_t ThreeQuarters   = (TotalBits  * 3) / 4;  static const size_t OneEighth       = TotalBits / 8;  static const size_t HighBits        = ((size_t)-1) << (TotalBits - OneEighth);      register size_t hash = 0;  size_t magic = 0;     while (size_t ch = (size_t)*str++)  {  hash = (hash << OneEighth) + ch;  if ((magic = hash & HighBits) != 0)  {  hash = ((hash ^ (magic >> ThreeQuarters)) & (~HighBits));  }  }  return hash;  }  /// @brief ELF Hash Function  /// @detail 由于在Unix的Extended Library Function被附带而得名的一种hash算法,它其实就是PJW Hash的变形。  template<class T>  size_t ELFHash(const T *str)  {  static const size_t TotalBits       = sizeof(size_t) * 8;  static const size_t ThreeQuarters   = (TotalBits  * 3) / 4;  static const size_t OneEighth       = TotalBits / 8;  static const size_t HighBits        = ((size_t)-1) << (TotalBits - OneEighth);      register size_t hash = 0;  size_t magic = 0;  while (size_t ch = (size_t)*str++)  {  hash = (hash << OneEighth) + ch;  if ((magic = hash & HighBits) != 0)  {  hash ^= (magic >> ThreeQuarters);  hash &= ~magic;  }         }  return hash;  }  

//BloomFilter.h

#pragma once
#include<string>
#include<iostream>
using namespace std;
#include"BitMap.h"
#include"HashFunc.h"
struct _HashFunc1
{size_t operator()(const string& s){return BKDRHash(s.c_str());}
};struct _HashFunc2
{size_t operator()(const string& s){return SDBMHash(s.c_str());}
};struct _HashFunc3
{size_t operator()(const string& s){return RSHash(s.c_str());}
};struct _HashFunc4
{size_t operator()(const string& s){return APHash(s.c_str());}
};struct _HashFunc5
{size_t operator()(const string& s){return JSHash(s.c_str());}
};template<class K = string,
class HashFunc1 = _HashFunc1,
class HashFunc2 = _HashFunc2,
class HashFunc3 = _HashFunc3,
class HashFunc4 = _HashFunc4,
class HashFunc5 = _HashFunc5
>
class BloomFilter
{
public:BloomFilter(size_t Num):_bm(Num*5*2),_size(Num*5*2){}void BloomSet(const K& key){size_t Hash1 = HashFunc1()(key)%_size;size_t Hash2 = HashFunc2()(key)%_size;size_t Hash3 = HashFunc3()(key)%_size;size_t Hash4 = HashFunc4()(key)%_size;size_t Hash5 = HashFunc5()(key)%_size;_bm.Set(Hash1);_bm.Set(Hash2);_bm.Set(Hash3);_bm.Set(Hash4);_bm.Set(Hash5);}bool Test(const K& key){size_t Hash1 = HashFunc1()(key)%_size;if(_bm.Test(Hash1) == false){return false;}size_t Hash2 = HashFunc2()(key)%_size;if(_bm.Test(Hash2) == false){return false;}size_t Hash3 = HashFunc3()(key)%_size;if(_bm.Test(Hash3) == false){return false;}size_t Hash4 = HashFunc4()(key)%_size;if(_bm.Test(Hash4) == false){return false;}size_t Hash5 = HashFunc5()(key)%_size;if(_bm.Test(Hash5) == false){return false;}return true;}
private:BitMap _bm;size_t _size;
};void BloomFilterTest()
{BloomFilter<> bm(1024);bm.BloomSet("11111");bm.BloomSet("11110");bm.BloomSet("11112");bm.BloomSet("11113");cout<<bm.Test("11111")<<endl;cout<<bm.Test("11101")<<endl;cout<<bm.Test("11102")<<endl;cout<<bm.Test("11113")<<endl;}

四、运行结果

哈希拓展--布隆过滤器相关推荐

  1. 哈希切割+布隆过滤器

    目录 布隆过滤器 布隆过滤器的提出 布隆过滤器的概念 布隆过滤器的实现 插入 set函数 布隆过滤器长度的设置 测试布隆误判率 布隆过滤器删除 布隆过滤器的实现场景 布隆过滤器的扩展以及哈希切分 布隆 ...

  2. 算法学习 (门徒计划)3-2 哈希表与布隆过滤器及经典问题 学习笔记

    算法学习 (门徒计划)3-2 哈希表与布隆过滤器及经典问题 学习笔记 前言 哈希表 哈希操作 冲突处理 开放定址法 再哈希法 公共溢出区 链式地址法 扩容哈希表 设计简易哈希表 总结 布隆过滤器 对比 ...

  3. 哈希的应用 -- 布隆过滤器与海量数据处理

    文章目录 布隆过滤器概念 布隆过滤器设计思路 布隆过滤器的应用 布隆过滤器模拟实现 布隆过滤器的基本框架 布隆过滤器的插入 布隆过滤器的探测 布隆过滤器的删除 布隆过滤器优点 布隆过滤器缺陷 布隆过滤 ...

  4. 哈希的应用 -- 布隆过滤器

    作者:@小萌新 专栏:@C++进阶 作者简介:大二学生 希望能和大家一起进步! 本篇博客简介:介绍并模拟实现哈希的应用 – 布隆过滤器 布隆过滤器 布隆过滤器的提出 布隆过滤器的概念 布隆过滤器的实现 ...

  5. 哈希表应用——布隆过滤器

    注:布隆过滤是用来处理海量数据且允许存在误判 目录 布隆过滤器提出 布隆过滤器概念 布隆过滤器的理论知识 布隆过滤器的实现 布隆过滤器的删除 布隆过滤器优点 布隆过滤器缺陷 布隆过滤器的应用场景 哈希 ...

  6. 哈希的应用(2)——布隆过滤器

    文章目录 布隆过滤器 布隆过滤器的概念 布隆过滤器的实现 哈希函数个数和布隆过滤器长度 模拟实现 布隆过滤器的删除 小结 海量数据处理相关题 5.1哈希切割 5.2位图应用 5.3布隆过滤器 扩展 布 ...

  7. Redis bitmap、hyperlog、布隆过滤器、RoaringBitmap原理应用场景与日活的统计的具体应用

    传统方案-mysql 缺点: 1.空间占用大 2.统计逻辑复杂,比如 统计最近 30 天用户的累计活跃天(每个用户在 30 天里有 N 天使用 app,N 为 1-30,然后将月活跃用户的 N 天加总 ...

  8. 布隆过滤器:一种低空间成本的判断元素是否存在的方式

    简介 布隆过滤器(BloomFilter)是一种用于判断元素是否存在的方式,它的空间成本非常小,速度也很快. 但是由于它是基于概率的,因此它存在一定的误判率,它的Contains()操作如果返回tru ...

  9. 布隆过滤器误判怎么办为什么会_说一说布隆过滤器

    介绍 布隆过滤器在wiki上的介绍: 布隆过滤器(Bloom Filter)是1970年由布隆提出的.它实际上是一个很长的二进制向量和一系列随机映射函数.布隆过滤器可以用于检索一个元素是否在一个集合中 ...

最新文章

  1. 通关制单机器人_2020关务节|“数字供应链与智能通关”论坛——如何打造云上跨境贸易生态圈...
  2. 【重置版】Android studio高效开发的秘密
  3. Leetcode 392. 判断子序列 解题思路及C++实现
  4. 人工机器:jetsonnano推理时出现 Segmentation fault(core dumped)
  5. JAVA确定这天是这年的某一天_[Java] 练习题014: 输入某年某月某日,判断这一天是这一年的第几天?...
  6. 【Spark调优】内存模型与参数调优
  7. 软件 Bug 引发的致命事故,程序员责任何在?| 技术头条
  8. Kubernetes的Service外部访问方式:NodePort和LoadBalancer
  9. 7 Object类型
  10. 希望能够在这条路上走下去
  11. 21年最新-李沐-动手学深度学习第二版
  12. 修改360企业版杀毒软件备注名的方法
  13. 软件测试工程师绩效考核细则,软件测试工程师绩效考核方案
  14. 元宇宙:不透明面纱下的“康德主义”
  15. 互联网思维之迭代思维
  16. table 点击文字按钮预览图片
  17. JS黄金分割法实现随机漂亮颜色!
  18. 【金融大屏项目】—— Echarts水滴图(echarts-liquidfill)
  19. 使用Puppeteer轻松爬取网易云音乐、QQ音乐的精品歌单
  20. 微信小程序WebView嵌入别人网页的解决办法

热门文章

  1. [POJ1463] Strategic game
  2. Mercurial hg web server的配置
  3. datetime2 数据类型
  4. 为移动端网页构造快速响应按钮
  5. hibernate中PO对象的三种状态分析以及session中的一些方法的区别
  6. 装修月记第一弹,硬装篇
  7. wps单机无网络版_单平台销量破百万,这个国产单机系列要出网游,还要上主机...
  8. solaris配置php,Solaris下安装Oracle_启动Oracle及监听
  9. 伸缩轨道_深度解析——伸缩喷漆房为什么这么受欢迎!
  10. mybatis通用mapper_全网最全Mapper解析,附实操代码帮你更好理解