位图/布隆过滤器/海量数据处理方式

news2024/11/15 13:38:15

位图

位图的概念 

所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在的。

直接来看问题:

给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在
这40亿个数中。

思路:解决问题的方法,可以使用位图来解决。把这40亿个数据映射在位图上,将位图上对应的比特位置为1。然后拿着需要判断的数在位图上看看其对应的比特位是否为1,如果是则存在,否则为0。

具体做法:

使用直接定址法,这40亿个数据的值是几,就把第几个比特位标记为1。因为40亿个整数,大概需要16G内存,而使用比特位,我们只需使用char作为存储在vector上的类型,每一个都是1bit大,因此在vector上开辟2^32大小的空间,表示数据大小范围,一共512M。

 开辟好空间后,开始将每一个数据映射到位图上。每一个char对象为8bit,于是让每一个值先确定自己在哪个char对象上,然后确定映射在哪个比特位上。

x映射的值,在第 x/8 个char对象上。

x映射的值,在第 x%8 个比特位上。

所以,我们可以根据上面的理论,用代码简单实现位图

使用非模板参数N,作为数据的个数。

开辟空间:空间开辟的大小为N /8 +1,因为N个数据,每8个为一组,多开辟一组,避免N不是8的整除。然后初始化为0。即位图上的比特位一开始全是0.

		//初始化空间,初始值为0
		bitset()
		{
			_bits.resize((N >> 3) + 1, 0);
		}

数据映射位图上的比特位:先计算好数据所在的组别和比特位的位置,然后将其置为1。置为1的操作是让这一个char对象组别的比特位与这个数据的比特位进行或运算。

		void set(size_t x)
		{
			size_t i = x >> 3;//位于哪一个char对象上
			size_t j = x % 8;//位于这个char对象上的哪个比特位上

			_bits[i] |= (1 << j);//通过或运算,将x对应的比特位变为1
		}

将某个数据映射的比特位从1变回0:同样的找到这个位置后,然后这一组别的比特位与这个数据的比特取反后进行与运算。

		void reset(size_t x)
		{
			size_t i = x >> 3;
			size_t j = x % 8;

			_bits[i] & = (~(1 << j));//通过与运算,让x对应的比特位变为0
		}

判断一共数据是是否存在:同样,先计算出这个数据映射的位置。然后返回这一组别跟这个数据的比特,然后进行与运算,注意不是与等,是不能改变原本位图的比特位的。

		//判断x是否存在,如果存在返回true
		bool test(size_t x)
		{
			size_t i = x >> 3;
			size_t j = x % 8;

			return _bits[i] & (1 << j);
		}

完整代码如下:

namespace my_BitSet
{
	template<size_t N>
	class bitset
	{
	public:
		//初始化空间,初始值为0
		bitset()
		{
			_bits.resize((N >> 3) + 1, 0);
		}

		void set(size_t x)
		{
			size_t i = x >> 3;//位于哪一个char对象上
			size_t j = x % 8;//位于这个char对象上的哪个比特位上

			_bits[i] |= (1 << j);//通过或运算,将x对应的比特位变为1
		}
		void reset(size_t x)
		{
			size_t i = x >> 3;
			size_t j = x % 8;

			_bits[i] & = (~(1 << j));//通过与运算,让x对应的比特位变为0
		}

		//判断x是否存在,如果存在返回true
		bool test(size_t x)
		{
			size_t i = x >> 3;
			size_t j = x % 8;

			return _bits[i] & (1 << j);
		}
	private:
		vector<char> _bits;
	};
}

布隆过滤器

位图对于判断大量数据中是否存在某一个数据的情况固然是好,其优点是节省空间和判断速度块。但其缺点是一般要求范围相对集中,如果范围特别分散,那么空间消耗就大了,而且是只针对整型。因此,布隆过滤器降临!

布隆过滤器的概念

布隆过滤器是一种紧凑型的、比较巧妙的概率型数据结构,特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”,它是用多个哈希函数,将一个数据映射到位图结构中,因为布隆过滤器是哈希+位图的结合。此种方式不仅可以提升查询效率,也可以节省大量的内存空间。

一般的位图下,每一个数据只跟位图产生一个映射点,而且只能用于整型。但布隆过滤器是每一个数据可以有N个映射点,N个映射点对应于N个哈希函数,这个是我们自己定义的。用哈希函数将非整型转化成整型。

 布隆过滤器的长度的计算方式:

