【哈希】位图/布隆过滤器

news2024/11/26 10:26:24

位图

前言

在实现位图结构之前我们先看一个问题:

给出40亿个不重复的无符号整型,并且是无序的。然后给一个无符号整数,怎样快速判断这个数是否在40亿个数之中。

方法一:对40亿个数据进行遍历。我们会发现,时间复杂度为O(N),对于40亿的数据量来说时间复杂度太高了 。

方法二:排序(O(N*logN)),然后二分查找(O(logN))。现在相比于方法一时间复杂度进行了优化。但是,一个无符号整型需要4Byte,40亿个大概需要16G的内存,所以还是不能够解决问题。

下面我们对位图进行介绍,位图就能很好得解决内存不够的这个问题:

位图概念

所谓位图,就是用比特位来标识一个状态,适用于海量数据、且无重复的使用场景。通常用来判断某个数据是否存在。

位图结构

前面提到的问题中,一个无符号整数所需要的内存是4Byte,那么我们能不能用更小的空间来存储一个数字呢?答案肯定是能,我们可以用一个bit位来标识这个数存不存在,一个数所占空间为4Byte,每个位上都是0或者1,那么我们就能用这个bit位上的数字来标识一个数的状态:

位图实现

template<size_t N>
//N为位图中要存放数据的个数
class bitset
{
public:
	bitset()
	{
		_bit.resize(N / 8 + 1, 0);
		//为了防止存储数据个数小于8时开辟的空间为0,所以要加上1
	}

	//将x存放入位图中
	void set(size_t X)
	{
		size_t hashi = X / 8; // hashi为这个数要放在编号为第几个字节位
		size_t num = X % 8;   // num为这个数据需要存放在第hashi字节中的位数
		_bit[hashi] |= (1 << num);
	}

	//修改x在位图中的状态
	void reset(size_t X)
	{
		size_t hashi = X / 8;
		size_t num = X % 8;
		_bit[hashi] &= ~(1 << num);
	}
	
	//查询x是否在位图中
	bool test(size_t X)
	{
		size_t hashi = X / 8;
		size_t num = X % 8;
		return _bit[hashi] & (1 << num);
	}
private:
	vector<char> _bit;
    //注意:这里实现的位图结构中vector中存放的是char
};

 通过用例测试一下位图:

 位图优缺点

优点:速度快、节省空间。

缺点:只能映射整型。浮点型、string等类型不能存储映射。

布隆过滤器

前言

在我们使用小说app时,系统会根据我们的喜好来推送内容。但是,如何防止推送我们已经浏览过的小说呢?在推送内容的时候一定会去掉那些我们已经看过的小说。那么又是如何进行去重地呢?app推送内容时一定会根据我们的历史浏览记录来进行去重。然后过滤已经查看过的内容。问题来了,现在要推送一部小说,如何快速确定这部小说是否在历史浏览记录中呢?

1、用哈希存储用户记录   缺点:浪费空间

2、用位图存储用户记录   位图一般用来处理整型,但小说内容编号是字符串,位图就处理不了了。

3、将哈希与位图结合,即布隆过滤器来处理这个问题

布隆过滤器概念

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

布隆过滤器结构

比如现在向过滤器中插入几个关键字:”红楼梦“、”西游记“、”封神演义“、”三国演义“。假如现在我们有三种映射关系。

 从这张图可知,布隆过滤器只能告诉我们某个东西一定不存在或者可能存在。布隆过滤器的结构底层也是位图,但是在存储和访问过程中,还有哈希函数的存在。比如上图,有三个哈希函数,每一个字符串都有自己在布隆过滤器中的映射值。

现在我们要查询”平凡的世界“这本书是否存在布隆过滤器中,假如他的哈希值是3,4,6。这是判断结果就是不存在。但是如果他的哈希值为3,4,5。那么此时就会产生误判,由于之前存入的数据将”平凡的世界“这本书的映射位置都置成了1,所以现在就产生了误判。所以布隆过滤器的特点就是能够精确判断不存在,但是存在是存在误判的。

布隆过滤器实现

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++)
		{
			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
,class K = 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 hashi1 = Hash1()(key) % len;
		_bitset.set(hashi1);

		size_t hashi2 = Hash2()(key) % len;
		_bitset.set(hashi2);

		size_t hashi3 = Hash3()(key) % len;
		_bitset.set(hashi3);

		cout << hashi1 << ' ' << hashi2 << ' ' << hashi3 << endl;
	}

	bool test(const string& key)
	{
		size_t len = N * _X;
		size_t hashi1 = Hash1()(key) % len;
		if (!_bitset.test(hashi1))
		{
			return false;
		}

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

		size_t hashi3 = Hash3()(key) % len;
		if (!_bitset.test(hashi3))
		{
			return false;
		}

		return true; // 注意,当判断这个关键字存在时可能会出现误判,因为其他关键值在存入时可能映射的位置跟s相同
	}
