C++|哈希应用->布隆过滤器

目录

一、概念

二、模拟实现

三、布隆过滤器扩展应用


 

上一篇章学习了位图的使用,但它只适用于整数,对于要查询字符串是否在不在,位图并不能解决。所以针对这一问题,布隆过滤器可以派上用场,至于布隆过滤器是什么,其实并没有什么神奇的,就是在位图上套了哈希函数罢了,这两者组合起来就是布隆过滤器,而字符串就可以通过哈希函数转换成整数映射到位图当中去。 

一、概念

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

原理分析: 

我们来进行分析,为什么不存在是一定的,而存在是可能的,以及为什么要这样做。

首先来解释为什么要用多个哈希函数。

我们知道,字符串可以通过哈希函数转换成整数,但是哈希冲突是避免不了的,可能存在多个字符串通过哈希函数都得到了一样的整数,所以,为了尽量的减少哈希冲突,可以使用多个哈希函数,让字符串通过多个哈希函数得到多个映射位置,只要不是多个映射位置都相同,就不会冲突,这样大大提高了效率。至于要用几个哈希函数是适合的。

这里有一份研究:(转载详解布隆过滤器的原理,使用场景和注意事项 - 知乎 (zhihu.com))

其中误报率就是哈希冲突率 

其中k、m、n满足:

 其中k、m、p满足:

我们可以发现,哈希函数用的越多,哈希冲突率就越低,但是哈希函数到3之后,误报率已经很低了,其次,当哈希函数、插入元素固定,所开空间越大,误报率也越低。

用一张图来表示通过哈希函数映射到位图中:

那么综上,即使采用了多个哈希函数,也依然可能会存在哈希冲突,所以在判断东西在不在时,若返回的是存在,这有可能是误判,说明映射的位置依然可能完全相同,而不存在时,说明映射的位置不完全相同,这是正确的结果,为了确保冲突率,我们在模拟实现的时候就采用3个哈希函数。

二、模拟实现

#include "MyBitSet.h"//在上一篇章已实现
struct BKDRHash
{
	size_t operator()(const string& key)
	{
		size_t hash = 0;
		for (auto e : key)
		{
			//BKDR
			hash *= 31;
			hash += e;
		}
		return hash;
	}
};


struct APHash
{
	size_t operator()(const string& key)
	{
		size_t hash = 0;
		for (size_t i = 0; i < key.size(); i++)
		{
			if ((i & 1) == 0)
			{
				hash ^= ((hash << 7) ^ key[i] ^ (hash >> 3));
			}
			else
			{
				hash ^= (~((hash << 11) ^ key[i] ^ (hash >> 5)));
			}
		}
		return hash;
	}
};
struct DJHash
{
	size_t operator()(const string& key)
	{
		
		register size_t hash = 5381;
		for(auto e : key)
		{
			hash += (hash << 5) + e;
		}
		return hash;
	}
};
namespace bit
{
	template<size_t N, 
		class K = string, //默认输入的是字符串
		class HashFunc1 = BKDRHash,
		class HashFunc2 = APHash,
		class HashFunc3 = DJHash>
	class BloomFilter
	{
	public:
		void set(const K& key)
		{
            //获取三个映射位置
			int hash1 = HashFunc1()(key) % N;
			int hash2 = HashFunc2()(key) % N;
			int hash3 = HashFunc3()(key) % N;

			_blf.set(hash1);
			_blf.set(hash2);
			_blf.set(hash3);

		}

		bool test(const K& key)
		{
			//key不存在是准确的。
			int hash1 = HashFunc1()(key) % N;
			if (_blf.test(hash1) == false)
				return false;

			int hash2 = HashFunc2()(key) % N;
			if (_blf.test(hash2) == false)
				return false;

			int hash3 = HashFunc3()(key) % N;
			if (_blf.test(hash3) == false)
				return false;

			//key存在可能有误判
			return true;
		}
	private:
		bitset<N> _blf;
	};
}

void TestBF1()
{
	bit::BloomFilter<100> bf;
	bf.set("猪八戒");
	bf.set("沙悟净");
	bf.set("孙悟空");
	bf.set("二郎神");

	cout << bf.test("猪八戒") << endl;
	cout << bf.test("沙悟净") << endl;
	cout << bf.test("孙悟空") << endl;
	cout << bf.test("二郎神") << endl;
	cout << bf.test("二郎神1") << endl;
	cout << bf.test("二郎神2") << endl;
	cout << bf.test("二郎神 ") << endl;
	cout << bf.test("太白晶星") << endl;
}