使用公式:

 K为哈希函数的个数,m为布隆过滤器长度,n为数据的个数。假设K为3,而ln2约等于0.7,因此m==4.2n。

布隆过滤器的功能支持:

布隆过滤器支持set和test方法,最好不要有将1变回0的操作。因为这样会导致其它数据的判断的误差。如果真的要支持,就用计数的方法,但这种方法不推荐。

简单实现代码如下

这里使用3个哈希函数,分别为:BKDRHash、APHash和DJBHash。使用string为类型。

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);

			//计算出位置后,使用位图的set方法将位图上对应的比特位进行0变1
			_bs.set(hash1);
			_bs.set(hash2);
			_bs.set(hash3);
			
		}

test方法:

		bool test(cost K& key)
		{
			//先逐个位置判断,如果它是0,直接返回false
			size_t hash1 = HashFunc1()(key) % (N * X);
			if (!_bs.test(hash1))
			{
				return false;
			}
			size_t hash2 = HashFunc2()(key) % (N * X);
			if (!_bs.test(hash2))
			{
				return false;
			}
			size_t hash3 = HashFunc3()(key) % (N * X);
			if (!_bs.test(hash3))
			{
				return false;
			}

			//直到最后,说明该数据是存在的,返回true
			return true;
		}

整体代码如下:

namespace my_BloomFilter
{
	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;
		}
	};

	template<size_t N,
		size_t X = 5,
		class K = string,
		class HashFunc1 = BKDRHash,
		class HashFunc2 = APHash,
		class HashFunc3 = DJBHash>
	class BloomFilter
	{
	public:
		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);

			//计算出位置后,使用位图的set方法将位图上对应的比特位进行0变1
			_bs.set(hash1);
			_bs.set(hash2);
			_bs.set(hash3);
			
		}

		bool test(cost K& key)
		{
			//先逐个位置判断,如果它是0,直接返回false
			size_t hash1 = HashFunc1()(key) % (N * X);
			if (!_bs.test(hash1))
			{
				return false;
			}
			size_t hash2 = HashFunc2()(key) % (N * X);
			if (!_bs.test(hash2))
			{
				return false;
			}
			size_t hash3 = HashFunc3()(key) % (N * X);
			if (!_bs.test(hash3))
			{
				return false;
			}

			//直到最后,说明该数据是存在的,返回true
			return true;
		}

	private:
		std::bitset<N* X> _bs;
	};
}

海量数据处理问题

哈希切割

给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址?

超过100G大小的文件,肯定不能直接放到内存中,而是通过将它切割,分成很多份。那么如何去切割呢?是平均分成100份,每一份1G这样吗?

如果平均切割,那么会导致的问题是:如果文件中有好几个相同的值,且分布不集中,此时平均切割就很可能使一个IP有很多份在很多小文件中。

因此不能平均切割,需要的是哈希切割。哈希切割就是通过取模,让取模结果相同的数据放到同一份小文件里面。

哈希切割后,通过map来对每一个小文件进行统计。

小问题如果超过1G的问题:

①不重复的IP有很多个,map就需要很多节点,因此map是统计不下来的。

②重复的IP有很多个,map可以统计下来,因为节点不多。

解决方法:

先不看什么情况,直接用map统计,如果是第二种情况的话就直接统计下来了。但是第一种情况,会在insert的时候失败,因此可以在失败的时候捕捉异常,接着换哈希函数递归切分再统计即可。

位图的应用 

1.给定100亿个整数,设计算法找到只出现一次的整数?

只出现一次,那就说明,它在位图中比特位是:01。如果找到该位置发现是00或11或者其它的情况,那就不是。

但一个一般的位图只会出现单个比特,即要么是0,要么是1,不会出现两个比特。这里的方法使用两个位图的结构。即定义两个位图,然后用同一个数据计算出来的同一个位置,分别在这个两个位图上进行0和1的操作。

