【C++】哈希(位图,布隆过滤器)

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

今天的内容是哈希的应用:位图和布隆过滤器【C++】哈希(位图,布隆过滤器)


目录

一、位图

1.位图概念

2.位图的应用

二、哈希切分

三、布隆过滤器

1.布隆过滤器的概念

2.布隆过滤器的应用

四、总结


 文章来源地址https://www.toymoban.com/news/detail-428809.html

一、位图

1.位图概念

今天的内容从一道面试题开始引入:

给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在
这40亿个数中。
 
首先我们对40亿个无符号整数改变一下,它到底是多少G呢?
 
40亿个整数大概是  40亿*4个字节=160亿个字节
 
4G=2^32byte,大概为42亿九千万字节,所以1G大概就是10亿字节 ,所以40亿个整数大概就是16G,那这么大数据放到内存中肯定是放不下的,所以什么二分查找,什么map,set更何况还有额外的消耗,这更不可能完成了,于是我们可以利用哈希的思想来搞一个位图!
 
        判断数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0
代表不存在。
 

位图是一种直接定址法的哈希,因此效率很高,用O(1)就可以探测到对应位是0还是1,效率非常高,因此可以快速判断。

 

利用每一个比特位的0或1的情况,来判断数在不在,所以40亿不重复的数,开辟2^32-1个比特位,转化为G,也就512m,内存很小。

【C++】哈希(位图,布隆过滤器)

举例说明:

 

【C++】哈希(位图,布隆过滤器) 每个数我们可以先num/8,算出他在第几个char里,然后再num%8算出在哪一位
比如:23/8=2,在第二个char;23%8=7,在第七位上面。

那如何把任意一位置1,且不改变其他位?

【C++】哈希(位图,布隆过滤器)

把它和左移(向高位移动)以后的1(即其他位是0,只有要改变那一位是1)和原来的数进行或运算,就可以得到结果。保证了其他位不变,只有该位被改变为了1.

那到底怎么移动呢?

【C++】哈希(位图,布隆过滤器)(一个char中)

 

那可能有人就会想,这会不会跟大小端有关系,数据在内存中的存储形式???

错错错,大错特错,首先大小端只存在于大于1字节的数据类型中,其次不管从哪边移动,本质是向高位或者低位移动。

所以说,%8以后,是哪一位,1直接左移几位(即向高位移动)。

 

那么在把某一位置为1以后,要重新置为0的话,应该怎么搞呢?

同理得:直接将1移位以后,再取反,将结果和原数进行与运算。

 

那要测试这个数在不在位图中,怎么测试呢?也就是看某一位是不是1

直接返回  1移位以后和原数相与的结果,不为0则存在,为0则不存在。

 

我们来看代码实现:


template<size_t N>
	class bitset
	{
	public:
		bitset()
		{
			//_bit.resize((N/8) + 1, 0);
			_bit.resize((N >> 3) + 1, 0);//左移3位就相当于/8,效率更快一些,但要注意运算符的优先级
		}

		void set(size_t x)
		{
			size_t i = x >> 3;
			size_t j = x % 8;
			_bit[i] |= (1 << j);//在知道是哪一个char之后,直接把这一个char相与。
		}

		void reset(size_t x)
		{
			size_t i = x >> 3;
			size_t j = x % 8;
			_bit[i] &= (~(1 << j));
		}
		bool test(size_t x)
		{
			size_t i = x >> 3;
			size_t j = x % 8;
			return _bit[i] & (1 << j);
		}
	private:
		vector<char> _bit;
	};

2.位图的应用

1. 给定100亿个整数,设计算法找到只出现一次的整数?
 
统计次数的话,那么就需要两个位图来实现,两个比特位来统计00(0次),01(1次),10(2次及以上)。
【C++】哈希(位图,布隆过滤器)
直接上代码:
template<size_t n>
	class two_bitset
	{
	public:
		void set(size_t x)
		{
			if (!_bs1.test(x) && !_bs2.test(x))//00
			{
				_bs2.set(x); //0次变1次
			}
			else if (!_bs1.test(x) && _bs2.test(x))//01
			{
				_bs1.set(x);
				_bs2.reset(x);//1次变两次
			}
		}
		void printonce()
		{
			for (size_t i = 0; i < n; ++i)
			{
				if (!_bs1.test(i) && _bs2.test(i))
				{
					cout << i << endl;
				}
			}
			cout << endl;
		}
	private:
		bitset<n> _bs1;
		bitset<n> _bs2;
	};

一个数如果在两个位图中的同一位置都是0,那么说明就是0次 ,再进来的数就要将00第二位set为01,表示出现一次,后面同理可得。

2. 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
首先我们知道,整数的范围最大是42亿多,所以100亿个整数中,一定存在许多重复的整数。
所以将文件中的数据都放入位图中,只看 存在或者不存在两种状态,这就自动去掉了重复的数,某一位一直是1。只有512MB,可以存入内存中进行处理。
然后两个位图进行相与操作,同时为1说明是交集。
 
