【C++】哈希与布隆过滤器

news2024/12/26 11:07:10

🌇个人主页:平凡的小苏
📚学习格言:命运给你一个低的起点,是想看你精彩的翻盘,而不是让你自甘堕落,脚下的路虽然难走,但我还能走,比起向阳而生,我更想尝试逆风翻盘
🛸C++专栏C++内功修炼基地
> 家人们更新不易,你们的👍点赞👍和⭐关注⭐真的对我真重要,各位路 过的友友麻烦多多点赞关注。 欢迎你们的私信提问,感谢你们的转发! 关注我,关注我,关注我,你们将会看到更多的优质内容!!

在这里插入图片描述

一、位图的概念

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

1、位图的面试题

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

  • 数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。

在这里插入图片描述

2、位图模拟实现

#include <iostream>
#include <vector>
using std::cout;
using std::endl;
namespace sqy
{
	template<size_t N>
	class bitset
	{
	public:
		bitset()
		{
			_bits.resize(N / 8 + 1);
		}
		void set(size_t n)
		{
			size_t i = n / 8;//算出n属于_bits的哪一个下标
			size_t j = n % 8;//算出属于_bits下标的哪一个比特位
			_bits[i] |= 1 << j;
		} 

		void reset(size_t n)
		{
			size_t i = n / 8;//算出n属于_bits的哪一个下标
			size_t j = n % 8;//算出属于_bits下标的哪一个比特位
			_bits[i] &= (~(1 << j));
		}

		bool test(size_t n)
		{
			size_t i = n / 8;//算出n属于_bits的哪一个下标
			size_t j = n % 8;//算出属于_bits下标的哪一个比特位
			return _bits[i] & (1 << j);
		}
	private:
		std::vector<char> _bits;
	};

3、位图的应用

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

我们可以使用两个位图,如果是00则表示没有出现一次,01表示出现一次,10往后的就可以不用实现了。

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

使用两个位图,将两个文件的数据分别映射到对应的位图中,在进行遍历按位与,如果为1,则为交集,否则,反之。

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

我们可以使用两个位图,如果是00则表示没有出现一次,01表示出现一次,10表示出现两次,11往后的就可以不用实现了。

代码示例

template<size_t N>
	class twobitset
	{
	public:
		void set(size_t n)
		{
			if (b1.test(n) == false && b2.test(n) == false) //00 -> 01
			{
				b1.set(n);
			}
			else if (b1.test(n) == true && b2.test(n) == false) //01 -> 10
			{
				b1.reset(n);
				b2.set(n);
			}
		}

		void printOnce()
		{
			for (size_t i = 0; i < N; i++)
			{
				if (!b2.test(i) && b1.test(i))
				{
					cout << i << " ";
				}
			}
			cout << endl;
		}
	private:
		bitset<N> b1;
		bitset<N> b2;
	};

4、位图的优缺点

优点:节省空间,查找速度快

缺点:要求范围相对集中,范围特别分散的,空间消耗大;

位图只对整型使用,浮点数、string等等其他类型无法使用。

如果要判断其他类型,该类型如果可以使用哈希函数转换为整型的,可以考虑布隆过滤器

二、布隆过滤器

1、布隆过滤器的概念

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

在这里插入图片描述

我们为了降低误判的概率,解决方法就是对同一个元素使用多组哈希函数进行映射,它能降低误判率,但增加了空间消耗,使用时需要把控号布隆过滤器的哈希函数的个数和布隆过滤器的长度

2、布隆过滤器的使用场景

1、不需要一定准确的场景,例如个人网站注册的时候昵称判重,使用布隆过滤器可以判断某个昵称一定没有被使用过,但会误判某些造成冲突但没有被使用的昵称

2、提高效率。例如客户端查找信息时,先用布隆过滤器筛选一下,如果不在,则直接将未查到的信息反馈给客户端;如果布隆过滤器发现查找信息与位图匹配,则将需要查找的信息推送给服务器中的数据库进行精确查找
在这里插入图片描述

3、布隆过滤器的删除

单纯的布隆过滤器时不支持删除的,因为一个比特位可能被多个元素所映射。如果非要在布隆过滤器中实现删除,那就只能将位图结构修改未计数器结构。数据set时,每被映射一次,计数器加1,reset时,该位计数器-1,直到该位计数器为0.但是这种操作所需的空间消耗急剧增加。

4、布隆过滤器的优缺点

优点

  1. 增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关

  2. 哈希函数相互之间没有关系,方便硬件并行运算

  3. 布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势

  4. 在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势

  5. 数据量很大时,布隆过滤器可以表示全集,其他数据结构不能

  6. 使用同一组散列函数的布隆过滤器可以进行交、并、差运算

缺点

  1. 有误判率,即存在假阳性(False Position),即不能准确判断元素是否在集合中

    (补救方法:再建立一个白名单,存储可能会误判的数据)

  2. 不能获取元素本身

  3. 一般情况下不能从布隆过滤器中删除元素