简单的代码实现:

	template<size_t N>
	class twobitset
	{
	public:
		void set(size_t x)
		{
			//初次映射:两个位图对应的比特位都为0,即00
			if (!_bs1.test(x) && !_bs2.test(x)//  00
			{
				_bs2.set(x);//  01
			}
			else if (!_bs1.test(x) && _bs2.test(x) //  01
			{
				//第二次遇到这个数字后,此时是01的,要变成10
				_bs1.set(x); //11
				_bs2.reset(x); // 10
			}
			//如果第三次遇到,也不用管了,第二次遇到的时候就已经不是它了
			//10
			//11
		}

		void PrintOnce()
		{
			for (size_t i = 0; i < N; ++i)
			{
				if (!_bs1.test(i) && _bs2.test(i))  // 01 出现一次
				{
					cout << i << endl;
				}
			}
			cout << endl;
		}
	private:
		bitset<N> _bs1;
		bitset<N> _bs2;
	};
}

 2.给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?

这里提供两种思路:

思路1:先将一个文件的数据映射到位图中,然后用另外一个文件的数据去遍历,得到交集,需要注意去重。

思路2:分布将两文件映射到两个位图,然后通过两位图的与运算判断是否有交集。

3.位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数。

这道问题跟第一个问题基本一样,就是让“01”和"10"为需要找到的整数。如果出现"11"以上,那么就不行。

布隆过滤器的应用

1. 给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法。

query是一般为一个查询指令,可能是一个网络请求的指令,也可能是一个数据库sql语句。

精确算法找文件交集的思路是:分别给两个文件创建布隆过滤器,然后让它们进行哈希切割,分成一个个小文件。最后通过编号相同的小文件中查找交集。

近似算法的思路是:将一个文件的数据映射到一个布隆过滤器中,然后另外一个文件去查找有没有相同的,有就是交集。这种算法会造成误判。

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

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

相关文章

Gradle+SpringBoot多模块开发

关于使用Gradle结合SpringBoot进行多模块开发。 本来是打算使用buildSrc之类的&#xff0c;但是感觉好像好麻烦&#xff0c;使用这种方法就可以实现&#xff0c;没必要采用其他的。 我不怎么会表述&#xff0c;可能写的跟粑粑一样&#xff0c;哈哈哈哈 这是我的项目地址。 存在…

Java速成篇-Day01笔记

提示&#xff1a;这里只记录我个人不熟悉的知识&#xff0c;并非所有内容 笔记目录课程&#xff1a;04-第一行代码① jshell② 对象.方法课程&#xff1a;05-第一份源码① Java开发程序的流程② 入口方法课程&#xff1a;06-常见问题-中文乱码① 乱码原因② 解决方法课程&#…

【基础算法】单链表的OJ练习(4) # 分割链表 # 回文链表 #

文章目录前言分割链表回文链表写在最后前言 本章的OJ练习相对前面的难度加大了&#xff0c;但是换汤不换药&#xff0c;还是围绕单链表的性质来出题的。我相信&#xff0c;能够过了前面的OJ练习&#xff0c;本章的OJ也是轻轻松松。 对于OJ练习(3)&#xff1a;-> 传送门 <…

华为OD机试题,用 Java 解【单词反转】问题

华为Od必看系列 华为OD机试 全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典使用说明 参加华为od机试,一定要注意不…

在 Docker 安装 Oracle12

说明 单独在Linux上安装Oracle很繁琐&#xff0c;首先需要安装各种依赖&#xff0c;其次安装如果失败了&#xff0c;重新配置安装也挺麻烦&#xff0c;所以如果是开发或者测试的时候使用Docker来进行安装会非常的方便。 搜索了很多的oracle相关镜像&#xff0c;选择一个适合自…

【Linux】-- 基本指令

目录 用户管理 adduser passwd userdel pwd ls指令 -l -a -d -F -r -t -R -1 which alias ll ls -n cd cd - cd ~ touch -d stat mkdir -p rmdir rm -r -f man cp ​编辑 -r -f mv cat -n tac more less -N head tail | 管道 dat…

Cookies与Session

&#x1f482;作者简介&#xff1a; THUNDER王&#xff0c;一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学会计学专业大二本科在读&#xff0c;同时任汉硕云&#xff08;广东&#xff09;科技有限公司ABAP开发顾问。在学习工作中&#xff0c;我通常使用偏后…

JavaScript基础六、函数

零、文章目录 文章地址 个人博客-CSDN地址&#xff1a;https://blog.csdn.net/liyou123456789个人博客-GiteePages&#xff1a;https://bluecusliyou.gitee.io/techlearn 代码仓库地址 Gitee&#xff1a;https://gitee.com/bluecusliyou/TechLearnGithub&#xff1a;https:…

Deploy Workshop|DIY部署环境,让OceanBase跑起来

2023 年 3 月 25 日&#xff0c;我们将在北京开启首次 OceanBase 开发者大会&#xff0c;与开发者共同探讨单机分布式、云原生、HTAP 等数据库前沿趋势&#xff0c;分享全新的产品 Roadmap&#xff0c;交流场景探索和最佳实践&#xff0c;此外&#xff0c;OceanBase 开源技术全…

Qt多线程文件查找器

⭐️我叫恒心&#xff0c;一名喜欢书写博客的研究生在读生。 原创不易~转载麻烦注明出处&#xff0c;并告知作者&#xff0c;谢谢&#xff01;&#xff01;&#xff01; 这是一篇近期会不断更新的博客欧~~~ 有什么问题的小伙伴 欢迎留言提问欧。 Qt多线程文件查找器 前言 最近…

JUC并发编程——wait-notify

目录一、wait / notify1.1 wait / notify 原理1.2 wait / notify API介绍二、wait VS sleep三、wait / notify —代码改进一、wait / notify 1.1 wait / notify 原理 ● Owner线程发现条件不满足&#xff0c;调用wait( )方法即可进入WaitSet变为 WAITING状态 ● BLOCKED 和 W…

AI的简单介绍

什么是AI&#xff1f; AI 是 Artificial Intelligent 的缩写&#xff0c;是我们通常意义上说的人工智能。 简单来说就是让机器能够模拟人类的思维能力&#xff0c;让它能够像人一样感知、思考甚至决策。 为什么要开发AI&#xff1f; 因为在过去&#xff0c;都是我们学习机器…

408 计算机基础复试笔记 —— 更新中

计算机组成原理 计算机系统概述 问题一、冯诺依曼机基本思想 存储程序&#xff1a;程序和数据都存储在同一个内存中&#xff0c;计算机可以根据指令集执行存储在内存中的程序。这使得程序具有高度灵活性和可重用性。指令流水线&#xff1a;将指令分成若干阶段&#xff0c;每…

opencv学习(一)图像的基本操作

数据的读取cv2.IMREAD_COLOR:彩色图像cv2.IMREAD_GRAYSCALE:灰度图像import cv2 img cv2.imread(E:/opencv/open-cv/2-7/cat.jpg,1)cv2.imshow("img", img) cv2.waitKey(0) cv2.destroyAllWindows() cv2.imread()读取图片&#xff0c;当括号里面是1时&#xff…

华为OD机试题,用 Java 解【水仙花数】问题

华为Od必看系列 华为OD机试 全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典使用说明 参加华为od机试,一定要注意不…

抓包技术(浏览器APP小程序PC应用)

P1 抓包工具 01. Fidder 首先第一个Fiddler它的优势&#xff0c;独立运行&#xff0c;第二个支持移动设备&#xff08;是否能抓移动APP的包&#xff0c;&#xff09;在这一块的话wireshark、httpwatch就不支持&#xff0c;因此在这一块就可以排除掉前连个&#xff0c;因为我们…

SpringCloudGateway--基于redis实现令牌桶算法

目录 一、限流算法 1、计数器算法 2、漏桶算法 3、令牌桶算法 二、Gateway中的限流 一、限流算法 1、计数器算法 计数器算法是指从第一个请求开始&#xff0c;每多一个请求就加1&#xff0c;假设设置每秒限流100&#xff0c;当在一秒钟前500ms已经达到100&#xff0c;后面的5…

华为云计算HCIA学习笔记-第1章 云计算基础概念

1.1 云计算课程安排说明 &#xff08;IA-虚拟化-FC / IP-Linux OpenStack 桌面云/IE-备份容灾迁移&#xff09; 1.2 为什么云计算IA讲虚拟化&#xff1f; 提前告知学员&#xff0c;为什么IA课程要重点讲解虚拟化&#xff1f;云计算基于OpenStack&#xff0c;其底层虚拟化技术…

赞!中原银行|古风金融产品体验运营数字驾驶舱大屏!

本期「V友故事」&#xff1a;中原银行设计师作品——古风金融产品体验运营大屏 金融行业数字化转型是近年来提出的一个关键词&#xff0c;它不仅在改变金融服务的结构、形式和方式&#xff0c;而且也在影响中国金融行业的扩张规模&#xff0c;基于此行业趋势&#xff0c;金融从…

加密图像的脆弱水印及应用

原文题目&#xff1a;《A self-embedding secure fragile watermarking scheme with high quality recovery》 学习笔记&#xff1a; 应用场景 为了确保图像在传输过程中不被损坏&#xff0c;在将原始图像发送到云端之前&#xff0c;将用于篡改检测和恢复的水印嵌入到原始图像…