【C++】位图和布隆过滤器

news2024/11/17 11:31:32

文章目录

  • 位图
    • 概念
    • 难点
    • 代码
  • 布隆过滤器
    • 概念
    • 插入
    • 查找
    • 删除
    • 优缺点
    • 代码

位图

概念

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

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

  1. 遍历,时间复杂度O(N)
  2. 排序(O(NlogN)),利用二分查找: logN
  3. 位图解决
    数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。比如下图:

我们用 char 来实现位图,一个char类型的数据占8个字节,下标是 0 到 7 ,如下图 3 个 char 类型的数据,就可以表示 0 到 23 这些整数的状态(是否存在)。
在这里插入图片描述

此外,在每一个 char 类型的数据内部,我们可以选择右低左高 或者 左低右高,如下图,选择右低左高的方式,这就类似于大端存储。
在这里插入图片描述

难点

位图并不难理解,难点主要就是找到数据对应的比特位,然后将该比特位修改为 1 或者 0。

假设在位图 _b (用 char 实现)中寻找数据 x 对应的比特位分为两步:

  • 找到位于哪一个 char 类型的数据中。 i = x / 8;
  • 找到在该 char 类型的数据中,处于第几个位置。 j = x % 8;

这样子,我们就可以知道 x 在位图中的位于 _b[i] 的第 j 个位置。

  • 如果要标记 x 存在,也就是把对应的比特位修改为 1。_b[i] |= (1 << j);
  • 如果要标记 x 不存在,也就是把对应的比特位修改为 0。 _b[i] &= ~(1 << j);

1 << j ,代表着将 1 左移 j 位,然后 _b[i] 按位或 (1<<j) ,就可以把 _b[i] 的第 j 位设置为1。因为本文实现的位图,在每一个 char 类型数据中,是按照 左高右低,所以 1 左移 j 位。

标记 x 不存在,先将 1 左移 j 位,然后取反, 假设 j =3 , 那么 ~(1<<3) 的结果就是 11110111。用它和 _b[i] 按位与,由于第 j 位是 0,所以 _b[i] 必定是 0;其他七位是1,按位与之后 _b[i] 的其他七位保持原状。

代码

#pragma once
#include<vector>
#include<string>

using namespace std;

template<size_t N>
class bitset
{
public:

	bitset()
	{
		_b.resize(N / 8 + 1, 0);
	}

	void set(size_t x)
	{
		size_t i = x / 8;
		size_t j = x % 8;

		_b[i] |= (1 << j);
	}

	void reset(size_t x)
	{
		size_t i = x / 8;
		size_t j = x % 8;
		_b[i] &= ~(1 << j);
	}

	bool test(size_t x)
	{
		size_t i = x / 8;
		size_t j = x % 8;
		return (1 << j) & _b[i];
	}


private:
	vector<char> _b;
};

布隆过滤器

概念

我们在使用新闻客户端看新闻时,它会给我们不停地推荐新的内容,它每次推荐时要去重,去掉那些已经看过的内容。问题来了,新闻客户端推荐系统如何实现推送去重的? 用服务器记录了用户看过的所有历史记录,当推荐系统推荐新闻时会从每个用户的历史记录里进行筛选,过滤掉那些已经存在的记录。 如何快速查找呢?

  1. 用哈希表存储用户记录,缺点:浪费空间。
  2. 用位图存储用户记录,缺点:位图一般只能处理整形,如果内容编号是字符串,就无法处理了。
  3. 将哈希与位图结合,即布隆过滤器。

布隆过滤器可以告诉我们 “某样东西一定不存在或者可能存在”,也就是说布隆过滤器说这个数不存在则一定不存,布隆过滤器说这个数存在可能不存在(误判,后面会讲到)。

其方法也不难理解,先将字符串进行哈希,映射到某个整数,然后用这个整数的状态(是否存在),标识这个字符串是否存在。( 整数的状态就可以用位图来表示。)
但是,为了保证正确性,一般会利用不同的哈希函数(正常是 3 个),分别对字符串进行哈希,得到多个不同的映射结果,用这多个结果共同标识字符串的存在与否(映射结果全部存在,字符串才存在,否则不存在)。

插入

如下,将字符串分别进行三次哈希映射,映射到三个整数,然后在位图中将这三个整数标记为存在(比特位修改为 1 ):

至于哈希函数,网络上有现成的,搜索即可。

在这里插入图片描述

查找

布隆过滤器的思想是将一个元素用多个哈希函数映射到一个位图中,因此被映射到的位置的比特位一定为1。所以可以按照以下方式进行查找:分别计算每个哈希值对应的比特位置存储的是否为零,只要有一个为零,代表该元素一定不在哈希表中,否则可能在哈希表中。

布隆过滤器的查找 会存在误判的情况,如下图,插入 x,y,z ,并没有插入 w,可是,当我们判断 w 是否存在时,三个哈希函数映射的结果都是 1,从位图的角度来看, w 就是存在的,但是实际上是因为 x,y,z 的哈希函数映射结果和 w 的三个重合了!

