【C++】哈希的应用——布隆过滤器

这篇具有很好参考价值的文章主要介绍了【C++】哈希的应用——布隆过滤器。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

哈希的应用——布隆过滤器

【C++】哈希的应用——布隆过滤器

一、布隆过滤器的概念与性质

1.布隆过滤器的引出

我们在使用新闻客户端看新闻时,它会给我们不停地推荐新的内容,它每次推荐时要去重,去掉那些已经看过的内容。问题来了,新闻客户端推荐系统如何实现推送去重的? 用服务器记录了用户看过的所有历史记录,当推荐系统推荐新闻时会从每个用户的历史记录里进行筛选,过滤掉那些已经存在的记录。 如何快速查找呢?

  1. 用哈希表存储用户记录,缺点:浪费空间
  2. 用位图存储用户记录,缺点:位图一般只能处理整形,如果内容编号是字符串,就无法处理了。但我们可以使用一些哈希算法把字符串类型转换成整型,比如BKDR哈希算法,但是这里还存在一个问题。字符串的组合方式太多了,一个字符的取值有256种,一个数字只有10种,所以不可避免会出现哈希冲突

上述法二将哈希与位图结合的方法,即布隆过滤器


2.布隆过滤器的概念

布隆过滤器是由布隆(Burton Howard Bloom)在1970年提出的 一种紧凑型的、比较巧妙的概率型数据结构,特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”,它是用多个哈希函数,将一个数据映射到位图结构中。相比于传统的 List、Set、Map 等数据结构,此种方式不仅可以提升查询效率,也可以节省大量的内存空间。

【C++】哈希的应用——布隆过滤器


3.布隆过滤器的误判

布隆过滤器是**一个大型位图(bit数组或向量) + 多个无偏哈希函数**

【C++】哈希的应用——布隆过滤器

如果我们要映射一个值到布隆过滤器中,我们需要使用多个不同的哈希函数生成多个哈希值**,**并对每个生成的哈希值指向的 bit 位置 1,例如针对值 “baidu” 和三个不同的哈希函数分别生成了哈希值 2、4、7,则上图转变为:

【C++】哈希的应用——布隆过滤器

现在,如果我们要查询"baidu"这个字符串是否存在,就要判断位图中下标2,4,7对应的值是否均为1,若是,则说明此字符串“可能”存在。注意这里就可能出现误判了,至于为什么我们先再存一个字符串"tencent",假设哈希函数返回3,4,8,则对应的图如下:

【C++】哈希的应用——布隆过滤器

  • 值得注意的是,4 这个 bit 位由于两个值的哈希函数都返回了这个 bit 位,因此它被覆盖了。现在我们如果想查询 “alibaba” 这个值是否存在,哈希函数返回了 2、5、8三个值,结果我们发现 5 这个 bit 位上的值为 0,说明没有任何一个值映射到这个 bit 位上,因此我们可以很确定地说 “alibaba” 这个值不存在。而当我们需要查询 “baidu” 这个值是否存在的话,那么哈希函数必然会返回 2、4、7,然后我们检查发现这三个 bit 位上的值均为 1,那么我们可以说 “baidu” 存在了么?答案是不可以,只能是 “baidu” 这个值可能存在(发生了误判)。
  • 这是为什么呢?答案很简单,因为随着增加的值越来越多,被置为 1 的 bit 位也会越来越多,这样某个值 “taobao” 即使没有被存储过,但是万一哈希函数返回的三个 bit 位都被其他值置位了 1 ,那么程序还是会判断 “taobao” 这个值存在。像上面的字符串baidu,哈希函数返回的是2,4,7,可是先前的字符串baidu,哈希函数返回的是3,4,8,你怎么知道比特位4的值对应的是字符串baidu呢?我说它是字符串baidu的也没毛病吧,因此“baidu”可能存在。这就是误判出现的典型现象。

**总结:**布隆过滤器是无法解决误判的问题的,一个key通过多种哈希函数映射多个比特位只能说是降低误判的概率,但无法去除。


4.布隆过滤器的应用场景

根据布隆过滤器的概念,我们得知,只要数据允许误判,并且不会对业务造成影响,就允许使用布隆过滤器,有如下场景。