3. 位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整
首先我们知道,整数的范围最大是42亿多,所以100亿个整数中,一定存在许多重复的整数。
所以将文件中的数据都放入位图中,只看 存在或者不存在两种状态,占用内存很少。
通过两个位图来表示次数,00(0次),01(1次),10(2次),11(3次及以上),然后控制条件(只找01,10)输出结果,和第一个问题其实是一样的。

二、哈希切分

还是一道面试题来引入哈希切分:

给一个超过100G大小的log fifile, log中存着IP地址, 设计算法找到出现次数最多的IP地址?
 
由前面所学,我们可能会想到位图,但是行不通,要统计次数就要开辟多个位图,
【C++】哈希(位图,布隆过滤器)
成倍的开辟位图来表示次数的话,会占用大量的内存空间,内存也存不下。
 
我们用的是map,但是在用map之前,要把大文件处理:
 
那我们就可以利用哈希的思想来把100G的文件分成100个小文件,每个1G,那么不就可以进内存了吗?
 
那怎么分???平均分?那当然不行,平均分对于分散的ip地址,都不在同一个小文件中,进入内存用map统计时,结果是不正确的。
 
直接哈希切分!!
【C++】哈希(位图,布隆过滤器)

 我们可以对100G大文件中的ip进行哈希切分,利用哈希表的思想,将哈希值相同的放入同一个小文件中,然后通过一个一个的小文件进入内存读取并统计个数,搞完一个clear掉,记录再进下一个。

理想很美好,现实却有点骨感?

单个文件超过1G:

因为存在哈希冲突,在数据进入小文件时,就会产生下面两种情况:

        1.一个小文件中,差不多都是不重复的数据,且个数还挺多,且map再加额外开销,导致内存很大,直接报错。

        2.一个小文件中,都是很多重复的数据,且个数还挺多,但是map却可以存下(重复的只增加次数),可以统计。

 

所以我们无需判断是哪种情况,直接无脑map,第一种情况发生就抛异常,捕获以后,换另一种哈希函数,再进行递归分割,拆成更小的文件后用map统计次数。

你懂了吗?【C++】哈希(位图,布隆过滤器)

与上题条件相同,如何找到top K的IP?如何直接用Linux系统命令实现?
利用堆来解决topK问题。

三、布隆过滤器

1.布隆过滤器的概念

开始讲布隆过滤器之前,我们要说一说位图的缺点是什么?

最大的缺点就是:1.开空间得看数据范围,一般要求范围集中,分散的话空间消耗就会上升

                             2.只能针对整型

如果给了一堆字符串,可不可以使用位图判断是否存在呢?

当然可以,可以使用哈希函数,将字符串转化为整型,再去映射到位图中。

当针对字符串来判断是否存在时,位图+哈希其实就是我们要讲的布隆过滤器。

布隆过滤器是由布隆(Burton Howard Bloom)在1970年提出的 一种紧凑型的、比较巧妙的
率型数据结构,特点是 高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存
在”,它是用多个哈希函数,将一个数据映射到位图结构中。此种方式 不仅可以提升查询效率,也
可以节省大量的内存空间
 
话不多说,上例子来理解这段话:
【C++】哈希(位图,布隆过滤器)
 
当不同的字符串通过哈希函数转化为整型映射到位图中时,就会发生哈希碰撞!
比如find通过哈希函数可能和insert映射到同一位置,那么当find不存在时,但是他的位置的确已经被置为1,所以这就导致了:
                         判断存在是不准确的
                        判断不存在一定是准确的,因为位置是0,那一定不存在
【C++】哈希(位图,布隆过滤器)

于是,我们就要想一些办法,让他的误判率低一些:

可以增加不同的哈希函数,转化为不同的哈希值,去映射到多个位置,降低误判率

【C++】哈希(位图,布隆过滤器)

 这样的话,我们可以看到,只有当一个字符串映射的全部位置都置为1时,这个数才可能存在,说的是可能存在,因为也可能存在哈希碰撞。但降低了哈希碰撞的概率,降低了误判率。【C++】哈希(位图,布隆过滤器)

那还有问题就是:一个字符串映射多个位图的位置,那位图应该开多大呢?

                             或者说如何选择哈希函数个数和布隆过滤器长度?

直接上大佬:大佬研究出来的一个公式:

【C++】哈希(位图,布隆过滤器)

 现在来实现布隆过滤器:

【C++】哈希(位图,布隆过滤器)

