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

目录

  • 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个比特位标记成1void set(size_t x){//先找到是在第几个整数中size_t i = x / 32;//再找到是在这个整数的第几个比特位size_t j = x % 32;_a[i] |= (1 << j);}// 第x个比特位个标记成0void reset(size_t x){size_t i = x / 32;size_t j = x % 32;_a[i] &= (~(1 << j));}// 检测第x个比特位是0还是1bool 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}//判断是否出现一次,即为01bool 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和10bool 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));elsehash ^= (~((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;//依旧是存在误判//不存在的值可能会返回truereturn 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则需要再加个小堆结构来处理。

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

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

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

相关文章

LabVIEW开发地铁运行安全监控系统

LabVIEW开发地铁运行安全监控系统 最近昌平线发生的故障事件引起了广泛关注&#xff0c;暴露了现有地铁运行监控系统在应对突发情况方面的不足。为了提高地铁系统的运行安全性&#xff0c;并防止类似事件再次发生&#xff0c;提出了一套全面的地铁运行安全监控系统方案。此方案…

MySQL数据库,变量、流程控制与游标

变量 系统变量 系统变量分为全局系统变量&#xff08;需要添加global关键字&#xff09;以及会话系统变量&#xff08;需要添加session关键字&#xff09;有时将全局系统变量简称为全局变量&#xff0c;将会话系统变量称为local变量。如果不写&#xff0c;默认会话级别。静态…

如何在页面中加入百度地图

官方文档&#xff1a;jspopularGL | 百度地图API SDK (baidu.com) 添加一下代码就可以实现 <!DOCTYPE html> <html> <head><meta name"viewport" content"initial-scale1.0, user-scalableno"/><meta http-equiv"Conten…

超分辨率重建——SAN训练自己数据集及推理测试(详细图文教程)

目录 一、源码包下载二、数据集准备三、预训练权重文件四、训练环境五、训练5.1 超参数修改5.2 训练模型5.2.1 命令方式训练5.2.2 Configuration配置参数方式训练 5.3 模型保存 六、推理测试6.1 超参数修改6.2 测试6.2.1 命令方式测试6.2.2 Configuration配置参数方式测试 6.3 …

响应者链概述

响应者链 iOS事件的3大类型 Touch Events(触摸事件)Motion Events(运动事件&#xff0c;比如重力感应和摇一摇等)Remote Events(远程事件&#xff0c;比如用耳机上得按键来控制手机) 触摸事件 处理触摸事件的两个步骤 寻找事件的最佳响应者事件的响应在响应链中的传递 寻…

Web UI 自动化 元素定位利器

序 元素定位&#xff0c;对于 Web UI 自动化而言&#xff0c;绝对是大家成长道路上的一道绊脚石。 很多初学者&#xff0c;都“死”在了元素定位上&#xff0c;从而失去了学习的兴趣。导致职业规划不得不半途而废~ 那么&#xff0c;今天&#xff0c;我们就使用 Katalon Stu…

深入理解CI/CD与Docker集成:自动化构建和部署的完整指南

在当今软件开发的快节奏环境中&#xff0c;自动化构建和部署是实现敏捷开发和DevOps实践的关键。Docker容器技术为这一过程引入了更高的灵活性和一致性。本文将深入研究如何将持续集成/持续部署&#xff08;CI/CD&#xff09;与Docker集成&#xff0c;提供更详细、实用的示例代…

向华为学习:基于BLM模型的战略规划研讨会实操的详细说明,含研讨表单(二)

上一篇文章&#xff0c;华研荟结合自己的经验和实践&#xff0c;详细介绍了基于BLM模型的战略规划研讨会的设计和组织流程&#xff0c;提高效率的做法。有朋友和我私信沟通说&#xff0c;其实这个流程不单单适合于BLM模型的战略规划研讨会&#xff0c;实际上&#xff0c;使用其…

v851s ssh搭建与使用

ssh 概述: 1. 用来远程登录的一种安全通道协议(常用于linux 、UNIX中); 2. 分为服务端和客户端: 1)服务端即openSSH ,一般属于目标开发板(linux中配置文件路径/etc/ssh/sshd_config); 2)客户端即登录端,常用工具:sercureCRT 、MobaXterm 、Putty等; 1. ssh 服务…