1、注册的时候,快速判断一个昵称是否使用过

  • 如果一个不在布隆过滤器里头,表示没有用过;如果在,就需要再去数据库确认查找一遍

2、黑名单

  • 如果一个人不在布隆过滤器里头,表示可同行;如果在,需要再去系统确认

3、过滤层,提高查找数据效率

  • 如果一个数据在布隆过滤器里头,接着去数据系统中查找具体的那个;如果不在,直接返回,可以不用进行后续昂贵的查询请求。

4、对爬虫网址进行过滤,爬过的不用再爬;

……


5.布隆过滤器优缺点

优点:

  1. 增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关
  2. 哈希函数相互之间没有关系,方便硬件并行运算
  3. 布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势
  4. 在能够承受一定的误判时,布隆过滤器比其他数据结构有着很大的空间优势
  5. 数据量很大时,布隆过滤器可以表示全集,其他数据结构不能
  6. 使用同一组散列函数的布隆过滤器可以进行交、并、差运算

缺点:

  1. 有误判率,即存在假阳性(False Position),即不能准确判断元素是否在集合中(补救方法:再建立一个白名单,存储可能会误判的数据)
  2. 不能获取元素本身
  3. 一般情况下不能从布隆过滤器中删除元素
  4. 如果采用计数方式删除,可能会存在计数回绕问题

6.如何选择哈希函数个数和布隆过滤器长度

  • 很显然,过小的布隆过滤器很快所有的 bit 位均为 1,那么查询任何值都会返回“可能存在”,起不到过滤的目的了。布隆过滤器的长度会直接影响误报率,布隆过滤器越长其误报率越小。
  • 另外,哈希函数的个数也需要权衡,个数越多则布隆过滤器 bit 位置位 1 的速度越快,且布隆过滤器的效率越低;但是如果太少的话,那我们的误报率会变高。

这是一位大佬绘制出来的一幅图,详细的说明了误判率和哈希函数个数及布隆过滤器长度之间的关系:

【C++】哈希的应用——布隆过滤器

如何选择适合业务的哈希函数的个数和布隆过滤器长度呢,一大佬给出的一个公式:

【C++】哈希的应用——布隆过滤器

其中k为哈希函数个数,m为布隆过滤器长度,n为插入的元素个数,p为误判率。

我们可以大概估算一下如果使用3个哈希函数,k = 3,ln2≈0.7,k = m/n * 0.7

通过计算得知m和n的关系大概是m = 4.3n,也就是布隆过滤器的长度应该是插入元素个数的4倍。


二、布隆过滤器的实现

1.布隆过滤器基本框架

  • 这里布隆过滤器要实现成一个模板类,因为布隆过滤器插入的元素类型不固定(整型、字符串……),正因为元素类型不固定,所以要通过哈希函数把数据类型转换为整型。但一般情况下布隆过滤器都是用来处理字符串的,所以这里可以将模板参数K的缺省类型设置为string。这里我们假定传入3个哈希函数,通过上述计算,布隆过滤器的长度大概是插入元素个数的四倍。
  • 布隆过滤器的成员也是一个位图,我们可以在布隆过滤器设置一个非类型模板参数M,用于调用者指定位图的长度。
template<size_t N, 
	size_t X = 5,
	class K = string,
	class HashFunc1 = BKDRHash,
	class HashFunc2 = APHash,
	class HashFunc3 = DJBHash,
	class HashFunc4 = JSHash>

布隆过滤器的三个哈希函数的作用是把数据转换成三个不同的整型,便于后续建立映射关系,这里我们使用BKDRHash、APHash和DJBHash这三种算法:

struct BKDRHash
{
	size_t operator()(const string& key)
	{
		size_t hash = 0;
		for (auto ch : key)
		{
			hash *= 131;
			hash += ch;
		}
		return hash;
	}
};

struct APHash
{
	size_t operator()(const string& key)
	{
		unsigned int hash = 0;
		int i = 0;

		for (auto ch : key)
		{
			if ((i & 1) == 0)
			{
				hash ^= ((hash << 7) ^ (ch) ^ (hash >> 3));
			}
			else
			{
				hash ^= (~((hash << 11) ^ (ch) ^ (hash >> 5)));
			}
            
			++i;
		}
		return hash;
	}
};

