哈希拓展--布隆过滤器

一、问题概述

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

相关文章

排序(Sort)--【一】

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

快速排序--全集

快速排序&#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…

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

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

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

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

Vim简单配置

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

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

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

[剑指Offer]替换空格

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

C语言关键字 ISO/ANSI C90 C99 C11

面试考点 https://blog.csdn.net/csdn_kou/article/details/81113215 * 有的常用的我们都不知道是关键字&#xff0c;比如sizeof.这是面试中的考点&#xff0c;要注意。 * 同时当回答C语言中有多少关键字时&#xff0c;要回答前题条件&#xff0c;时针对哪一个版本

关于sudo

之前&#xff0c;我们使用sudo的时候&#xff0c;是因为其用户本身具有root权限&#xff0c;所以可以sudo后执行相关操作&#xff0c;但是对于普通用户来说&#xff0c;它是既不具有sudo权限&#xff0c;又不在sudo用户组中&#xff0c;那么我们来研究一下如何将新创建的用户添…

Bash入门

Bash简介&#xff1a; Bash&#xff08;GNU Bourne-Again Shell&#xff09;是一个为GNU计划编写的Unix shell&#xff0c;它是许多Linux平台默认使用的shell。 shell是一个命令解释器&#xff0c;是介于操作系统内核与用户之间的一个绝缘层。准确地说&#xff0c;它也是能力…

线程之售票系统pthread_mutex,_lock,_unlock

先看一下这篇文章 https://blog.csdn.net/csdn_kou/article/details/81148268 四个人同时买票票&#xff0c;引出线程 #include "head.h" int ticket 100; void * route(void *arg) {char *id (char *)arg;while(1){if(ticket>0){usleep(1000);printf("…

Bash基本语法

1. 变量赋值 a375 hello$a 这里需要注意的是&#xff0c;等号两边不能有空格 还有一个例子是这样的 例1&#xff1a; 结果为&#xff1a; 关于上述&#xff0c;主要有如下几点&#xff1a; $hello和${hello}是一样的&#xff0c;在bash中如果遇到空格&#xff0c;tab键时&a…

关于fd和fp(fd:file descirptor fp:file pointor)

通常&#xff0c;我们在输入数据或输出数据的设备为键盘或者显示器。当然&#xff0c;我们比较熟悉的输入输出&#xff0c;可能就是对于文件的操作&#xff0c;还有直接从终端输出&#xff0c;显示到显示器上。在C语言中&#xff0c;我们使用fopen,fclose,fread,fwrite对文件进…

[linux]wait详解

wait&#xff1a;进程等待 主要有两种等待方式&#xff1a;阻塞式等待和非阻塞式等待 阻塞式等待&#xff1a;如果子进程正在运行&#xff0c;父进程将会一直等待着子进程运行结束&#xff0c;并且自己什么事都不干 非阻塞式等待&#xff1a;如果子进程正在运行&#xff0c;…

[Linux]消息队列

我们知道进程间通信的方法有多种&#xff0c;主要有管道&#xff0c;消息队列&#xff0c;信号量&#xff0c;共享内存&#xff0c;socket等。之前介绍过管道&#xff0c;今天再介绍一个新的概念–消息队列。 消息队列&#xff1a;将一个进程到另一个进程之间发送数据块的方式…

C++关键字速查手册

[TOC] https://blog.csdn.net/csdn_kou/article/details/81113215 C98关键字 C11 关键字共73个 alignas alignof用于获取取指定表达式指定的&#xff08;类似sizeof&#xff0c;可以直接是类型名&#xff09;的对齐(alignment)。alignas用于声明时指定对齐类似于现有的类型…

[Linux]信号

Linux下的信号是一个什么概念呢。我们在现实生活中也遇到过信号之类的。比如红绿信号灯&#xff0c;班主任叫你去办公室并且脸色不好&#xff0c;诸如此类的都会给你一个信号。让你辨别事情的发生。同样&#xff0c;Linux下也有许多的信号&#xff0c;让你执行相应的操作。比如…

[数据结构]Map和Set

说起map和set&#xff0c;想必我们都学过红黑树了吧&#xff0c;map和set就是红黑树的一个应用领域。它的底层就是由红黑树来实现的。下面简单说一下map和set的使用吧。 首先&#xff0c;有一个栗子是这样的&#xff0c;让我们统计出每种水果出现的次数。 我们会想到怎么解决…