private:
	static const size_t _X = 3; 
    //为了防止存储数据过多而引起误判率的提高,所以适当延长布隆过滤器的长度
	bitset<N*_X> _bitset;
};

布隆过滤器优点 

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

2. 哈希函数相互之间没有关系,方便硬件并行运算
3. 布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势
4. 在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势
5. 数据量很大时,布隆过滤器可以表示全集,其他数据结构不能
6. 使用同一组散列函数的布隆过滤器可以进行交、并、差运算

布隆过滤器缺陷

1. 有误判率,即存在假阳性(False Position),即不能准确判断元素是否在集合中(补救方法:再
建立一个白名单,存储可能会误判的数据)
2. 不能获取元素本身
3. 一般情况下不能从布隆过滤器中删除元素
4. 如果采用计数方式删除,可能会存在计数回绕问题

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

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

相关文章

重启天涯,一场关于 “救命” 的直播

大家好&#xff0c;我是校长。 昨天在知乎热搜榜上看到了关于天涯网站的帖子&#xff0c;感觉值得思考。 前一段时间&#xff0c;我们就看到了关于天涯要关闭的新闻&#xff0c;感觉撑不下去了。 说实话&#xff0c;当时看到这个新闻的时候&#xff0c;挺感慨的&#xff0c;一个…

初识网络之UDP网络套接字

目录 一、UDP中的socket编程常用接口 1. socket的含义 2. sockaddr结构 3. socket编程中UDP协议常用接口介绍 3.1 创建socket文件描述符&#xff08;TCP/UDP、客户端 服务器&#xff09; 3.2 绑定端口号&#xff08;TCP/UDP&#xff0c;服务器&#xff09; 3.3 接收数据…

2023年上半年数据库系统工程师上午真题及答案解析

1.计算机中, 系统总线用于( )连接。 A.接口和外设 B.运算器、控制器和寄存器 C.主存及外设部件 D.DMA控制器和中断控制器 2.在由高速缓存、主存和硬盘构成的三级存储体系中&#xff0c;CPU执行指令时需要读取数据&#xff0c;那么DMA控制器和中断CPU发出的数据地…

聊聊我在淘宝做性能分析的经历

我们新推出大淘宝技术年度特刊《长期主义&#xff0c;往往从一些小事开始——工程师成长总结专题》&#xff0c;专题收录多位工程师真诚的心路历程与经验思考&#xff0c;覆盖终端、服务端、数据算法、技术质量等7大技术领域&#xff0c;欢迎一起沟通交流。 本文为此系列第三篇…

RNN基础概念

一、潜变量回归模型 使用潜变量 h t h_{t} ht​总结过去的信息 二、RNN 更新隐藏状态&#xff1a; h t φ ( W h h h t − 1 W h x x t − 1 b h ) h_{t}φ(W_{hh}h_{t-1}W_{hx}x_{t-1}b_{h}) ht​φ(Whh​ht−1​Whx​xt−1​bh​) 更新输出&#xff1a; o t W o h h t b…

第六十天学习记录:C语言进阶:文件操作3文件版通讯录改造

在之前写的静态通讯录和动态通讯录中&#xff0c;都存在一个致命的弊端&#xff0c;那就是当软件运行的过程中添加信息&#xff0c;不关闭软件能够正常显示信息。但一旦关闭软件&#xff0c;内存回收之后当再次打开软件&#xff0c;之前保存的信息全部都得重新添加。 因此需要…

人工智能-推荐

常用的推荐系统算法实现方案有三种&#xff1a; 协同过滤推荐&#xff08;Collaborative Filtering Recommendation&#xff09;&#xff1a;该算法的核心是分析用户的兴趣和行为&#xff0c;利用共同行为习惯的群体有相似喜好的原则&#xff0c;推荐用户感兴趣的信息。兴趣有高…

老旧Mac不能升级macOS Ventura 13解决方案

老旧Mac不能升级macOS Ventura 13. 我2016年的MacBook Pro,遇到升级xcode失败以及不能更新到最新系统的问题。 ITMS-90725: SDK Version Issue - This app was built with the iOS 15.5 SDK. All iOS apps submitted to the App Store must be built with the iOS 15 SDK or …

chatgpt赋能python:Python信息隐藏:掩耳盗铃的神奇技巧

