【C++ ——— 哈希】位图 | 布隆过滤器

文章目录

  • 1、位图
      • 1.1位图概念
  • 2.位图实现
  • 位图的应用
      • 1.一百亿个整数,设计算法找到只出现一次的整数?
      • 2.给两个文件,分别有一百亿个整数,我们只有1G内存该如何找到两个文件的交集?
      • 3.位图应用变形:一个文件有100亿个int,1G内存,设计算法找到出现不超过两次的所有整数。
  • 3.布隆过滤器
      • 3.1 布隆过滤器的提出
        • 3.2布隆过滤器概念
  • 海量数据的面试题
      • 1.给两个文件,分别由100亿个query(数据请求),我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法(近似算法就是布隆过滤器算法):
      • 2.给一个超过100G大小的log file,log 中存放着IP地址,设计算法找到出现次数最多的IP地址?如何找到top K的IP?


1、位图

1.1位图概念

1.面试题
给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。【腾讯】
解决方案:

  1. 二分查找。[不能用这种方式:1.要排序,2.40亿个整数需要约16g的内存,内存开不出这么大的空间]
  2. 位图解决:我们只需要标记这个值在不在,没必要存下来,让每个值映射一个比特位,需要2^32个比特位,也就是只需要0.5G。数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。

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

2.位图实现

//N是需要多少比特位
template<size_t N>
class bitset
{
public://构造bitset(){//构造函数提前开空间//_bits.resize(N/32+1, 0);_bits.resize((N >> 5) + 1, 0);//2^5是32 且移位运算优先级要注意。}//映射一个值到位图中void set(size_t x){//要把第j位处理为1,其他位不变。//找一个第j位为1其他位为0的补码//让他们或运算,或是两个位都为0时才为0size_t i = x / 32;size_t j = x % 32;_bits[i] |= (1 << j);//左移操作符向高位移动//右移操作符向低位移动//注意左移右移操作符不是方向的问题,是高位和低位的问题。}//将x的对应比特位置为0void reset(size_t x){//把第j位处理位0,其他位不变//同上找第j位为0,其他位为1的补码//按位与运算。 与运算两个位都为1才为1size_t i = x / 32;size_t j = x % 32;_bits[i] &= ~(1 << j);}//查看这个x在位图中是否为1,为1就是存在。bool test(size_t x){size_t i = x / 32;size_t j = x % 32;return _bits[i] & (1 << j);}private:vector<int> _bits;
};

我们看上题说要开40亿个整形我们要怎么开呢?

//直接写为16进制
bitset<oxffffffff> bigbs;
//-1传过去转化为size_t类型直接能转化为最大值。
bitset<-1> bigbs2;

位图的应用

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

这有一百亿个数会不会出现空间不够的情况?
不会。就算是一百亿个整数最多也就42亿九千万个整数会出现很多重复值。
不论是100亿还是1000亿都只开42亿九千万个空间
这里需要三种状态:

  1. 出现0次
  2. 出现1次
  3. 出现两次即以上
    可以用两个位表示这三种状态。
    我们用两个位图表示一个位。
    在这里插入图片描述

位图代码实现:

//用两个位图来表示一个值
class twobitset
{
public:void set(size_t x){//00->01//01->10//10->状态不变出现了两次即以上if (_bs1.test(x) == false && _bs2.test(x) == false){_bs2.set(x);}else if (_bs1.test(x) == false && _bs2.test(x) == true){_bs1.set(x);_bs2.reset(x);}}void PrintfOnce(){for (size_t i = 0; i < N; i++){if (_bs1.test(i) == false && _bs2.test(i) == true){cout << i << endl;}}cout << endl;}private:bitset<N> _bs1;bitset<N> _bs2;
};

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

跟上题思路很像,各自映射到一个位图中。一个值两个位图都存在,就是交集。(先去重复值)

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

我们可以用上面的一个值映射两个位图解决这个方法,但是我们只有1G内存所以还是要做一些特殊处理的

template<size_t N>
//用两个位图来表示一个值
class twobitset
{
public:void set(size_t x){//00->01//01->10//10->11//11->状态不变if (_bs1.test(x) == false && _bs2.test(x) == false){_bs2.set(x);}else if (_bs1.test(x) == false && _bs2.test(x) == true){_bs1.set(x);_bs2.reset(x);}else if (_bs1.test(x) == true && _bs2.test(x) == false){_bs1.set(x);_bs2.set(x);}}void Printf(){for (size_t i = 0; i < N; i++){if (_bs1.test(i) == false && _bs2.test(i) == true){cout <<"1->"<< i << endl;}else if(_bs1.test(i) == true && _bs2.test(i) == false){cout << "2->" << i << endl;}}cout << endl;}
private:bitset<N> _bs1;bitset<N> _bs2;
};

3.布隆过滤器

搜索