template<
		size_t N,
		size_t M,
		class K = string,
		class Hash1= BKDRHash,
		class Hash2= APHash,
		class Hash3 = DJBHash>
	class Bloomfilter
	{
	public:
		void set(const K& key)
		{
			size_t i = Hash1()(key) % (N * M);
			size_t j = Hash2()(key) % (N * M);
			size_t k = Hash3()(key) % (N * M);

			_bs.set(i);
			_bs.set(j);
			_bs.set(k);
		}

		//void reset() 没有reset的原因是:因为存在哈希冲突,修改一个数的哈希值映射位置的值,会影响到其他的数,导致结果不准确。
		//硬要有reset,就需要计数,通过计数(--)来控制,那就需要成倍的位图来表示个数,严重浪费内存空间。

		//布隆过滤器,存在哈希冲突,所以确定不了一定存在的值
		//但是可以确定一定不存在的值
		bool test(const K& key)
		{
			size_t i = Hash1()(key) % (N * M);
			if (!_bs.test(i))
			{
				return false;
			}
			size_t j = Hash2()(key) % (N * M);
			if (!_bs.test(j))
			{
				return false;
			}
			size_t k = Hash3()(key) % (N * M);
			if (!_bs.test(k))
			{
				return false;
			}
			//到这里说明可能存在
			return true;
		}
	private:
		bitset <N* M> _bs;
	};

那布隆过滤器支持删除吗?当然不支持!

没有reset(不可以删除)的原因是:因为存在哈希冲突,修改一个数的哈希值映射位置的值,会影响到其他的数,导致结果不准确。

硬要有reset,就需要计数,通过计数(--)来控制,那就需要成倍的位图来表示个数,严重浪费内存空间。

【C++】哈希(位图,布隆过滤器)

如上图所示这样实现 

2.布隆过滤器的应用

1.日常应用中,最常见的场景:

【C++】哈希(位图,布隆过滤器)

当数据量比较大时,会存放在磁盘中,磁盘访问速度相对来说很慢,所以在客户端和服务器中间加入布隆过滤器就会很大程度上加快访问速度,提高效率。

在过滤器阶段,数据不存在时,直接返回不存在;存在时,是可能存在(因为存在哈希冲突),所以会继续访问磁盘中的数据,数据在磁盘中存在即存在,不存在返回不存在。

2. 给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出
精确算法和近似算法。
 
query-般是查询指令,比如可能是一个网络请求, https://zhuanlan.zhihu.com/p/43263751/
或者是一个数据库sq|语句
假设平均每个query是50byte, 100亿个query合计多少内存? -- 500G
 
精确算法:交集中一定是准确的(哈希切分)
近似算法:那么一定是允许有误判的情况(有误差),那么就可以使用布隆过滤器。
 
当看到这个题目时,可能就会想到位图来解决,但是100亿个字符串都是不相同的,100亿个字符串已经超过了1G,不可行。
 
精确算法:
利用哈希函数,将100亿个query是500G,因为要到内存中比较两个文件,所以需要分为1000个小文件,每个小文件占用0.5G,那么两个小文件就可以都进内存中比较了。
如图所示:
【C++】哈希(位图,布隆过滤器)

 当然也会出现哈希冲突超过0.5G的情况,若是重复数较多,但是我们是找交集,所以用位图来存或不在时,0.5G的小文件中数据个数占的内存一定小于0.5G,然后两个位图相与即可。

但如果是都不重复,就需要递归继续分割。用位图找交集

四、总结

不同的场景需要我们灵活的去找适合的方法去解决问题。

我们下期再见!【C++】哈希(位图,布隆过滤器)

 

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

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

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

相关文章

  • [C++]哈希应用之位图&布隆过滤器

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

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

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

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

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

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

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

    2024年02月05日
    浏览(46)
  • 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日
    浏览(56)
  • 【C++杂货铺】再谈哈希算法:位图 | 布隆过滤器 | 哈希切分

    给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。 解决方案 : 遍历 :遍历这40亿个整数,去判断该数是否在这40亿个整数中。时间复杂度是 O ( N ) O(N) O ( N ) 。 set :用 set 将这40亿个整数存起来,然后去判断这个数是否在

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

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

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

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

    2024年02月04日
    浏览(45)
  • C++ 哈希+unordered_map+unordered_set+位图+布隆过滤器(深度剖析)

    想象一下,你有一个巨大的图书馆,里面摆满了成千上万本书。如果你想要找到其中一本特定的书,你会怎么做?如果你按照书的编号来有序地排列它们,找到特定的书本可能会很容易。但是,如果书籍是随机地摆放,你可能需要逐本地查找,这个过程会非常耗时。 而哈希函

    2024年02月21日
    浏览(55)
  • 哈希的应用--位图和布隆过滤器

    位图(Bitset)是一种数据结构,用于表示一组布尔值,其中每个元素通常对应于一个位或一个二进制值,可以存储0或1。位图在计算机科学和计算机工程中经常用于各种应用,特别是在位级别的标志、掩码和快速查找中。以下是位图的一些关键特点: 二进制表示 :位图中的每

    2024年02月08日
    浏览(53)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包