C++ 位图布隆过滤器哈希切割

文章目录

  • 位图
    • 概念
    • 模拟实现
    • 海量数据面试题1
  • 布隆过滤器
    • 模拟实现
    • 应用场景
    • 海量数据面试题2
  • 哈希切割
    • 海量数据面试题3

位图

概念

我们用一道题引出此概念:

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

分析:40亿无符号整数占多大空间?16G —— 如果将这些整形数据尽数导入内存中再用诸如遍历、排序后二分查找等方式处理,空间上多少会吃不消
既然想节省空间,又只是判断数据是否存在,我们可以考虑借助更小的数据单位 位(bit) 去表示存在与否(1/0):

在这里插入图片描述

所谓位图,就是这样用每一位来存放某种状态的结构。适用于海量数据,数据无重复的场景

模拟实现

//位图
template<size_t N>//非类型模板参数,用于指示位图范围
class bitset {
public:
	bitset() {
		_bits.resize(N / 8 + 1, 0);
	}
	//插入
	void set(size_t data) {
		int i = data / 8;//是第几个char
		int j = data % 8;//是char里的第几个数据
		_bits[i] |= (1 << j);
	}
	//删除
	void reset(size_t data) {
		int i = data / 8;
		int j = data % 8;
		_bits[i] &= ~(1 << j);
	}
	bool test(size_t data) {
		int i = data / 8;
		int j = data % 8;
		return _bits[i] & (1 << j);
	}
private:
	vector<char> _bits;//存储char,使用其中的每个bit
};

海量数据面试题1

  1. 100亿个整数,找出其中只出现一次的整数?

以上我们用 1/0 表示存在与否,稍作延伸我们可以用 10 / 01 / 00 表示出现 2 / 1 / 0 次

//复用位图解决问题
template<size_t N>
class twobitset
{
public:
	void set(size_t x){
		// 00 -> 01
		if (_bs1.test(x) == false
		&& _bs2.test(x) == false){
			_bs2.set(x);
		}
		// 01 -> 10
		else if (_bs1.test(x) == false
			&& _bs2.test(x) == true){
			_bs1.set(x);
			_bs2.reset(x);
		}
		// 10
	}

	void Print(){
		for (size_t i = 0; i < N; ++i){
			if (_bs2.test(i)){
				cout << i << endl;
			}
		}
	}
public:
	bitset<N> _bs1;
	bitset<N> _bs2;
};
  1. 两个文件分别有100亿个整数,如何在只有1G内存的情况下求得它们的交集

思路1:先将一个文件中的数据导入位图中,然后看第二个文件,每次找到交集数据时将位图中相应位置reset,防止输出重复数据
思路2:分别用两个位图存储两文件中的数据,从 0 遍历到 -1(对于无符号整形而言就是相应的最大值),看两个位图中是否都有相应数据

布隆过滤器

大量的数据如果用哈希表存储,会很浪费空间,于是我们用位图处理,但位图一般只能处理整形,面对字符串无能为力,但如果运用哈希的思想的话……

于是将哈希(将数据转换为整形并计算映射位置)与位图(用位1 / 0去表示数据的存在与否)结合,即布隆过滤器

因为只是用位表示数据的存在与否而非记录数据,所以如果遇上哈希冲突的话是没办法处理的,于是只能想办法减少哈希冲突:让一个数据映射到多个位置

示例:用不同的哈希函数将数据映射到不同的位置(感谢Yang Chen大佬的图(?))
在这里插入图片描述
可以看出,这种做法使得布隆过滤器在查找数据时,如果 返回结果为该数据存在,则不一定真的存在,但若返回不存在,则一定不存在

模拟实现