  4. 如果采用计数方式删除,可能会存在计数回绕问题

5、布隆过滤器面试题

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

我们可以将它分成100个小文件,使用哈希算法,将ip转换成整型,将它取模, i=HashFunc(ip)%100,i是多少,ip就进入几号小文件。

如果冲突的桶超过1G怎么办?

1、这个桶冲突的IP很多,大多都是不重复的,map统计不下

2、这个桶冲突的IP很多,大多都是重复的,map可以统计

​ 直接使用map中的insert将每一个冲突桶的元素插入到map中。情况一:

如果insert失败,说明空间不足,new节点失败,抛出异常。解决方法是:

换个哈希函数,递归再次对这个桶进行切分。情况二:map可以正常统计

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

精确算法:使用哈希切分,将两个大文件分别切成一个个小文件A0-A99,B0-B99(蛋哥小文件超过1G换一个哈希函数);因为使用的是同一个哈希函数,所以交集必定存在于A0和B0,A1和B1这种相同下标的小文件中。可以先将A0存放至哈希表中,B0去重后与哈希表比对,就能够得到精确交集

6、布隆过滤器的实现

#include <iostream>
#include <vector>
#include <bitset>
#include <string>
using namespace std;

struct BKDRHash
{
	size_t operator()(const string& s)
	{
		// BKDR
		size_t value = 0;
		for (auto ch : s)
		{
			value *= 31;
			value += ch;
		}
		return value;
	}
};
struct APHash
{
	size_t operator()(const string& s)
	{
		size_t hash = 0;
		for (long i = 0; i < s.size(); i++)
		{
			if ((i & 1) == 0)
			{
				hash ^= ((hash << 7) ^ s[i] ^ (hash >> 3));
			}
			else
			{
				hash ^= (~((hash << 11) ^ s[i] ^ (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;
	}
};

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 len = X * N;
		size_t index1 = HashFunc1()(key) % len;
		size_t index2 = HashFunc2()(key) % len;
		size_t index3 = HashFunc3()(key) % len;
		/* cout << index1 << endl;
		cout << index2 << endl;
		cout << index3 << endl<<endl;*/
		_bs.set(index1);
		_bs.set(index2);
		_bs.set(index3);
	}
	bool test(const K& key)
	{
		size_t len = X * N;
		size_t index1 = HashFunc1()(key) % len;
		if (_bs.test(index1) == false)
			return false;
		size_t index2 = HashFunc2()(key) % len;
		if (_bs.test(index2) == false)
			return false;
		size_t index3 = HashFunc3()(key) % len;
		if (_bs.test(index3) == false)
			return false;
		return true;//存在误判的
	}
	// 不支持删除,删除可能会影响其他值。
	void reset(const K& key);
private:
	bitset<X* N> _bs;
};

void test_bloomfilter1()
{
	srand((unsigned int)time(0));
	const size_t N = 100000;
	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);
	}

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

}

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

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

相关文章

二叉树题目:二叉树的所有路径

文章目录 题目标题和出处难度题目描述要求示例数据范围 解法思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;二叉树的所有路径 出处&#xff1a;257. 二叉树的所有路径 难度 4 级 题目描述 要求 给你一个二叉树的根结点 root \texttt{root} root&#xff…

Go 并发编程

并发编程 1.1 并发与并⾏ 并⾏与并发是两个不同的概念&#xff0c;普通解释&#xff1a; 并发&#xff1a;交替做不同事情的能⼒并⾏&#xff1a;同时做不同事情的能⼒ 如果站在程序员的⻆度去解释是这样的&#xff1a; 并发&#xff1a;不同的代码块交替执⾏并⾏&#xf…

慢 SQL 的致胜法宝

大促备战&#xff0c;最大的隐患项之一就是慢SQL&#xff0c;对于服务平稳运行带来的破坏性最大&#xff0c;也是日常工作中经常带来整个应用抖动的最大隐患&#xff0c;在日常开发中如何避免出现慢SQL&#xff0c;出现了慢SQL应该按照什么思路去解决是我们必须要知道的。本文主…

C++对象模型(5)-- 数据语义学:继承的对象布局(不含虚函数)

1、单继承的对象布局 (1) 在普通继承&#xff08;没有虚函数、没有继承虚基类&#xff09;的情况下&#xff0c;按父对象、子对象的顺序布局 我们来看下面的例子&#xff1a; class Base { protected:int x;int y; };class Derive : public Base { private:int z; };int mai…

vue2项目中使用element ui组件库的table,制作表格,改表格的背景颜色为透明的

el-table背景颜色变成透明_el-table背景透明_讲礼貌的博客-CSDN博客 之前是白色的&#xff0c;现在变透明了&#xff0c;背景颜色是蓝色

短视频矩阵系统源码--源头技术独立自研框架开发

目录 一、批量剪辑&#xff08;采用php语言&#xff0c;数学建模&#xff09; 短视频合成批量剪辑的算法主要有以下几种&#xff1a; 1. 帧间插值算法&#xff1a;通过对多个视频的帧进行插帧处理&#xff0c;从而合成一段平滑的短视频。 2. 特征提取算法&#xff1a;提取多…

RedissonClient中Stream流的简单使用

1、pub端 //获取一个流 RStream rStream redissonClient.getStream("testStream"); //创建一个map&#xff0c;添加数据 Map<String, Object> rr new HashMap<>(); rr.put("xx", RandomUtil.randomString(5)); //添加到流 rStream.addAll(r…

TypeScript 笔记:String 字符串

1 对象属性 length 返回字符串的长度 2 对象方法 charAt() 返回在指定位置的字符 charCodeAt() 返回在指定的位置的字符的 Unicode 编码 concat 连接两个或更多的字符串 indexOf 返回某个指定的字符串值在字符串中首次出现的位置 lastIndexOf 从后向前搜索字符串&…

c语言练习题83:#include“ “和#include<>的区别

#include" "和#include<>的区别 #include<> 默认根据环境变量的值去先搜索标准库&#xff0c;搜索系统文件会比较快。 #include“” 先搜索当前工程的路径&#xff0c;搜索自己自定义的文件会比较快。 因此自定义的头文件的名称包含在<>中的话…

LeetCode 24.两两交换链表中的结点

题目链接 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 题目解析 首先可以特判一下&#xff0c;如果结点数目小于等于1&#xff0c;则直接返回即可&#xff0c;因为数目小于等于1就不需要交换了。 然后我们可以创建一个虚拟的头结点&#xff0c;然…

SpringCloud源码探析(十)-Web消息推送

1.概述 消息推送在日常使用中的场景比较多&#xff0c;比如有人点赞了我的博客或者关注了我&#xff0c;这时我就会收到一条推送消息&#xff0c;以此来吸引我点击或者打开应用。消息推送的方式主要分为两种&#xff1a;web消息推送和移动端消息推送。它将所要发送的信息&…

【物联网】Arduino+ESP8266物联网开发(一):开发环境搭建 安装Arduino和驱动

ESP8266物联网开发 1.开发环境安装 开发软件下载地址&#xff1a; 链接: https://pan.baidu.com/s/1BaOY7kWTvh4Obobj64OHyA?pwd3qv8 提取码: 3qv8 1.1 安装驱动 将ESP8266连接到电脑上&#xff0c;安装ESP8266驱动CP210x 安装成功后&#xff0c;打开设备管理器&#xff0c…

抖音seo源代码开源部署----基于开放平台SaaS服务

抖音SEO搜索是什么&#xff1f; 抖音SEO搜索是指在抖音平台上进行搜索引擎优化&#xff08;Search Engine Optimization&#xff09;的一种技术手段。 通过优化抖音账号、发布内容和关键词等&#xff0c;提高抖音视频在搜索结果中的排名&#xff0c;从而增加视频曝光量和用户点…

什么是全流程的UI设计?它与单页面的视觉设计有什么区别?

在软件产品研发流程中&#xff0c;产品交互设计一般是根据项目需求&#xff0c;做出设计方案&#xff0c;以求解决某个问题。而全流程的设计不再局限于短暂或者单个页面的视觉优化&#xff0c;而是追求持续性地参与&#xff0c;以全局性整体性地提升产品体验。 在软件内的信息传…

mybatis配置entity下不同文件夹同类型名称的多个类型时启动springboot项目出现TypeException源码分析

记录问题&#xff1a;当配置了 mybatis.type-aliases-packagecom.runjing.erp.entity 配置项时&#xff0c;如果entity文件夹下存在不同子文件夹下的同名类型时&#xff0c;mybatis初始化加载映射时会爆出org.apache.ibatis.type.TypeException&#xff1a; The alias TestDemo…

怎么将自己拍摄的视频静音?详细步骤教会你~

大部分人都会遇到的一个问题&#xff0c;我们在拍摄视频时容易将嘈杂的背景音或环境音录进去&#xff0c;怎样解决这个问题呢&#xff1f;今天就来教大家具体操作步骤&#xff0c;只需用到这个软件即可&#xff01; 第一步&#xff1a;打开我们的【音分轨】APP&#xff0c;进入…

山西电力市场日前价格预测【2023-10-10】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2023-10-10&#xff09;山西电力市场全天平均日前电价为555.24元/MWh。其中&#xff0c;最高日前电价为1130.92元/MWh&#xff0c;预计出现在18: 30。最低日前电价为293.94元/MWh&#xff0c;预…

网络流量安全分析-工作组异常

在网络中&#xff0c;工作组异常分析具有重要意义。以下是网络中工作组异常分析的几个关键点&#xff1a; 检测网络攻击&#xff1a;网络中的工作组异常可能是由恶意活动引起的&#xff0c;如网络攻击、病毒感染、黑客入侵等。通过对工作组异常的监控和分析&#xff0c;可以快…

【SHUD】SHUD模型Windows下的编译过程

目录 〇、绪论一、SHUD水文模型说明二、SHUD编译1、SUNIALS库的编译1.1. 使用MSVC1.1.1 CMAKE 1.2. 使用MINGW1.2.1 CMAKE1.2.2 生成库文件 2、SHUD的编译过程2.1. LINUX和MAC环境2.2. WINDOWS环境2.2.1 shud2.2.2 shud_omp 〇、绪论 科学模型依据有限时空内的观测&#xff0c…

华为OD机试 - 数字反转打印(Java 2023 B卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#…