Python信息隐藏&#xff1a;掩耳盗铃的神奇技巧 在现代社会中&#xff0c;保护信息资产安全已经变得至关重要。信息隐藏&#xff08;steganography&#xff09;是一种将秘密信息嵌入到其他无关数据中以便于传输的技术。Python作为一种易学易用&#xff0c;而且功能强大的编程语…

嵌入式驱动入门之LCD-1.基础原理(颜色格式、FB、8080/RGB接口)

主要介绍LCD显示的基本原理&#xff0c;涉及像素、分辨率、颜色模型、RGB888等格式、Framebuffer、8080接口、RGB接口。 参考资料&#xff1a;嵌入式驱动入门之LCD-1.基础原理&#xff08;颜色格式、FB、8080/RGB接口&#xff09; 1.LCD显示出图片的基本原理 LCD作为显示器&…

Golang每日一练(leetDay0083) 汇总区间、多数元素II

目录 228. 汇总区间 Summary Ranges &#x1f31f; 229. 多数元素 II Majority Element ii &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Rust每日一练 专栏 Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专…

WPF绘制深圳地铁路线图

经常坐地铁&#xff0c;却不知道地铁多少条线路&#xff1f;哪个站下车&#xff1f;今天就带领大家熟悉并绘制深圳地铁路线图。 WPF在绘制矢量图方面有非常强大的优势&#xff0c;利用WPF可以绘制出各种矢量图形&#xff0c;如线&#xff0c;圆&#xff0c;多边形&#xff0c;…

SQL:批量获取次月留存和滚动留存

一、批量获取每月的次月留存率 问题描述&#xff1a; 现在有一份用户活跃数据&#xff0c;想要取每个月的用户在次月的留存&#xff0c;结果如下表 month活跃用户数次月留存用户数2023-011000050002023-021100060002023-03150007500... 思路&#xff1a; 先生成每个日期对…

Hadoop 原理介绍

1 文件系统和分布式文件系统 1.1 文件系统 文件系统&#xff1a;一种存储和组织数据的方法 实现了数据的存储、分级组织、访问、获取等操作使得用户对文件的访问和查找更容易使用树形目录的抽象概念代替了硬盘等物理设备中数据块的概念 ——>用户不必关系数据底层存在硬盘…

Java与分布式架构:微服务和RPC框架的应用和设计思路

章节一&#xff1a;引言 在当今互联网时代&#xff0c;大规模、高并发的应用成为了主流。为了应对这些挑战&#xff0c;软件架构趋向于分布式架构的设计。本文将重点讨论Java语言在分布式架构中的应用&#xff0c;以及微服务和RPC框架在设计中的关键思路和技术案例。 章节二&…

chatgpt赋能python:Python倒序排列的全面指南

Python倒序排列的全面指南 在Python中&#xff0c;几乎所有的数据结构都支持倒序排列。倒序排列是许多编程问题的解决方案&#xff0c;如查找最后一个元素&#xff0c;寻找最大值或最小值等等。在这篇文章中&#xff0c;我们将深入探讨Python中倒序排列的使用方法和技巧&#…

Flutter 笔记 | Flutter 核心原理(五)Box 布局模型和 Sliver 布局模型

根据前文我们已经从宏观上得知&#xff1a;Layout流程的本质是父节点向子节点传递自己的布局约束Constraints&#xff0c;子节点计算自身的大小&#xff08;Size&#xff09;&#xff0c;父节点再根据大小信息计算偏移&#xff08;Offset&#xff09;。在二维空间中&#xff0c…

Android逆向猿人学2022年app比赛第五题双向验证SSLpinning(步步验证)

SSLpinnig 前言一、起步二、抓包三、分析四、验证第一种方法&#xff1a;第二种方法&#xff1a; 借鉴 前言 这题在抓包方面会有点小问题&#xff0c;但是最后结果是正确出来了&#xff0c;如果有了解后面这个问题的读者&#xff0c;请多指教&#xff0c;十分感谢。 一、起步…

C Primer Plus第十五章编程练习答案

学完C语言之后&#xff0c;我就去阅读《C Primer Plus》这本经典的C语言书籍&#xff0c;对每一章的编程练习题都做了相关的解答&#xff0c;仅仅代表着我个人的解答思路&#xff0c;如有错误&#xff0c;请各位大佬帮忙点出&#xff01; 由于使用的是命令行参数常用于linux系…

串口通信简介

1. 数据通信的基础概念 1.1 数据通信方式 按数据通信方式分类&#xff0c;可分为串行通信和并行通信两种。串行和并行的对比如下图所示&#xff1a; 串行通信的基本特征是数据逐位顺序依次传输&#xff0c;优点是传输线少、 布线成本低 、 灵活度高等优点&#xff0c;一般用…