//让一个数据映射三个位置(需要三个哈希/散列函数)
//暂时不用太在意这三个仿函数用的是什么逻辑
struct BKDRHash
{
	size_t operator()(const string& s)
	{
		size_t hash = 0;
		for (auto ch : s)
		{
			hash += ch;
			hash *= 31;
		}

		return hash;
	}
};
struct APHash
{
	size_t operator()(const string& s)
	{
		size_t hash = 0;
		for (long i = 0; i < s.size(); i++)
		{
			size_t ch = s[i];
			if ((i & 1) == 0)
			{
				hash ^= ((hash << 7) ^ ch ^ (hash >> 3));
			}
			else
			{
				hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));
			}
		}
		return hash;
	}
};
struct DJBHash
{
	size_t operator()(const string& s)
	{
		size_t hash = 5381;
		for (auto ch : s)
		{
			hash += (hash << 5) + ch;
		}
		return hash;
	}
};

// N最多会插入key数据的个数
template<size_t N,
	class K = string,	//默认支持string
	class Hash1 = BKDRHash,
	class Hash2 = APHash,
	class Hash3 = DJBHash>
class BloomFilter
{
public:
	//插入
	void set(const K& key)
	{
		size_t len = N * _X;
		size_t hash1 = Hash1()(key) % len;
		_bs.set(hash1);

		size_t hash2 = Hash2()(key) % len;
		_bs.set(hash2);

		size_t hash3 = Hash3()(key) % len;
		_bs.set(hash3);
	}
	//查找
	bool test(const K& key)
	{
		size_t len = N * _X;

		size_t hash1 = Hash1()(key) % len;
		if (!_bs.test(hash1))
		{
			return false;
		}

		size_t hash2 = Hash2()(key) % len;
		if (!_bs.test(hash2))
		{
			return false;
		}

		size_t hash3 = Hash3()(key) % len;
		if (!_bs.test(hash3))
		{
			return false;
		}
		// 在      不准确的,存在误判
		// 不在    准确的

		return true;
	}
private:
	static const size_t _X = 6;//为减少哈希冲突,提前确定每有一个数据,就多_X个位(经后面的测试发现6的效果不错)
	bitset<N* _X> _bs;		   
};

测试误判率:

//老师用于测试布隆过滤器的示例:
void test_bloomfilter()
{
	srand(time(0));
	const size_t N = 10000;
	BloomFilter<N> bf;

	std::vector<std::string> v1;
	std::string url = "https://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html";//随便找一个网址

	for (size_t i = 0; i < N; ++i)
	{
		v1.push_back(url + std::to_string(i));//在网址字符串后加不同的东西使得插入的数据不同
	}

	for (auto& str : v1)
	{
		bf.set(str);
	}

	// v2跟v1是相似字符串集,但是不一样
	std::vector<std::string> v2;
	for (size_t i = 0; i < N; ++i)
	{
		std::string url = "https://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html";
		url += std::to_string(999999 + i);
		v2.push_back(url);//可以看出,v2存储的数据前面网址部分的字符串和v1存储的数据都是一样的(相似字符串)
	}

	size_t n2 = 0;
	for (auto& str : v2)
	{
		if (bf.test(str))
		{
			++n2;
		}
	}
	cout << "相似字符串误判率:" << (double)n2 / (double)N << endl;

	// 不相似字符串集
	std::vector<std::string> v3;
	for (size_t i = 0; i < N; ++i)
	{
		string url = "zhihu.com";
		//string url = "https://www.cctalk.com/m/statistics/live/16845432622875";
		url += std::to_string(i + rand());
		v3.push_back(url);//可以看出,v3存储的数据和v1一点都不相似
	}

	size_t n3 = 0;
	for (auto& str : v3)
	{
		if (bf.test(str))
		{
			++n3;
		}
	}
	cout << "不相似字符串误判率:" << (double)n3 / (double)N << endl;
}

应用场景

返回结果都不准确了,能用在什么地方上呢?

例一:判断昵称是否被占用
显然,只是设置昵称显示被占用,这种事情即便偶尔判断出错,也没什么影响

例二:辅助加快判断速度
如果我就是要求昵称判断是否被占用100%准确呢?
像是昵称、密码之类的用户数据一般都被存储在公司的数据库中(硬盘),直接访问速度比内存要慢很多,我们就可以借助布隆过滤器快速解决部分数据:
在这里插入图片描述

海量数据面试题2