解锁数据探索新时代,JetBrains DataGrip 2023 Mac/win中文版下载

JetBrains DataGrip 2023 Mac/win&#xff0c;作为一款全新的数据库管理和开发工具&#xff0c;为数据工程师、分析师和开发人员提供了强大的功能和工具&#xff0c;帮助他们更高效地处理和分析数据。无论你是使用Mac还是Windows系统&#xff0c;都能够通过这款软件轻松驾驭数据…

springMVC 学习总结(四) 拦截器及统一异常处理

一.拦截器 1.拦截器与过滤器的区别 拦截器 Interceptor 和 过滤器 Filter类似&#xff0c;主要用于拦截用户请求并作出一定处理操作&#xff0c; 但两则也有不同之处&#xff0c;如过滤器只在Servlet前后起作用&#xff0c;是Servlet规范定义的&#xff0c;是Servlt容器才支…

CTF网络安全大赛是干什么的?发展史、赛制、赛程介绍,参赛需要学什么?

CTF&#xff08;Capture The Flag&#xff09;是一种网络安全竞赛&#xff0c;它模拟了各种信息安全场景&#xff0c;旨在提升参与者的网络安全技能。CTF 赛事通常包含多种类型的挑战&#xff0c;如密码学、逆向工程、网络攻防、Web 安全、二进制利用等。 发展史 CTF 的概念…

SpringBoot中处理处理国际化

SpringBoot中处理处理国际化 1. 创建SpringBoot项目2. resource下创建i18n目录3. 右键i18n新建资源包4. 弹框中添加需要支持的国际化语言5. messages.properties中添加需要国际化的键6. application.yaml添加配置7. 国际化工具8. 使用功能9 场景问题 1. 创建SpringBoot项目 2.…

spring 基于注解管理bean @Autowired

目录结构&#xff1a; 代码示例&#xff1a; UserContronller.java package com.atguigu.spring6.autowired.controller;import com.atguigu.spring6.autowired.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springfra…

阿里云对象存储OSS外网流出流量异常增多导致费用上升的解决方法

本文介绍在使用阿里云的对象存储 OSS服务过程中&#xff0c;外网流量消耗异常增多并导致异常的费用支出这一情况的问题发现、问题定位与几种不同的问题解决方法。 细细数来&#xff0c;写博客已经有近3的历史了&#xff0c;这成为了一项已经陪伴我大半人生的工作&#xff1b;也…

二十章总结

一.线程简介 二.创建线程 1.继承Thread类 Thread类中常用的两个构造方法如下&#xff1a; public Thread():创建一个新的线程对象。 public Thread(String threadName):创建一个名称为threadName的线程对象。 继承Thread类创建一个新的线程的语法如下&#xff1a; p…

设计模式 简单工厂 工厂方法模式 抽象工厂模式

工厂模式介绍 工厂模式是我们最常用的实例化对象模式了&#xff0c;是用工厂方法代替new操作的一种模式。它是创建型模式。 简单工厂 简单工厂模式是指由一个工厂对象决定创建出哪一种产品类的实例, 但它不属于GOF 23种设计模式 简单工厂适用于工厂类负责创建的对象较少的场景,…

如何在Go中并发运行多个函数

引言 Go语言的一个流行特性是它对并发的一流支持,即一个程序可以同时做多件事。随着计算机从更快地运行单个代码流转向同时运行更多代码流,能够并发地运行代码正在成为编程的重要组成部分。为了让程序运行得更快,程序员需要把程序设计成并发运行,这样程序中并发的每一部分…

mmpose 使用笔记

目录 自己整理的可以跑通的代码&#xff1a; 图片demo&#xff1a; 检测加关键点 自己整理的可以跑通的代码&#xff1a; 最强姿态模型 mmpose 使用实例-CSDN博客 图片demo&#xff1a; python demo/image_demo.py \tests/data/coco/000000000785.jpg \configs/body_2d_k…

AE-制作绚丽的图形通道

目录 1.新建合成命名为四边形 2.在合成中新建纯色层命名为tao 3.在纯色层上添加RG Trapcode –>Tao 效果&#xff0c;设置Segment参数 4. 在合成中添加摄像机 5.设置Tao Repeat Paths 相关参数&#xff0c;并调整摄像机的位置 6.设置Tao的 Material & Lighting 等…