  1. 暴力排查 数据量大了,效率就低
  2. 排序+二分查找 问题a:排序有代价 问题b:不方便
  3. 搜索树-> AVL树+红黑树
  4. 哈希 ->扩容代价很高
    ——————————————
    以上数据结构,都是以空间换时间、空间消耗很高

数量很大的数据
5、[整形]的在不在及其扩展问题 – 位图及其变形 以时间换空间的算法
6、[其他类型]的在不在呢? --需要用到布隆过滤器

3.1 布隆过滤器的提出

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

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

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

用什么方法能让字符串映射到位图中呢?
我们可以考虑把先把字符串转换为一个对应的整形,不过整形的位图一个整形对应一个位,有42亿多的整型值够我们匹配,但是字符串呢?字符串可是无限的,所以肯定会有不同的字符串映射到同一个整形
不过我们只需要判断这个字符串在不在
以上思路:
判断:在(不准确,可能存在误判不同字符串映射的整形是同一个。本质也就是哈希冲突导致的。)
判断:不在(准确,没有字符串映射肯定就是不在的)

看下图中映射方式:
一个字符串映射三个位置的整形值。会降低误判的概率
布隆过滤器无法完全解决误判,所以布隆过滤器出现的场景就需要接受误判

在这里插入图片描述

布隆过滤器场景:
我们玩游戏创建角色时,需要给角色创建昵称,如果直接拿你输入的昵称和数据库里存的已有昵称进行比较那样效率会很低。
在这里插入图片描述
我们可以在数据库和玩家客户端的中间建立一个布隆过滤器
布隆过滤器只能准确判断不在数据库中的数据。
判断在数据库中的情况时,需要额外去数据库中匹配数据。
在这里插入图片描述
布隆过滤器、代码实现:

struct BKDRHash
{size_t operator()(const string& key){//BKDRsize_t hash = 0;for (auto e : key){hash *= 31;hash += e;}return hash;}
};template<size_t N ,class K = string,class HashFunc1 = BKDRHash,class HashFunc2 = BKDRHash,class HashFunc3 = BKDRHash>
class BloomFilter
{
public:void Set(const K& key){size_t Hash1 = HashFunc1()(key) % N;size_t Hash2 = HashFunc2()(key) % N;size_t Hash3 = HashFunc3()(key) % N;_bs.set(Hash1);_bs.set(Hash2);_bs.set(Hash3);}//一般不支持删除,删除一个值可能会影响其他值//非要支持删除的话,可以使用引用计数法,不过那样会void Reset(const K& key);bool Test(const K& key){size_t Hash1 = HashFunc1()(key) % N;if (_bs.test(Hash1) == false)return false;size_t Hash2 = HashFunc2()(key) % N;if (_bs.test(Hash2) == false)return false;size_t Hash3 = HashFunc3()(key) % N;if (_bs.test(Hash3) == false)return false;//可能存在误判的风险return true;}
private:bitset<N> _bs;
};

海量数据的面试题

布隆过滤器(哈希切分)

1.给两个文件,分别由100亿个query(数据请求),我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法(近似算法就是布隆过滤器算法):

假设:平均一个query是50byte,100亿个query就是5000亿个byte。1G约等于10亿byte
那么5000亿byte 约等于 500G
内存存不下,红黑树/哈希表都使用不了。
分析:
1.我们可以先把两个文件的500G文件分成500份,每份1G左右

在这里插入图片描述
2.但是只是单纯的划分了500份空间对我们的查询比对操作效率,没有任何的提高。所以我们在划分空间时需要用到哈希算法,通过哈希函数把映射位置相同或相似的值,集中在一个小块。
我们做了这么多的目的就是为了把这块空间加载到内存中
下图这种方式就是哈希切分法
在这里插入图片描述
3.之后我们把Ai和Bi的数据分别加载到内存中,对他们两个分别进行位图映射。然后判断他们中是否有交集
在这里插入图片描述
4.但是这种思路还有一个问题–如果单个文件过大呢?
比如这个小文件有10G
这种单个文件过大差不多有两种情况:
一、这个小文件,内大多数都是相同的query
二、这个小文件,有很多种不同的query

解决方法:
Ai和Bi分别插入setA和setB中
不管文件大小直接插入:
情况一:这个小文件大多都是相同query,插入第一个后,后面重复插入就会set失败。
情况二:不断插入set后,内存不足,会抛异常,需要换个哈希函数把这个小文件进行二次切分,再找交集。

2.给一个超过100G大小的log file,log 中存放着IP地址,设计算法找到出现次数最多的IP地址?如何找到top K的IP?

哈希切割
1.我们先要统计次数,但是文件太大加载不到内存,我们无法统计次数,可以用哈希切分把相似IP地址分到一个区域里(跟上题思路类似)(相同IP一定会进入同一个文件)
2.切分完,直接在对应Ai中的数据使用map或者unordered_map统计次数,记得统计完次数和最大值比对进行更新。
3.top K问题:额外建一个小堆如果次数比堆顶大就进堆。

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

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

相关文章

【介绍下运维,什么是运维?】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

Linux 编译安装python

以deepin操作系统安装Python3.8.10为例。 下载 python3.8.10 官网下载 Linux要下载源码&#xff0c;进行编译。 下图tarball即tar包&#xff0c;是压缩包的意思。python官网给出两种压缩格式的tarball&#xff0c;下载哪个都可以。 方式一&#xff1a;直接点击链接下载 方式…

python-模块-网络编程-多任务

一、模块 1-1 Python 自带模块 Json模块 处理json数据 {"key":"value"} json不是字典 本质是一个有引号的字符串数据 json注意点 {} 中的数据是字符串引号必须是双引号 使用json模块可以实现将json转为字典&#xff0c;使用字典的方法操作数据 。 或者将…

Debian和ubuntu 嵌入式的系统的 区别

随着开源操作系统的日益流行&#xff0c;Debian和Ubuntu这两个基于Linux的发行版本成为了众多开发者和系统管理员的首选。它们各自拥有独特的优势和特点&#xff0c;那么&#xff0c;在选择时&#xff0c;哪一个更适合你呢&#xff1f;接下来&#xff0c;我们将深入探讨两者的关…

C++候捷stl-视频笔记2

深度搜索list list是双向链表&#xff1a;底部实现是环状双向链表 list内部除了存data之外&#xff0c;还要存一个前向指针prev和一个后向指针next list的iterator&#xff0c;当迭代器的时候&#xff0c;是从一个节点走到下一个节点&#xff0c;是通过访问next指针实现的 主要…

NFS p.1 服务器的部署以及客户端与服务端的远程挂载

目录 介绍 应用 NFS的工作原理 NFS的使用 步骤 1、两台机子 2、安装 3、配置文件 4、实验 服务端 准备 启动服务&#xff1a; 客户端 准备 步骤 介绍 NFS&#xff08;Network File System&#xff0c;网络文件系统&#xff09;是一种古老的用于在UNIX/Linux主…

新宏观范式和产业趋势下,纷享销客如何助力企业出海?

出海&#xff0c;已不再是企业的“备胎”&#xff0c;而是必须面对的“大考”&#xff01;在这个全球化的大潮中&#xff0c;有的企业乘风破浪&#xff0c;勇攀高峰&#xff0c;也有的企业在异国他乡遭遇了“水土不服”。 面对“要么出海&#xff0c;要么出局”的抉择&#xff…

C++设计模式-策略模式

文章目录 27. 策略模式 运行在VS2022&#xff0c;x86&#xff0c;Debug下。 27. 策略模式 策略模式让算法的选择与使用独立开来&#xff0c;使得代码更灵活、可扩展和易维护。应用&#xff1a;如在游戏开发中&#xff0c;AI角色需要根据环境和条件做出不同的行为&#xff0c;如…

微型导轨在自动化制造中有哪些优势?

微型导轨在自动化制造中发挥重要作用&#xff0c;能够满足自动化设备制造中对精度要求较高的工艺环节。适用于自动装配线、自动检测设备和机器人操作等环节&#xff0c;推动了行业的进步与发展。那么&#xff0c;微型导轨在使用中有哪些优势呢&#xff1f; 1、精度高和稳定性强…

Vue之组件基础(插槽)

在HTML中&#xff0c;开发者可以在双标签内添加一些信息。而在Vue中&#xff0c;组件以标签的形式引用&#xff0c;那么如何在组件的标签内添加一些信息并将信息渲染到页面中呢?其实&#xff0c;Vue 提供了插槽&#xff0c;专门用来实现这样的效果。 一.什么是插槽 Vue为组件…

【会议征稿】2024年无人驾驶与智能传感技术国际学术会议(ADIST 2024)

2024年无人驾驶与智能传感技术国际学术会议&#xff08;ADIST 2024&#xff09;将于2024年6月28-30日在珠海召开。ADIST 2024旨在搭建学术资源共享平台&#xff0c;加强中外学术合作&#xff0c;促进自动驾驶和智能传感技术的发展&#xff0c;促进全球研究人员、开发人员、工程…

react、vue动态form表单

需求在日常开发中反复写form 是一种低效的开发效率&#xff0c;布局而且还不同这就需要我们对其封装 为了简单明了看懂代码&#xff0c;我这里没有组件&#xff0c;都放在一起&#xff0c;简单抽离相信作为大佬的你&#xff0c;可以自己完成&#xff0c; 一、首先我们做动态f…

PostgreSQL的学习心得和知识总结(一百四十四)|深入理解PostgreSQL数据库之sendTuples的实现原理及功能修改

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《PostgreSQL数据库内核分析》 2、参考书籍&#xff1a;《数据库事务处理的艺术&#xff1a;事务管理与并发控制》 3、PostgreSQL数据库仓库…

C# 类型系统

1. 隐式类型 c#允许使用 var 声明变量&#xff0c;编译期会通过初始化语句右侧的表达式推断出变量的类型。 // i is compiled as an int var i 5;// s is compiled as a string var s "Hello";// a is compiled as int[] var a new[] { 0, 1, 2 };// expr is co…

521源码网-免费网络教程-Cloudflare使用加速解析-优化大陆访问速度

Cloudfalre 加速解析是由 心有网络 向中国大陆用户提供的公共优化服务 接入服务节点: cf.13d7s.sit 接入使用方式类似于其它CDN的CNAME接入&#xff0c;可以为中国大陆用户访问Cloudflare网络节点大幅度加速&#xff0c;累计节点130 如何接入使用 Cloudflare 加速解析&#…

【机器学习300问】106、Inception网络结构如何设计的?这么设计的目的是什么?

谷歌的Inception网络&#xff0c;也被称为GoogLeNet&#xff0c;是Google在2014年推出的一种深度卷积神经网络&#xff08;CNN&#xff09;模型&#xff0c;在这之前的AlexNet、VGG等结构都是通过增大网络的深度&#xff08;层数&#xff09;来获得更好的训练效果&#xff0c;但…

阿里云 通过EIP实现VPC下的SNAT以及DNAT

192.168.0.85 有公网地址192.1680.95无公网地址 在192.168.0.85&#xff08;有公网地址服务器上操作&#xff09; #开启端口转发 echo "net.ipv4.ip_forward 1" >> /etc/sysctl.conf sysctl -p#仅允许192.168.0.95 iptables -t nat -I POSTROUTING -s 192.16…

【前缀和 记忆化搜索】LeetCode1444. 切披萨的方案数

本文涉及的基础知识点 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 动态规划 记忆化搜索 LeetCode1444. 切披萨的方案数 给你一个 rows x cols 大小的矩形披萨和一个整数 k &#xff0c;矩形包含两种字符&#xff1a; ‘A’ &#xff…

GEYA格亚GRT8-S1S2间歇性双时间循环继电器时间可调交流220V 24v

品牌 GEYA 型号 GRT8-S2 AC/DC12-240V 产地 中国大陆 颜色分类 GRT8-S1 A220,GRT8-S1 AC/DC12-240V,GRT8-S2 A220,GRT8-S2 AC/DC12-240V GRT8-S&#xff0c;循环延时&#xff0c;时间继电器&#xff1a;LED指示灯&#xff0c;触头容量大&#xff0c;电压超宽&#xff0…

某咨询公司的大数据解决方案介绍(32页PPT)

方案介绍&#xff1a; 本咨询公司的大数据平台解决方案以企业实际需求为出发点&#xff0c;结合先进的大数据技术和行业经验&#xff0c;为企业提供一站式的大数据服务。通过实时数据收集与处理、深度数据分析与挖掘、可视化数据展示以及灵活的数据应用与扩展&#xff0c;帮助…