在这里插入图片描述

删除

布隆过滤器不能直接支持删除工作,因为在删除一个元素时,可能会影响其他元素。
比如:删除上图中"tencent"元素,如果直接将该元素所对应的二进制比特位置0,“baidu”元素也被删除了,因为这两个元素在多个哈希函数计算出的比特位上刚好有重叠。
一种支持删除的方法:将布隆过滤器中的每个比特位扩展成一个小的计数器,插入元素时给k个计数器(k个哈希函数计算出的哈希地址)加一,删除元素时,给k个计数器减一,通过多占用几倍存储空间的代价来增加删除操作。

缺陷:

  1. 无法确认元素是否真正在布隆过滤器中
  2. 存在计数回绕

优缺点

布隆过滤器优点

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

布隆过滤器缺陷

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

代码


template<size_t N>
class bitset
{
public:

	bitset()
	{
		_b.resize(N / 8 + 1, 0);
	}

	void set(size_t x)
	{
		size_t i = x / 8;
		size_t j = x % 8;

		_b[i] |= (1 << j);
	}

	void reset(size_t x)
	{
		size_t i = x / 8;
		size_t j = x % 8;
		_b[i] &= ~(1 << j);
	}

	bool test(size_t x)
	{
		size_t i = x / 8;
		size_t j = x % 8;
		return (1 << j) & _b[i];
	}


private:
	vector<char> _b;
};


#pragma once
#include<vector>
#include<string>

using namespace std;

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


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 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 = 5;
	bitset<N * _x> _bs;
};

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

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

相关文章

buu_Misc总结2

目录 百里挑一 exiftool: [SUCTF2018]followme grep工具使用&#xff1a; [安洵杯 2019]Attack mimikatz工具使用&#xff1a; 百里挑一 打开文件是流量包&#xff0c;发现里面有很多图片 导出http 另存一个文件夹&#xff0c;里面很多图片&#xff0c;啥也看不出来 &…

医用影像技术

1.X光和CT原理 X光和CT&#xff08;计算机断层扫描&#xff09;都是医学成像技术&#xff0c;用于诊断和治疗。它们的原理如下&#xff1a; X光原理&#xff1a; X光是一种电磁辐射&#xff0c;与可见光类似&#xff0c;但具有更高的能量。当X光通过人体或物体时&#xff0c;…

创作一周年纪念日【道阻且长,行则将至】

✨个人主页&#xff1a; 北 海 &#x1f389;所属专栏&#xff1a; 技术之外的往事 &#x1f383;所处时段&#xff1a; 大学生涯[1/2] 文章目录 一、起点一切皆有定数 二、成果尽心、尽力 三、相遇孤举者难起&#xff0c;众行者易趋 四、未来长风破浪会有时&#xff0c;直挂云…

[MySQL]MySQL表中数据的增删查改(CRUD)

[MySQL]MySQL表中数据的增删查改(CRUD) 文章目录 [MySQL]MySQL表中数据的增删查改(CRUD)1. 新增数据1.1 单列插入1.2 多列插入1.3 插入否则更新1.4 替换 2. 基本查询数据2.1 全列查询2.2 指定列查询2.3 查询字段为表达式2.4 为查询结果指定别名2.5 结果去重2.6 where子句2.7 or…

修复漏洞(二)离线升级Tomcat版本

前言 生产环境无法联网&#xff0c;只能通过下载离线版本更新Tomcat到小版本最新注意Tomcat10和11与jdk1.8都不兼容&#xff0c;只能更新到小版本的最新前提是按照我这种方法配置Tomcat开机自启的https://blog.csdn.net/qq_44648936/article/details/130022136 步骤 备份整个…