void TestBF2()
{
	srand(time(0));
	const size_t N = 100000;
	bit::BloomFilter<N * 10> bf;

	std::vector<std::string> v1;
	//std::string url = "https://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html";
	std::string url = "猪八戒";

	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 urlstr = url;
		urlstr += std::to_string(9999999 + i);
		v2.push_back(urlstr);
	}

	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 = "孙悟空";
		url += std::to_string(i + rand());
		v3.push_back(url);
	}

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

测试:

#include <string>
#include "MyBloomFilter.h"


int main()
{

	TestBF2();

	return 0;
}

 输出结果:

三、布隆过滤器扩展应用

1.给两个文件,分别由100亿个字符串,只有1G内存,如何找到两个文件交集?

假设每个字符串占50个字节,那么100亿就是5000字节,约等于500G,内存肯定存不下,此时可以采用哈希切分。如图:

 

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

与第一题类似,先进行哈希切分,然后通过map统计每个小文件中IP地址出现的次数进行比较即可。 

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

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

相关文章

G2.【C语言】EasyX绘制颜色窗口

1.窗口 窗口&#xff1a;宽度*高度&#xff08;单位都是像素&#xff09; #include <stdio.h> #include <easyx.h> int main() {initgraph(640, 480);getchar();return 0; } 640是宽&#xff0c;480是高 2.操作窗口的三个按钮 #include <stdio.h> #incl…

推荐Bulk Image Downloader插件下载网页中图片链接很好用

推荐&#xff1a;Bulk Image Downloader chome浏览器插件下载图片链接&#xff0c;很好用。 有个网页&#xff0c;上面放了数千的gif的电路图&#xff0c;手工下载会累瘫了不可。想找一个工具分析它的静态链接并下载&#xff0c;找了很多推荐的下载工具&#xff0c;都是不能分…

Github:git提交代码到github

创建 GitHub 仓库 a. 登录到您的 GitHub 账户。 b. 点击右上角的 "" 图标&#xff0c;选择 "New repository"。 c. 填写仓库名称&#xff08;例如 "Mitemer"&#xff09;。 d. 添加项目描述&#xff08;可选&#xff09;。 e. 选择仓库为 &…

Feign-未完成

Feign Java中如何实现接口调用&#xff1f;即如何发起http请求 前三种方式比较麻烦&#xff0c;在发起请求前&#xff0c;需要将Java对象进行序列化转为json格式的数据&#xff0c;才能发送&#xff0c;然后进行响应时&#xff0c;还需要把json数据进行反序列化成java对象。 …

MySQL的count()方法慢

前言 mysql用count方法查全表数据&#xff0c;在不同的存储引擎里实现不同&#xff0c;myisam有专门字段记录全表的行数&#xff0c;直接读这个字段就好了。而innodb则需要一行行去算。 比如说&#xff0c;你有一张短信表(sms)&#xff0c;里面放了各种需要发送的短信信息。 …

C语言图书馆管理系统(管理员版)

案例&#xff1a;图书馆管理系统&#xff08;管理员版&#xff09; 背景&#xff1a; 随着信息技术的发展和普及&#xff0c;传统的图书馆管理方式已经无法满足现代图书馆高效、便捷、智能化的管理需求。传统的手工登记、纸质档案管理不仅耗时耗力&#xff0c;而且容易出现错…

拉普拉斯逆变换

https://www.bilibili.com/video/BV17i4y1475Y?p21&vd_source2e6b4ba548ec9462b2f9633ff700e9b9 CV 17 陈永平教授关于拉普拉斯逆变换的式子的推导 最关键的两步 想到取一个合适的contour L R L_R LR​部分是实部 γ \gamma γ要大于所有极点的实部,这样就可以搞一个大…

2.1 tmux和vim

文章目录 前言概述tmuxvim总结 前言 开始学习的时间是 2024.7.6 ,13&#xff1a;47 概述 最好多使用&#xff0c;练成条件反射式的 直接使用终端的工具&#xff0c;可以连接到服务器&#xff0c;不需要使用本地的软件 tmux 这个主要有两个功能&#xff0c;第一个功能是分…

uni-app组件 子组件onLoad、onReady事件无效

文章目录 导文解决方法 导文 突然发现在项目中&#xff0c;组件 子组件的onLoad、onReady事件无效 打印也出不来值 怎么处理呢&#xff1f; 解决方法 mounted() {console.log(onLoad, this.dateList);//有效// this.checkinDetails()},onReady() {console.log(onReady, this.da…

TensorCore 指令与汇编编程

