哈希扩展:位图与布隆过滤器

目录

  • 1. 位图
    • 1.1 位图引入
    • 1.2 位图概念
    • 1.3 位图的模拟实现
    • 1.4 位图相关问题
    • 1.5 位图的应用
  • 2. 布隆过滤器
    • 2.1 布隆过滤器概念
    • 2.2 模拟实现
    • 2.3 布隆过滤器相关问题
    • 2.3.1 哈希切分

1. 位图

1.1 位图引入

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

很经典的判断在不在的模型,那么可以使用set吗?答案是不可以的,40亿个整数大概需要16G的内存空间来进行存储,一般而言操作系统是不能也无法开辟这么大的空间,因此这种数据量大且仅需判断在不在的场景可以使用位图来解决。

数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。比如:
在这里插入图片描述
一个整形能表示数的个数为2的32次方,因此就需要这么多个比特位来表示,换算成字节为2的29次方也就是500MB左右,500MB系统是可以开出来的。

若有负数的情况,需要做一次相对映射,让其加上一个数变成正数,其余所有数字都需要加上这个数,然后判断某个值是否存在则需要再减去这个数得到它的真实值即可

1.2 位图概念

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

1.3 位图的模拟实现

//位图的模板参数为非类型模板参数
//用来表示数的总范围
template<size_t N>
class bitset
{
public:
	bitset()
	{
		// 先开辟空间
		_a.resize(N / 32 + 1);
	}

	//三个主要成员函数:

	// 第x个比特位标记成1
	void set(size_t x)
	{
		//先找到是在第几个整数中
		size_t i = x / 32;
		//再找到是在这个整数的第几个比特位
		size_t j = x % 32;
		_a[i] |= (1 << j);
	}

	// 第x个比特位个标记成0
	void reset(size_t x)
	{
		size_t i = x / 32;
		size_t j = x % 32;
		_a[i] &= (~(1 << j));
	}
	// 检测第x个比特位是0还是1
	bool test(size_t x)
	{
		size_t i = x / 32;
		size_t j = x % 32;
		return _a[i] & (1 << j);
	}
private:
	// 由于要用到移位运算符,因此参数类型必须是整形家族的
	//若使用char则上面所有/或%32的位置都要改成8
	//因为char占8个比特位
	vector<int> _a;
};

1.4 位图相关问题

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

一个比特位只能表示两种状态,即在不在,无法统计对应数字的出现次数,而两个比特位则可以表示出四种状态,00 01 10和11,因此可以使用两个比特位即两个位图来统计次数,规定00代表没有出现,01代表出现一次,10代表两次及以上。

代码实现:

template<size_t N>
class twoBt {
public:
	void set(size_t x) {
		//如果是00则置为01,表示出现一次
		if (!b1.test(x) && !b2.test(x)) {
			b2.set(x);
		}
		//如果是01则置为10,表示出现次数≥2次
		else if (!b1.test(x) && b2.test(x)) {
			b1.set(x);
			b2.reset(x);
		}
		//其它情况均表示大于2次无需出口i
	}

	//判断是否出现一次,即为01
	bool isOnce(size_t x) {
		return !b1.test(x) && b2.test();
	}

private:
	//使用两个位图
	bitset<N> b1;
	bitset<N> b2;
};
  1. 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?

一种方法是建立一个位图,然后将一个文件中的数据依次set到对应的位置,然后再依次检测另一个文件中的数据,看该数据对应要set的位置是否为1,若为1说明前面一个文件中出现了和当前相同的元素,就是交集,此时需要把该位置的值reset一下即置成0,因为可能会出现重复的值,但是交集没有重复,相当于去重。
另一种方法是建立两个位图,把两个文件中的数据依次set到对应位图中,然后遍历两个位图,若两个位图中的同一个位置都为1说明是交集,使用两个位图天然就完成了去重操作。

代码实现:

//使用第二种方法:
int main() {
	int a1[] = { 1,1,2,2,3,4,5,5,6 };
	int a2[] = { 3,5,7,9 };

	bit::bitset<10> b1;
	bit::bitset<10> b2;

	for (int x : a1) {
		b1.set(x);
	}
	for (int x : a2) {
		b2.set(x);
	}

	for (int i = 0; i < 10; ++i) {
		if (b1.test(i) && b2.test(i)) {
			cout << i << ' ';
		}
	}
	cout << endl;
	return 0;
}
  1. 位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数。

和第一问非常类似,直接上代码:

template<size_t N>
class twoBt {
public:
	void set(size_t x) {
		//如果是00则置为01,表示出现一次
		if (!b1.test(x) && !b2.test(x)) {
			b2.set(x);
		}
		//如果是01则置为10,表示出现两次
		else if (!b1.test(x) && b2.test(x)) {
			b1.set(x);
			b2.reset(x);
		}
		//如果是10则置成11,表示出现三次及以上
		else {
			b1.set(x);
			b2.set(x);
		}
	}
	
	//判断出现0、1和2次
	//00、01和10
	bool isValid(size_t x) {
		return (!b1.test(x) && !b2.test(x)) || (!b1.test(x) && b2.test(x)) || (b1.test(x) && !b2.test(x));
	}
private:
	bitset<N> b1;
	bitset<N> b2;
};

1.5 位图的应用

  1. 快速查找某个数据是否在一个集合中
  2. 排序 + 去重
  3. 求两个集合的交集、并集等
  4. 操作系统中磁盘块标记

2. 布隆过滤器

位图一般应用在判断整形数据是否存在的场景,得到的结果一定准确,若为字符串(或其它自定义)类型则得到的结果就不一定准确了,会存在误判的可能性,因为不同的字符串通过哈希函数得到的映射位置可能是一样的,这样便导致了冲突造成误判,如何有效缓解误判问题呢?

2.1 布隆过滤器概念

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

将哈希与位图结合,即布隆过滤器

需要注意的是,即使是布隆过滤器也无法完全杜绝误判的存在,只能降低误判率

2.2 模拟实现

#include <iostream>
#include <bitset>
using namespace std;
struct BKDRHash {
    size_t operator()(const string& str) {
        size_t hash = 0;
        for (auto ch : str)
            hash = hash * 131 + ch;
        return hash;
    }
};

