哈希拓展--布隆过滤器

一、问题概述

       布隆过滤器是由布隆提出来的,是由一个很长的二进制序列和一系列的映射函数组成。主要用于检测一个元素是否在一个集合中。当然在设计计算机软件时,我们也经常会判断一个元素是否在一个集合中。比如:在字处理软件中,需要检查一个英语单词是否拼写正确,(即是否在字典中),在网络爬虫里,这个网站是否被访问过。最直接的方法就是将元素都存入计算机中,遇到一个新元素时,将它和元素进行比较即可。一般是用哈希表存储的,因为它的查询速度快,就是比较浪费空间。集合小的时候存储效率还好,当集合大的时候,存储效率低的问题就显现出来了。再比如,对于一个像 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;}

四、运行结果




本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/382760.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

开启一个新的命令行窗口

1&#xff0c;start cmd /k echo Hello, World!2&#xff0c;start cmd /C pause区别是第二种执行完毕以后&#xff0c;新开的窗口会自动关闭&#xff0c;第一种则不会

C语言中 \r, \n, \b

\r\n 和 \n 区别 &#xff08;重新排版整理&#xff09; \r回车符\n换行符计算机还没有出现之前&#xff0c;有一种叫做电传打字机&#xff08;Teletype Model 33&#xff09;的玩意&#xff0c;每秒钟可以打10个字符。但是它有一个问题&#xff0c;就是打完一行换行的时候&am…

排序(Sort)--【一】

排序&#xff0c;对于大家再熟悉不过了吧。我们之前在学习c语言的时候接触过的冒泡排序&#xff0c;选择排序等。今天给大家介绍两种新的排序。 1、直接插入排序 升序排列&#xff1a;将第一个数确定好&#xff0c;从下标为1的数开始插入&#xff0c;如果插入的数比前一个数大…

用python os.system 执行 批处理的时候, 出现的一些问题

如果 在一个py文件里面 , 假设用 三条语句 os.system(a.bat) os.system(b.bat) os.system(c.bat)这样的话 只会最后一条生效.

wait()和waitpid()的参数解析

进程的等待 #include <sys/types.h> #include <sys/wait.h> wait(),waitpid()区别&#xff1a; 在一个子进程终止前&#xff0c;wait使其调用者阻塞&#xff0c;而waitpid有一个选项&#xff0c;可使调用者不阻塞;waitpid()并不等待在其调用之后的第一个终止的…

快速排序--全集

快速排序&#xff1a;一听名字就知道这种排序很快的&#xff0c;是吧&#xff1f;没错&#xff0c;它是一种效率比较高的排序算法。 快速排序采用的是分治的思想。 比如&#xff0c;将一串数中的一个元素作为基准&#xff0c;然后将比它小的数排在它的左边&#xff0c;比它大…

task_struct结构体查找

网上有很多解析task_struct结构体的文章&#xff0c;可是都没有说这个结构体到底在哪里&#xff1f; 这个结构体位于头文件 shced.h cd / find -name sched.h 显示结果如下 注意只有 位于内核中的include 才是正确的。 /usr/src/kernels/2.6.32-431.el6.i686/include/linux…

使用python 创建快捷方式

import os import pythoncom from win32com.shell import shell from win32com.shell import shellcondef set_shortcut(): # 如无需特别设置图标&#xff0c;则可去掉iconname参数try:filename r"D:\AppServ\timer\win_cron_zq\timer.exe" # 要创建快捷方式的文件…

python 各个模块的简单介绍 转载

转自 https://www.jianshu.com/p/f8c43e25c02e

闹钟函数alarm()的解释与实践

alarm 定义 也称为闹钟函数&#xff0c;它可以在进程中设置一个定时器&#xff0c;当定时器指定的时间到时&#xff0c;它向进程发送SIGALRM信号。可以设置忽略或者不捕获此信号&#xff0c;如果采用默认方式其动作是终止调用该alarm函数的进程。 #include "head.h&quo…

Linux下如何设置权限让用户只删除自己的文件(粘滞位)

之前我们知道如何针对用户和用户组来设置文件权限。通常是用三个八进制来设置权限的&#xff0c;这里我要说的是&#xff0c;其实是由四个八进制表示的。其中第一个八进制我们通常是忽略的。第二个到第四个是对应于SUID,SGID,sticky-bit。 SUID&#xff1a;设置了SUID 位的文件…

CentOS安装yum 镜像 举例阿里云镜像

如何安装yum 镜像 CentOS 1、备份 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup 2、下载新的CentOS-Base.repo 到/etc/yum.repos.d/ CentOS 5 wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-5.r…

python在ubuntu执行sh脚本,提示权限不够的解决方法, 转载

https://blog.csdn.net/weixin_40320794/article/details/81772194

Vim简单配置

vim配置&#xff1a; &#xff08;在Centos6.5下配置vim&#xff09; 1.找到用户的主工作目录&#xff0c;ls看是否有.vimrc文件&#xff0c;有的话打开即可。没有的话自己touch一个。vim进入.vimrc中&#xff1a; set nu 设置行数 colorscheme desert syntax enabl…

运算符面试题(剑指offer,面试宝典,牛客网)

利用一个宏实现两个数的交换&#xff1f;不使用if,?,switch或者其他判断语句比较两个变量的大小&#xff1f;利用位运算实现加法&#xff1f;以下程序输出结果是&#xff1f;用位运算实现求平均数&#xff1f;不用循环判断一个数是不是2的N次方&#xff1f; 利用一个宏实现两个…

js 出现 replace 无法完全替换 指定字符串的时候的解决办法

/{/g 通过这种方式替换掉 replace( /这里填写需要被替换的字符串/g , "");

[WPS笔试题]实现栈的push,pop,max且时间复杂度为O(1)

今天做了一下WPS的笔试题&#xff0c;遇到了一道关于栈的题&#xff0c;觉得挺有意思的&#xff0c;就写篇博客分享一下吧~~ 题目要求&#xff1a;要求实现栈的数据结构&#xff0c;在该类型中实现一个能够得到栈的最大元素的max函数&#xff0c;在该栈中&#xff0c;调用max,…

MarkDown生成目录索引

123 在第一行开头写[TOC] 必须是第一行&#xff0c;不可以在前面加别的东西。 1 2 3

ubuntu 如何用root身份进行登录

公司有个小项目, 需要用python调用 sh脚本来执行一些东西, 执行脚本的时候需要输入密码 类似 sudo S paaswd 脚本, 但是给客户部署的话, 再让客户客户 保存密码到配置文件, 就显得麻烦, 就想到用root方式去登陆系统, 结果用了网上的方法, 还是登陆不进去, 最后结合简书的一个方…

[剑指Offer]替换空格

今天看题的时候&#xff0c;遇到一个替换空格的题目&#xff0c;分析一下哈。 题目要求&#xff1a;把字符串中的每个空格替换成“%20”。例如输入“we are happy”&#xff0c;则输出“we%20are%20happy”。 解题思路&#xff1a;我们首先想到的是&#xff1a;移位思想。遇到…