TensorCore 指令是 NVIDIA 在其 GPU&#xff08;图形处理单元&#xff09;中引入的一种特殊硬件指令&#xff0c;用于加速深度学习计算&#xff0c;特别是矩阵乘法和卷积操作。TensorCore 指令专为处理深度学习的张量运算而设计&#xff0c;能够在单个时钟周期内执行大量的计算…

leetcode力扣_贪心思想

455.分发饼干&#xff08;easy-自己想得出来并写好&#xff09; 假设你是一位很棒的家长&#xff0c;想要给你的孩子们一些小饼干。但是&#xff0c;每个孩子最多只能给一块饼干。对每个孩子 i&#xff0c;都有一个胃口值 g[i]&#xff0c;这是能让孩子们满足胃口的饼干的最小尺…

机器学习——岭回归

1、岭回归与线性回归的区别 岭回归&#xff08;Ridge Regression&#xff09;和线性回归&#xff08;Linear Regression&#xff09;都是用于回归分析的统计方法&#xff0c;但它们在处理方式和应用场景上有一些关键的区别&#xff1a; a)基本概念 线性回归&#xff1a;目标是…

网易游戏员工怒怼丁磊上热搜:每天员工陪你演戏点赞有意思吗

【头部财经】近日&#xff0c;网易游戏一员工在内部群怒怼丁磊的聊天记录曝光&#xff0c;引发网友关注。据头部财经了解&#xff0c;该员工名叫石佳煊&#xff0c;是网易游戏的游戏开发工程师&#xff0c;毕业于华盛顿大学&#xff0c;已在网易工作四年多。 截图显示&#xf…

提高论文发表机会:Nature Communications 最新研究教你如何巧妙回复审稿意见

我是娜姐 迪娜学姐 &#xff0c;一个SCI医学期刊编辑&#xff0c;探索用AI工具提效论文写作和发表。 对于科研搬砖人来说&#xff0c;在论文投稿过程中&#xff0c;如何有效回复审稿意见才能得到审稿人的认可&#xff0c;一直是一个让人困惑又带点玄学的问题。 但是&#xff0c…

docker push 推送镜像到阿里云仓库

1.登陆阿里云 镜像服务&#xff0c;跟着指引操作就行 创建个人实例&#xff0c;创建命名空间、镜像仓库&#xff0c;绑定代码源头 2.将镜像推送到Registry $ docker login --username*** registry.cn-beijing.aliyuncs.com $ docker tag [ImageId] registry.cn-beijing.aliy…

白嫖A100-interLM大模型部署试用活动,亲测有效-2.Git

申明 以下部分内容来源于活动教学文档&#xff1a; Docs git 安装 是一个开源的分布式版本控制系统&#xff0c;被广泛用于软件协同开发。程序员的必备基础工具。 常用的 Git 操作 git init 初始化一个新的 Git 仓库&#xff0c;在当前目录创建一个 .git 隐藏文件夹来跟踪…

Linux 防火墙配置指南:firewalld 端口管理应用案例(二十个实列)

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f427;Linux基础知识(初学)&#xff1a;点击&#xff01; &#x1f427;&#x1f427;Linux高级管理专栏&#xff1a;点击&#xff01; &#x1f510;Linux中firewalld防火墙&#xff1a;点击&#xff01; ⏰️…

策略为王股票软件源代码-----如何修改为自己软件62----资讯菜单修改-----举例---------调用同花顺F10资讯------

//char szInfoF10[] "http://www.f10.com.cn/ggzx/ggzl.asp?zqdm%s"; char szInfoF10[] "http://basic.10jqka.com.cn/601899/"; // MENUITEM "F10资讯(&F)", ID_INFO_F10 MENUITEM &…

14-26 剑和侠客 – 预训练模型三部曲3 – 机器人时代来临

概述 在第 1 部分和第 2 部分中&#xff0c;我们讨论了适用于文本和图像任务的预训练模型&#xff0c;并探索了当今常用的模型。我们分析了这些模型的架构以及如何将它们用于特定任务。实现 AGI 所需的两个主要支柱是语言理解和机器的视觉能力。有许多任务与这两种能力有关。 …

Unity中使用VectorGraphics插件时,VectorUtils.RenderSpriteToTexture2D方法返回结果错误的解决方法

Unity中使用VectorGraphics插件时&#xff0c;如果使用VectorUtils.BuildSprite方法创建Sprite&#xff0c;那么得到的Sprite往往是一个三角网格数比较多的Sprite&#xff0c;如果想要得到使用贴图只有两个三角面的方形Sprite&#xff0c;可以使用该插件提供的VectorUtils.Rend…