struct APHash {
    size_t operator()(const string& str) {
        size_t hash = 0;
        for (size_t i = 0; i < str.size(); i++) {
            size_t ch = str[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& str) {
        size_t hash = 5381;
        for (auto ch : str)
        {
            hash += (hash << 5) + ch;
        }
        return hash;
    }
};

namespace bit {
	template<size_t N,
			 class K = string,
			 class hashFunc1 = BKDRHash,
			 class hashFunc2 = APHash,
		     class hashFunc3 = DJBHash>
        class BloomFilter {
        public:
            void set(const K& key) {
                size_t hashi1 = hashFunc1()(key) % N;
                _bs.set(hashi1);

                size_t hashi2 = hashFunc2()(key) % N;
                _bs.set(hashi2);

                size_t hashi3 = hashFunc3()(key) % N;
                _bs.set(hashi3);
            }

            bool test(const K& key) {
                //计算出对应存储位置
                //只要有一个位置是0就是不存在
                size_t hashi1 = hashFunc1()(key) % N;
                if (!_bs.test(hashi1))
                    return false;

                size_t hashi2 = hashFunc2()(key) % N;
                if (!_bs.test(hashi2))
                    return false;

                size_t hashi3 = hashFunc3()(key) % N;
                if (!_bs.test(hashi3))
                    return false;

                //依旧是存在误判
                //不存在的值可能会返回true
                return true;           
            }

        private:
            bitset<N> _bs;
    };
}

一般而言,布隆过滤器是不支持删除的,因为简单的把某个位置置成0有可能会影响其它值的判定,比如x和y都映射到了1位置,若删除x,把1位置置成0则会影响到y的判定,y存在但是查找y会得到不存在的结果。

一种支持删除的方法:将布隆过滤器中的每个比特位扩展成一个小的计数器,插入元素时给k个计数器(k个哈希函数计算出的哈希地址)加一,删除元素时,给k个计数器减一,通过多占用几倍存储空间的代价来增加删除操作。

布隆过滤器的优势之一就是空间损耗小,若像上述支持删除功能的话就相当于把这个优势给抹掉了,需要酌情考虑

2.3 布隆过滤器相关问题

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

设两个大文件的名称分别为A文件和B文件。

近似算法:把A文件中的数据全部放进布隆过滤器,然后逐个检测B文件中的数据是否在过滤器中,如果不在那一定不是交集,如果在,很有可能就是交集,因为还存在小概率误判。

精确算法:可以将其分别平均分割成N份小文件(每个文件大小为几百兆即可),然后把A文件中数据依次放入每个小文件中,然后依次拿A的每个小文件中的数据与B的每个小文件中的数据依次比对,相同就是交集,但是这种比对的效率很低,如果要提高效率则需要用到哈希切分

2.3.1 哈希切分

同样是将其分割成N份小文件(无需平均分割),编号为0~N,把A和B文件中的query依次通过哈希函数计算出一个位置i,然后将该query数据放入编号为Ai(或者Bi)的小文件中,由于使用的是相同的哈希函数,因此A和B文件中相同的query数据必然分别会存放到对应相同编号的小文件中(但也可能包含冲突的query)。

这里与哈希桶非常相似,每个相同的数据必然进入同一个桶,但是桶中不一定都是相同的数据,还可能包含不同但冲突的数据

切分完毕后,找交集,只需要把Ai文件中的数据依次放入哈希表中,然后再用Bi中的数据依次检测是否存在,在就是交集再把其从表中删除(防止重复),这种做法极大的提高了比对效率,那还有什么问题没?

问题是每个小文件并不是平均切分的,因此可能某个小文件相同的或者冲突的query过多导致文件过大,该怎么办?

解决方案如下:先把该文件中的数据读出来放入一个set中,若set报错抛异常(bad_alloc),那么说明该文件数据的冲突太多,set放不下了,这种情况则需要换个哈希函数继续对其进行切分,重新对数据进行映射。
若能够全部放进set中,则说明该文件中有大量相同的数据,这种情况直接进行比对即可。

还有一个与其类似的问题:
给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址?与上题条件相同,如何找到top K的IP?

同样使用哈希切分,切分成若干份小文件,把每个IP地址数据通过哈希函数计算出一个编号i,将其存进对应编号的小文件中,然后使用哈希表依次统计每个小文件中每个数据的出现次数,并且每统计一个文件都要更新出现次数最多的那个IP地址数据,全部文件统计完成后即可找到出现次数最多的那个IP地址。

而统计topK则需要再加个小堆结构来处理。

同样会出现某个小文件过大的问题,但是解决方法是一样的

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

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

相关文章

MySQL,分组order by

一、创建分组 ## 创建分组 -- 返回每个发布会的参会人数 SELECT event_id,COUNT(*) as canjia_num FROM sign_guest GROUP BY event_id; 1、group by子句可以包含任意个列&#xff0c;但是但指定的所有列都是一起计算的。 group by 后2个字段一起计算的 2、group by后面可以跟…

QT Widget - 随便画个圆

简介 实现在界面中画一个圆, 其实目的是想画一个LED效果的圆。代码 #include <QApplication> #include <QWidget> #include <QPainter> #include <QColor> #include <QPen>class LEDWidget : public QWidget { public:LEDWidget(QWidget *pare…

正态总体的假设检验

一、三种情况 1.均值μ的假设检验 (1)σ已知 (2)σ未知 2.方差σ的假设检验 二、例题

Docker部署wordpress和Jenkins

准备机器&#xff1a; 192.168.58.151 &#xff08;关闭防火墙和selinux&#xff09; 安装好docker服务 &#xff08;详细参照&#xff1a;http://t.csdnimg.cn/usG0s 中的国内源安装docker&#xff09; 部署wordpress: 创建目录&#xff1a; [rootdocker ~]# mkdi…

【Java JVM】运行时数据区

JVM 在执行 Java 程序的过程中会把它管理的内存分为若干个不同的数据区域, 这些区域有着各自的用途。 根据《Java虚拟机规范》中规定, JVM 所管理的内存大致包括以下几个运行时数据区域, 如图所示: 这个运行时数据区被分为了 5 大块 方法区 (Method Area)堆 (Heap)虚拟机栈 (V…

Facebook广告系统结构

Facebook广告系统是一个复杂的大型系统&#xff0c;由多个组件和子系统相互配合工作&#xff0c;实现了广告的投放、拍卖、个性化推荐和效果评估等功能。下面小编讲讲Facebook广告系统的结构。 1、广告管理界面 广告管理界面是广告主与Facebook进行交互的入口&#xff0c;广告…

FlinkSQL中的窗口

多维分析 需求&#xff1a;有一张test表&#xff0c;表的字段为&#xff1a;A, B, C, amount, 其中A, B, C为维度字段&#xff0c;求以三个维度任意组合&#xff0c;统计sum(amount) Union方案&#xff1a; A, B, C的任意组合共有8种&#xff0c;分别为&#xff08;A, B,C,AB…

软件工程期末复习+数据仓库ETL

一、软件工程 请用基本路径测试方法为下列程序设计测试用例&#xff0c;并写明中间过程&#xff1a; 第1步&#xff1a;画出流程图 1.菱形用于条件判断。用在有分支的地方。 2.矩形表示一个基本操作。 3.圆形是连接点 第2步&#xff1a;计算程序环路复杂性 流图G的环路复杂…

设计模式——装饰模式(结构型)

引言 装饰模式是一种结构型设计模式&#xff0c; 允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。 假设你正在开发一个提供通知功能的库&#xff0c; 其他程序可使用它向用户发送关于重要事件的通知。 库的最初版本基于 通知器Notifier类&#xff0c;…

西南科技大学数据库实验二(表数据插入、修改和删除)

一、实验目的 &#xff08;1&#xff09;学会用SQL语句对数据库进行插入、修改和删除数据操作 &#xff08;2&#xff09;掌握insert、update、delete命令实现对表数据插入、修改和删除等更新操作。 二、实验任务 创建数据库&#xff0c;并创建Employees表、Departments表和…

机器学习 | 过拟合与正则化、模型泛化与评价指标

一、过拟合与正则化 1、多项式逼近思想 任何函数都可以用多项式来表示。 举个栗子 ~ 比如说 泰勒公式 若要拟合sinx&#xff0c;泰勒认为仿造一条曲线&#xff0c;首先要保证在原点重合&#xff0c;之后在保证在这个点处的倒数相同&#xff0c;导数的倒数相同。 高次项引入了更…

appium2.0.1安装完整教程+uiautomator2安装教程

第一步&#xff1a;根据官网命令安装appium&#xff08;Install Appium - Appium Documentation&#xff09; 注意npm前提是设置淘宝镜像&#xff1a; npm config set registry https://registry.npmmirror.com/ 会魔法的除外。。。 npm i --locationglobal appium或者 npm…

【Redis】远程访问配置教程与远程客户端连接测试

前言 Redis 是一种基于内存的高性能键值存储数据库&#xff0c;常用于缓存、会话管理和实时数据分析等场景。在默认情况下&#xff0c;Redis 不允许远程连接&#xff0c;为了进行远程连接&#xff0c;需要进行一些配置和操作。接下来将介绍如何修改配置文件以允许远程连接&…

JVM学习之运行时数据区

运行时数据区 概述 内存 内存是非常重要的系统资源&#xff0c;是硬盘和CPU的中间桥梁&#xff0c;承载着操作系统和应用程序的实时运行。JVM内存布局规定了Java在运行过程中内存申请&#xff0c;分配&#xff0c;管理的策略&#xff0c;保证了JVM高效稳定运行。不同的JVM对于…

k8syaml提供的几个有意思的功能,Kubernetes在线工具网站

k8syaml.cn 提供的几个有意思的功能。 一、yaml资源快速生成 之前编写operator的helm的时候就需要自己写deployment、service、configmap这些资源&#xff0c;那么多字段也记不清&#xff0c;都是先找个模版&#xff0c;然后copy改改&#xff0c;再看官方文档&#xff0c;添加…

网络安全—学习溯源和日志分析

日志分析的步骤&#xff1a; 判断是否为攻击行为 不是&#xff1a;不用处理 是&#xff1a;判断攻击是否成功或者失败 攻击失败&#xff1a;判断IP地址是否为恶意地址&#xff0c;可以让防火墙过滤IP地址 攻击成功&#xff1a;做应急处置和溯源分析 应急处置&#xff1a;网络下…

【Linux】驱动

驱动 驱动程序过程 系统调用 用户空间 内核空间 添加驱动和调用驱动 驱动程序是如何调用设备硬件 驱动 在计算机领域&#xff0c;驱动&#xff08;Driver&#xff09;是一种软件&#xff0c;它充当硬件设备与操作系统之间的桥梁&#xff0c;允许它们进行通信和协同工作。驱动程…

OpenAI开源超级对齐方法:用GPT-2,监督、微调GPT-4

12月15日&#xff0c;OpenAI在官网公布了最新研究论文和开源项目——如何用小模型监督大模型&#xff0c;实现更好的新型对齐方法。 目前&#xff0c;大模型的主流对齐方法是RLHF&#xff08;人类反馈强化学习&#xff09;。但随着大模型朝着多模态、AGI发展&#xff0c;神经元…

MySQL数据库 DML

目录 DML概述 添加数据 修改数据 删除数据 DML概述 DML英文全称是Data Manipulation Language(数据操作语言)&#xff0c;用来对数据库中表的数据记录进行增、删、改操作。 添加数据(工NSERT)修改数据(UPDATE)删除数据(DELETE) 添加数据 (1)给指定字段添加数据 INSERT …

多线程JUC 第2季 CAS的作用介绍与自旋锁

一 CAS作用介绍 1.1 CAS作用 CAS有3个操作数&#xff0c;位置内存值V&#xff0c;旧的预期值A&#xff0c;要修改的更新值B&#xff0c;如果内存值V和预期值相同则&#xff0c;内存值改为B&#xff0c;否则什么都不做。当它重来重试的这种行为称为-自旋。 CAS是一条cpu的原…