IAR编译报错:Error[Pe065: expected a “.“ and Error[Pe007]:unrecognized token

IAR报错 Error[Pe065: expected a “.” and Error[Pe007]:unrecognized token 使用IAR编译报如下错误&#xff1a; 找到软件报错的地方&#xff0c;从肉眼看&#xff0c;并没有错误的地方&#xff0c;如下图所示&#xff1a; 这时肯定是丈二和尚摸不着头脑&#xff0c;这里…

VMware种ubuntu22.04挂载ax88179网卡不显示的问题

网上找了很多解决办法&#xff0c;都说是驱动的问题&#xff0c;其实不是。ubuntu22自带无bug的ax88179的驱动。 其实是Vmware的问题&#xff0c;在虚拟机设置种添加一个usb控制器&#xff0c;然后这样设置就好了。

HCIP第一课实验小练习

目录 题目&#xff1a;​编辑 第一步&#xff1a;地址规划&#xff08;子网划分&#xff09; 第二步&#xff1a;设计拓扑并规划地址配置 第三步&#xff1a;VLAN规划配置 LW1 LW2 第四步&#xff1a;网关配置 第五步&#xff1a;及静态路由配置 第六步防止成环 题目&…

深入理解Linux网络——TCP连接建立过程(三次握手源码详解)

文章目录 一、相关实际问题二、深入理解listen1&#xff09;listen系统调用2&#xff09;协议栈listen3&#xff09;接收队列定义4&#xff09;接收队列申请和初始化5&#xff09;半连接队列长度计算6&#xff09;小结 三、深入理解connect1&#xff09;connect调用链展开2&…

单片机第一季:零基础6——按键

目录 1&#xff0c;独立按键 2&#xff0c;矩阵按键 &#xff08;注意&#xff1a;文章中的代码仅供参考学习&#xff0c;实际使用时要根据需要修改&#xff09; 1&#xff0c;独立按键 按键管脚两端距离长的表示默认是导通状态&#xff0c;距离短的默认是断开状态&#xf…

用PuTTY作为telnet客户端

PuTTY工具可以作为telnet客户端使用。 打开PuTTY&#xff0c;输入telnet服务器的ip地址、端口号&#xff0c;选择Other&#xff0c;然后点击Open&#xff0c;就可以了&#xff1a;

C# Modbus通信从入门到精通(2)——Modbus RTU协议原理

Modbus RTU是串行链路上的协议,也就是说Modbus RTU是通过串口通信来实现的,它可以通过RS232、RS485物理层的接口来实现,同时它也是一个主从协议,在同一时间总线上只能有一个主站和一个或多个(最多247)个从站。Modbus通信总是由主站发起,从站没有接收到主站的请求时不会发…

【Maven三】——maven生命周期和插件

系列文章目录 Maven之POM介绍 maven命令上传jar包到nexus 【Maven二】——maven仓库 maven生命周期和插件 系列文章目录前言一、什么是生命周期&why1.三套生命周期2.clean生命周期3.default生命周期4.site生命周期5.命令行与生命周期 二、插件目标三、插件绑定1.内置绑定2…

大华智慧园区综合管理平台RCE漏洞复现(0day)

0x01 产品简介 “大华智慧园区综合管理平台”是一款综合管理平台&#xff0c;具备园区运营、资源调配和智能服务等功能。平台意在协助优化园区资源分配&#xff0c;满足多元化的管理需求&#xff0c;同时通过提供智能服务&#xff0c;增强使用体验。 0x02 漏洞概述 大华智慧园…

基于PyTorch实战权重衰减——L2范数正则化方法(附代码)

文章目录 0. 前言1. 权重衰减方法作用2. 权重衰减方法原理介绍3. 验证权重衰减法实例说明3.1 训练数据样本3.2 网络模型3.3 损失函数3.4 训练参数 4. 结果对比5. 源码 0. 前言 按照国际惯例&#xff0c;首先声明&#xff1a;本文只是我自己学习的理解&#xff0c;虽然参考了他人…

Java 设计模式——迭代器模式

目录 1.概述2.结构3.案例实现3.1.抽象迭代器3.2.具体迭代器3.3.抽象聚合3.4.具体聚合3.5.测试 4.优缺点5.使用场景6.JDK 源码解析——Iterator 1.概述 迭代器模式 (Iterator Pattern) 是一种行为型设计模式&#xff0c;它提供一种顺序访问聚合对象&#xff08;如列表、集合等&…

JVM学习之内存与垃圾回收篇1

文章目录 1 JVM与Java体系结构1.0 Java发展重大事件1.1 虚拟机和Java虚拟机1.3 JVM整体结构1.4 Java代码执行流程1.5 JVM架构模型1.6 JVM的生命周期1.7 JVM发展历程 2 类加载子系统2.1 ClassLoader2.2 用户自定义类加载器2.2.1 为什么需要自定义类加载器2.2.2 自定义类加载器的…

【框架篇】对象注入的三种实现方式

对象注入的实现 一&#xff0c;实现方式的使用 对象注入也可被称为对象装配&#xff0c;是把Bean对象获取出来放到某个类中。 对象注入的实现方式有3种&#xff0c;分别为属性注入&#xff0c;Setter注入和构造方法注入。 为了更好地理解对象注入的实现方式&#xff0c;搞个…

24 MFC文档串行化和单文档应用程序

文章目录 文档串行化全部代码 单文档应用程序搭建原理搭建框架Win32 过度到MFC 三部曲设置ID资源全部代码 单文档应用程序设置标题绘图 简单的管理系统部分代码 文档串行化 ui 设计 保存 void CfileDemoDlg::OnBnClickedBtnSave() {UpdateData();//CFile file(L"Demo.dat…

python+pytest接口自动化(9)-cookie绕过登录(保持登录状态)

目录 cookie工作原理 cookie绕过登录 总结 在编写接口自动化测试用例或其他脚本的过程中&#xff0c;经常会遇到需要绕过用户名/密码或验证码登录&#xff0c;去请求接口的情况&#xff0c;一是因为有时验证码会比较复杂&#xff0c;比如有些图形验证码&#xff0c;难以通过…