struct DJBHash
{
	size_t operator()(const string& key)
	{
		unsigned int hash = 5381;

		for (auto ch : key)
		{
			hash += (hash << 5) + ch;
		}

		return hash;
	}
};

struct JSHash
{
	size_t operator()(const string& s)
	{
		size_t hash = 1315423911;
		for (auto ch : s)
		{
			hash ^= ((hash << 5) + ch + (hash >> 2));
		}
		return hash;
	}
};

其它哈希算法的链接:各种字符串Hash函数算法


2.布隆过滤器的Set插入

布隆过滤器的插入就是提供一个Set接口,核心思想就是把插入的元素通过三个哈希函数获取对应的整型并%比特位数从而获得对应的3个映射位置,再把这三个位置置为1即可。

【C++】哈希的应用——布隆过滤器

//set插入
void set(const K& key)
{
	size_t hash1 = HashFunc1()(key) % (N * X);
	size_t hash2 = HashFunc2()(key) % (N * X);
	size_t hash3 = HashFunc3()(key) % (N * X);
	size_t hash4 = HashFunc4()(key) % (N * X);
	
    _bs.set(hash1);
	_bs.set(hash2);
	_bs.set(hash3);
	_bs.set(hash4);
}

3.布隆过滤器的Test查找

布隆过滤器的查找就是提供一个Test接口,实现规则如下:

  1. 把测试数据通过三个哈希函数获取对应的整型并%比特位数从而获得对应的3个映射位置
  2. 如果三个位置中有任何一个位置不是1,直接返回false,说明查找的值不可能存在
  3. 只有三个位置全部为1,才可返回true,但是可能会存在误判(上面已经讲过)
//test查找
bool test(const K& key)
{
	size_t hash1 = HashFunc1()(key) % (N * X);
	size_t hash2 = HashFunc2()(key) % (N * X);
	size_t hash3 = HashFunc3()(key) % (N * X);
	size_t hash4 = HashFunc4()(key) % (N * X);

	if (!_bs.test(hash1))
	{
		return false;
	}
			
	if (!_bs.test(hash2))
	{
		return false;
	}

	if (!_bs.test(hash3))
	{
		return false;
	}

	if (!_bs.test(hash4))
	{
		return false;
	}
    
	// 前面判断不在都是准确,不存在误判
	return true; // 可能存在误判,映射几个位置都冲突,就会误判
}

4.布隆过滤器的删除

布隆过滤器不能直接支持删除工作,因为在删除一个元素时,可能会影响其他元素。

  • 比如:删除上图中"create"元素,如果直接将该元素所对应的二进制比特位置0,“source”元素也被删除了,因为这两个元素在多个哈希函数计算出的比特位上刚好有重叠。

一种支持删除的方法(计数法删除):

  • 将布隆过滤器中的每个比特位扩展成一个小的计数器,插入元素时给k个计数器(k个哈希函数计算出的哈希地址)加一,删除元素时,给k个计器减一,通过多占用几倍存储空间的代价来增加删除操作。

【C++】哈希的应用——布隆过滤器

缺陷:

  1. 无法确认元素是否真正在布隆过滤器中
  2. 存在计数回绕

总结:

  • 布隆过滤器不支持直接删除归根结底在于其主要就是用来节省空间和提高效率的,在计数法删除时需要遍历文件或磁盘中确认待删除元素确实存在,而文件IO和磁盘IO的速度相对内存来说是很慢的,并且为位图中的每个比特位额外设置一个计数器,就需要多用原位图几倍的存储空间,这个代价也是不小的。若支持删除就不那么节省空间了,也就违背了布隆过滤器的本质需求。

相关参考文献链接:布隆过滤器的原理,使用场景和注意事项文章来源地址https://www.toymoban.com/news/detail-421589.html