● 给两个文件,分别有100亿个query(当做字符串),我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法
近似算法:将文件1中的query映射到一个布隆过滤器,读取文件2 的query,判断在不在布隆过滤器中,在就是交集

老师是这么说的……但100亿个数据,全塞进布隆过滤器的话……如果以我们上面写的为例,那就会占用约 50G 的内存( ???)

精确算法:哈希切割

哈希切割

海量数据面试题3

● 给两个文件,分别有100亿个query(当做字符串),我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法
在这里插入图片描述
● 给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址?如何找到top K的IP?

思路:用哈希分割切成500个小文件,使用map / unordered_map统计每个小文件中的 ip 出现的次数,并记录每个小文件各自出现最多的 ip(记得每处理完一个小文件后 clear 释放空间)
若统计过程中抛异常,说明此小文件占用内存过大或冲突太多,需要继续切分此小文件
……………………

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

算法---回溯(正文)

1.什么是回溯&#xff1f; 回溯算法的定义就是和暴力枚举一样枚举所有可能并加撤回&#xff0c;也能和暴力一样去掉一些重复&#xff08;在之前就被筛出&#xff0c;但还要枚举这个&#xff0c;我们可以跳过这个了---------这个就是回溯剪枝&#xff09;。但为什么回溯不是暴力…

多线程基础详解(看到就是赚到)

&#x1f3a5; 个人主页&#xff1a;Dikz12&#x1f4d5;格言&#xff1a;那些在暗处执拗生长的花&#xff0c;终有一日会馥郁传香欢迎大家&#x1f44d;点赞✍评论⭐收藏 目录 创建线程 1.创建类继承Thread,重写run() 2.实现Runnable,重写run() 3.继承Thread,使用匿名内部类 …

版本控制工具——Git

版本控制工具——Git 前言一、版本库二、git的工作区域和文件状态三、添加和提交文件四、回退版本&#xff1a;git reset --模式 版本号五、查看差异&#xff1a;git diff六、从版本库中删除文件七、.gitignore&#xff1a;git中的特殊文件八、Git、GitHub跟Sourcetree的关系九…

基于 Python 的漏洞扫描系统,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

【Python】使用 requirements.txt 与 pytorch 相关配置

【Python】使用 requirements.txt 与 pytorch 相关配置 前言一、pip1、导出结果含有路径2、导出不带路径的 二、Conda1、导出requirements.txt2、导出yml 文件 三、第三方包&#xff1a;pipreqs&#xff08;推荐&#xff09;1、创建并激活conda环境2、安装requirements文件的pi…

金融信贷风控评分卡模型

评分卡模型概念 评分模型是根据借款人的历史数据&#xff0c;选取不同维度的数据类型&#xff0c;通过计算而得出的对借款人信用情况打分的模型。不同等级的信用分数代表了借款人信用情况的好坏&#xff0c;以此来分析借款人按时还款的可能性。 评分卡模型分类 A卡&#xff…

揭开Markdown的秘籍:标题|文字样式|列表

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;Markdown指南、网络奇遇记 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. ⛳️Markdown 标题二. ⛳️Markdown 文字样式2.1 &#x1f514;斜体2.2 &…

Netty连接通道中的Channel参数模型

ChannelOption(Channel中的连接参数) ChannelOption.SOBACKLOG ChannelOption.SO_BACKLOG对应的是tcp/ip协议listen函数中的backlog参数&#xff0c;服务端处理客户端连接请求是顺序处理的&#xff0c;所以同一时间只能处理一个客户端连接&#xff0c;多个客户端来的时候&…

代理与Reflect反射