到了这里,关于【C++】哈希的应用——布隆过滤器的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++ 哈希思想应用:位图,布隆过滤器,哈希切分

    1.问题 给你40亿个不重复的无符号整数,没排过序.给一个无符号整数,如何快速判断一个数是否在这40亿个数中? 2.分析 1 Byte = 8 bit 1KB = 1024 Byte 1MB = 1024KB = 1024 1024 大约= 10的6次方Byte 1GB = 1024MB = 1024 10的6次方 大约= 10的9次方Byte = 10亿字节 因此4GB 约等于40亿字节 其实最快的方式就是

    2024年04月17日
    浏览(30)
  • [C++]哈希应用之位图&布隆过滤器

               主厨:邪王真眼 主厨的主页:Chef‘s blog   所属专栏:c++大冒险        我们之前学习了哈希表,哈希表通过映射关系,实现了O(1)的复杂度来查找数据,哈希在实践中是一个非常重要的思想,今天要学习的就是哈希思想的两大应用:位图与布隆过滤器 给 40 亿个

    2024年04月15日
    浏览(28)
  • 【C++】哈希的应用:位图、哈希切分与布隆过滤器

    需要云服务器等云产品来学习Linux的同学可以移步/--腾讯云--/--阿里云--/--华为云--/官网,轻量型云服务器低至112元/年,新用户首次下单享超低折扣。   目录 一、位图 1、位图的概念 2、大厂面试题 2.1位图应用(腾讯) 2.2位图应用 3、位图的优缺点 二、哈希切分 三、布隆过滤

    2023年04月09日
    浏览(34)
  • 【C++学习】哈希的应用—位图与布隆过滤器

    文章简介 : 在这篇文章中,你会学习到关于哈希思想的最常见的两个应用,也就是 位图 与 布隆过滤器 , 文章会讲解位图和布隆过滤器的概念,底层实现,对应的适应的场景,以及相关经典 海量数据面试题 及解析。 所谓位图,就是用每一位来存放某种状态,适用于 海量

    2024年04月14日
    浏览(46)
  • 【C++高阶(六)】哈希的应用--位图&布隆过滤器

    💓博主CSDN主页:杭电码农-NEO💓   ⏩专栏分类:C++从入门到精通⏪   🚚代码仓库:NEO的学习日记🚚   🌹关注我🫵带你学习C++   🔝🔝 哈希最常用的应用是unordered 系列的容器,但是当面对海量数据 如100亿个数据中找有没有100这 个数时,使用无序容器的话内存放不下 所以哈希

    2024年02月05日
    浏览(32)
  • C++进阶--哈希的应用之位图和布隆过滤器

    哈希是一种映射的思想。 先来看一道题:给40亿个不重复的无符号整数,没排序过。给一个无符号整数,如何 快速判断 一个数 是否在 这40亿个数中。 首先想到的解法可能有这几种: 解法1 :遍历40亿个数,O(N) 解法2 :先排序,快排O( N l o g 2 N Nlog_2N Nl o g 2 ​ N ),再利

    2024年02月22日
    浏览(34)
  • C++哈希hash:位图、布隆过滤器的实现及应用

    所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用 来判断某个数据存不存在的。 当我们想查找某一个数据是否存在或者是否处于某种状态时,相比于直接对存放数据的容器进行遍历查找,与原存放数据的容器所建立映射关系的位图来

    2024年04月11日
    浏览(34)
  • 哈希思想应用【C++】(位图,布隆过滤器,海量数据处理面试题)

       目录 一,位图 1. 位图概念 2.实现 3. 测试题 位图的优缺点 二,布隆过滤器 1). 布隆过滤器提出 2). 概念 3). 布隆过滤器的查找 4). 布隆过滤器删除(了解) 5). 布隆过滤器优点 6). 布隆过滤器缺陷 三,海量数据面试题 1)哈希切割 我们首先由一道面试题来理解位图 给40亿个不

    2024年02月04日
    浏览(33)
  • 哈希的应用——布隆过滤器

    上一篇文章,我们学习了位图,位图在某些场景下是非常适用的,非常快捷方便。 但是,在文章的最后,我们也提出了位图的一些缺陷——比如位图只能映射整型数据,其它类型的数据则不行。 因为位图里面的元素去映射的其实就是下标嘛,而下标的话都是整型啊。 那有没

    2024年02月09日
    浏览(30)
  • 【C++】哈希(位图,布隆过滤器)

    今天的内容是哈希的应用:位图和布隆过滤器 目录 一、位图 1.位图概念 2.位图的应用 二、哈希切分 三、布隆过滤器 1.布隆过滤器的概念 2.布隆过滤器的应用 四、总结   今天的内容从一道面试题开始引入: 给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何

    2024年02月01日
    浏览(24)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包