属性描述符 Proprety Descriptor 属性描述符 用于描述一个属性的相关信息 1.Object.getOwnPropertyDescriptor(对象&#xff0c;属性名) 可以得到一个对象的 某个属性的属性描述符 Object.getOwnPropertyDescriptors(对象) 可以得到某个对象的所有属性描述符 如果需要为某个…

node.js基础-02

Author nodes&#xff1a;&#xff08;题记&#xff09; Hypertest Transfer protocol is very important to programming personnel。it doesnt matter if youre a front-end engineer or a back-end engineer.So,lets study it together. http协议对于编程工程师很重要&am…

基于华为云欧拉操作系统(HCE OS)单节点容器化部署(Prometheus、node-exporter、Grafana)应用性能监控平台

写在前面 博文内容为 华为云欧拉操作系统入门级开发者认证(HCCDA – Huawei Cloud EulerOS)实验笔记整理认证地址&#xff1a;https://edu.huaweicloud.com/certificationindex/developer/9bf91efb086a448ab4331a2f53a4d3a1内容涉及&#xff0c;HCE OS 容器化部署(Prometheus、…

【C++修行之道】(引用、函数提高)

目录 一、引用 1.1引用的基本使用 1.2 引用注意事项 1.3 引用做函数参数 1.4 引用做函数返回值 1.5 引用的本质 1.6 常量引用 1.7引用和指针的区别 二、函数提高 2.1 函数默认参数 2.2函数占位参数 2.3 函数重载 2.4函数重载注意事项 一、引用 1.1引用的基本使用 …

【canvas】获取鼠标点击位置坐标的颜色信息

在项目当中&#xff0c;要实现某业务需求例如PS魔棒功能时&#xff0c;则需要获取点击坐标的颜色信息。 功能不复杂&#xff0c;代码也很少&#xff0c;一看便知~~ 核心API为getImageData&#xff0c;传入4个参数&#xff0c;前2个为点击坐标xy&#xff0c;后2个都传1&#xf…

那些 C语言指针 你不知道的小秘密 (3)

本篇会加入个人的所谓‘鱼式疯言’ ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 我会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人能…

vscode连接ssh报错

关于vscode更新版本至1.86后&#xff0c;导致无法连接服务器问题的记录 原因&#xff1a;vscode1.86更新了对glibc的要求&#xff0c;需要最低2.28版本&#xff0c;导致各种旧版本的linux发行版&#xff08;比如最常见的centos 7&#xff09;都无法用remote-ssh来连接了&#…

Linux探秘之旅:透彻理解路径、命令与系统概念

目录 如何远程连接 远程登录简明指南 linux区别 1.严格区分大小写 2.linux的命令返回结果判断 3.如何查看网络信息 4.关于后缀名&#xff08;Linux不关心文件后缀&#xff09; 4.1 需要记忆的后缀 5.echo命令 6.linux一切皆文件 6.1比如磁盘的文件 6.2可执行文件 …

nginx + DNS域名解析

配置链接: Nginx 安装配置 | 菜鸟教程 安装完nginx后&#xff0c;访问&#xff1a; cd /usr/local/nginx/sbin/ 然后使用./nginx可使用nginx。 访问:http://服务器的ip地址后出现 因为访问IP地址很繁琐&#xff0c;需要记忆ip的数字地址&#xff0c;因此需要给它一个通俗的…

如何在Sprint中管理UI测试?

作为iOS团队&#xff0c;我们编写3种类型的UI测试。如果你问这些是什么&#xff1b;快照、冒烟和回归测试。那么这些测试到底是什么&#xff1f;让我们稍微谈谈这些。 快照测试快照测试是检查UI中的某些内容是否损坏的测试。 首先&#xff0c;它将所需的视图图像保存在某处&am…

Lombok 高级说明

优质博文&#xff1a;IT-BLOG-CN 一、痛点 【1】代码臃肿&#xff1a;POJO中的getter/setter/equals/hashcode/toString等&#xff1b; 【2】样板式代码&#xff1a;I/O流的关闭操作等&#xff1b; Lombok是一个可以通过注解简化Java代码开发的工具&#xff0c;能够在我们编…

string容器

1. string基本概念 1.1 本质&#xff1a; string是C风格的字符串&#xff0c;而string本质上是一个类 string和char * 区别&#xff1a; char * 是一个指针 string是一个类&#xff0c;类内部封装了char*&#xff0c;管理这个字符串&#xff0c;是一个char*型的容器。 1.2 特点…
最新文章