C++进阶:哈希(2)位图与布隆过滤器

目录

  • 1. 位图(bitset)
    • 1.1 引子:海量整形数据的处理
    • 1.2 结构描述
    • 1.3 位图实现
    • 1.4 位图相关题目练习
  • 2. 布隆过滤器(BloomFilter)
    • 2.1 引子:海量非int类型数据处理(string)
    • 2.2 结构描述
    • 3.3 布隆过滤器的实现
    • 3.4 相关练习

1. 位图(bitset)

1.1 引子:海量整形数据的处理

  1. 背景问题:40亿个无序的无符号整数,如何快速判断一个无符号整形是否存在?
  2. 当我们进行大量数据处理时,内存不足以一次性将全部数据读取处理,我们应该如何解决,接下来,我们就来进行相关的学习。

1.2 结构描述

  1. 当数据量较小时,内存能够一次性读取所有数据:
    <1> 排序 + 二分查找
    <2> set + find
  2. 当数据量过大,内存无法一次性读取并处理所有数据,在只需要查找的背景下,我们能否将相关数据状态信息进行压缩,使得内存存储的代价大大降低呢?
  3. 计算机中,可以标识数据状态的最小单位为bit位,而在数据只有整形的情况下,可以直接将每个数字映射一一对应的bit位,物理上,通过开辟一段指定连续内存空间来映射存储,即位级别的哈希表,我们称之为位图。
  4. 位图的方式,可以大大较少需要消耗的内存空间,约42亿整形数据范围经过转换仅需大小512的内存空间。
  5. 开辟空间空间时,我们无法以bit位为单位进行开辟,但可以通过计算机内置类型来间接申请开辟,下面的具体实现我们采用int类型。

在这里插入图片描述

1.3 位图实现

  1. 位图结构
//int类型数据
//unsigned_int类型最大数据范围:UINT_MAN,-1,0x0ffffffff
template<size_t N>//非类型模板参数确定需开辟bit位数量
class bit_set
{
public://构造bit_set(){//N为bit位数量,转换为整形需除32换算//仅除32开辟不够_data.resize(N / 32 + 1, 0);}//bit位置1,非类型模板参数定义的类,类型不带模板参数void set(size_t pos);//bit位置0void reset(size_t pos);//测试某个bit位的状态bool test(size_t pos);private:vector<int> _data;//vector内为动态数组
};
  1. 操作实现
//bit位置1
void set(size_t pos)
{assert(pos <= N);size_t hashi = 1;//第几个数组元素int i = pos / 32;//哪一个bit位int j = pos % 32;//大端机与小端机的数据存储方式不同//但位左移右移的操作并不会因此受影响//编程语言中的左移右移概念,指的是向计算机的高位,低位移动hashi <<= j;_data[i] |= hashi;
}//bit位置0
void reset(size_t pos)
{assert(pos <= N);size_t hashi = 1;int i = pos / 32;int j = pos % 32;hashi = ~(hashi << j);_data[i] &= hashi;
}//测试某个bit为的状态
bool test(size_t pos)
{assert(pos <= N);size_t hashi = 1;int i = pos / 32;int j = pos % 32;hashi <<= j;hashi &= _data[i];return hashi;
}

1.4 位图相关题目练习

  1. 100亿个int类型的数据,查找其中只出现一次的数据
    答:使用两个位图嵌套封装的方式,实现可以使用两个bit位表示多种状态的数据结构,从而来完成筛选。
template<size_t N>
class two_bit_set
{
public://置1void set(size_t pos){if (a.test(pos) == 0 && b.test(pos) == 0)//一次{//0b.set(pos);//1}else if(a.test(pos) == 0 && b.test(pos) == 1)//两次{a.set(pos);//1b.reset(pos);//0}else//三次以上{a.set(pos);//1b.set(pos);//1}}//置0void reset(size_t pos){a.reset(pos);b.reset(pos);}size_t test(size_t pos){if (a.test(pos) == 0 && b.test(pos) == 0){return 0;}else if (a.test(pos) == 0 && b.test(pos) == 1){return 1;}else{return 2;}}private:bit_set<N> a;bit_set<N> b;
};
  1. 现有两个文件,其中分别有100亿个int类型的数据,现内存大小为1G,如何找到两个文件的交集
    答: 将两个文件中的数据分别set入两个位图当中,然后同步遍历两个位图,查找交集
    补充:当数据量较小时,可以使用set容器,去重后,使用去重算法遍历
  1. 现有一个文件,其中有着100亿个int类型的数据,现内存大小为1G,如何找出重复次数不超过两次的数据
    答:位图映射,多个bitset嵌套,表示多种状态
  1. 现有一个文件,有着100亿个int类型的数据,内存大小为512MB,如何找到只出现一次的所有数据
    答:因为内存空间相对来说严重不足,我们无法一次性创建出映射包括所有int范围的位图,所以,我们只能较少位图的大小,将数据切分,分批次处理,一次只处理一定数据范围内的数据。

在这里插入图片描述

2. 布隆过滤器(BloomFilter)

2.1 引子:海量非int类型数据处理(string)

  1. 问题背景: 当存在大量非int类型的数据,诸如,string,内存空间无法容纳处理,有因为非整形数据,也无法直接使用位图映射时,我们该如何对其中的相应数据进行查询

2.2 结构描述

  1. 在之前的学习中,我们学习与简单实现了哈希表这一数据结构,在key映射的操作上,其中对于非int类型数据的处理,采用了哈希函数转换,将非int类型数据转换为int类型,而后再进行映射。
  2. 对于大量非int类型数据的处理,我们也采用哈希函数的方式,转换key值将其存储在位图当中,进行数据信息的压缩,但由于并非1:1直接映射,同时数据量又非常大,所以此种哈希函数的映射方式很大可能存在着误判(将原本不存在的数据,判断为存在)。
  3. 因为哈希函数key值映射的不稳定性,key值大概率可能发生越界情况,所以,对此的处理采用key值越界回绕映射的方式。
  4. 因为存在误判的可能,当只有单个哈希函数时,这种误判的概率是极高的,所以,我们一般采用多个哈希函数共同映射。
  5. 经过查阅资料,得到如下一个的插入数据个数与位图开辟空间大小之间的数学公式,而位图开辟的空间越大,key值得误判率越低。

在这里插入图片描述

  1. 此种非int类型,多哈希函数位图映射的数据结构,我们就称之为布隆过滤器(BloomFilter)
  2. 因为多哈希函数共同映射,每个数据得key值之间可能存在交集,所以布隆过滤器不支持置0删除key值的操作。

在这里插入图片描述

3.3 布隆过滤器的实现

  1. 哈希函数
//BKDR
struct HashBKDR
{size_t operator()(const string& s){size_t hash = 0;for (auto ch : s){hash *= 131;hash += ch;}return hash;}
};//AP
struct HashAP
{size_t operator()(const string& s){size_t hash = 0;for (size_t 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;}
};//DJB
struct HashDJB
{size_t operator()(const string& s){size_t hash = 5381;for (auto ch : s){hash = hash * 33 ^ ch;}return hash;}
};
  1. 具体实现
//非类型模板参数声明先于模板参数
template<size_t M, class K = string, class HashFunc1 = HashBKDR, class HashFunc2 = HashAP, class HashFunc3 = HashDJB>
class BloomFilter
{
public://添加void set(K key){//key值可能越界,需进行回绕size_t key1 = hs1(key) % N;size_t key2 = hs2(key) % N;size_t key3 = hs3(key) % N;bs.set(key1);bs.set(key2);bs.set(key3);}//布隆过滤器不支持删除,可能会同时影响其他值//查询bool test(K key){//匿名对象size_t key1 = HashFunc1()(key) % N;size_t key2 = HashFunc2()(key) % N;size_t key3 = HashFunc3()(key) % N;//存在有误判,不存在无误判if (bs.test(key1) == false)return false;if (bs.test(key2) == false)return false;if (bs.test(key3) == false)return false;return true;}private://非类型模板参数必须是整形家族,且为const修饰static const size_t N = 8 * M;//计算公式,M为插入数据个数,N为开辟空间大小bit_set<N> bs;HashFunc1 hs1;HashFunc2 hs2;HashFunc3 hs3;
};

3.4 相关练习

  1. 两个文件分别有100亿个query(查询请求,可以简单理解为字符串,一个query的大小为50byte),现只有1G内存
    <1> 如何大致找出两个文件的交集
    <2> 如何精确找出两个文件的交集

在这里插入图片描述

  1. 答:
    <1> 将文件至内存足够一次性容纳的大小,分批次读取
    <2> 但哈希切分的方式,并非是按照文件大小来切分,所以导致文件的大小存在不确定性
    <3> 当文件存在有较多相似,相同内容时,哈希切分后还会存在大型文件,内存仍无法容纳
    <4> 位图具有自动去重的特性,当出现大型子文件时,我们可以现进行内容读取,当读取过程中出现异常,那么,就证明文件体积过大的原因并非相同内容堆积所引起,此时,我们只需要再切换另一种哈希函数来进行切分文件,重复上述步骤即可

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

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

相关文章

紫光展锐先进技术科普 | 工业互联网遇到5G,1+1>2?

随着工厂自动化的加速普及&#xff0c;如今我们可能经常看到这样的场景&#xff1a;在高温、潮湿、粉尘、腐蚀等恶劣环境作业场景&#xff0c;巡检机器人穿梭其中&#xff0c;工人们不必弯腰去搬沉重又危险的器件&#xff0c;而旁边会有一个个机械臂帮手平稳有序地完成好所有搬…

性能测试工具—jmeter的基础使用

1.Jmeter三个重要组件 1.1线程组的介绍&#xff1a; 特点&#xff1a; 模拟用户&#xff0c;支持多用户操作多个线程组可以串行执行&#xff0c;也可以并行执行 线程组的分类&#xff1a; setup线程组&#xff1a;前置处理&#xff0c;初始化普通线程组&#xff1a;编写…

Redis:string类型

文章目录 string基本认识重谈set重谈getmset和mgetsetnx/setex/psetexincr系列命令字符串其他操作string的编码方式string的应用场景缓存功能session会话手机验证码 本篇开始总结的是Redis中的string类型 string基本认识 Redis中的string来说&#xff0c;所有的key都是字符串…

Java数据类型:基本数据类型

Java是一种强类型语言&#xff0c;定义变量时&#xff0c;必须指定数据类型。 // 变量必须指定数据类型 private String username;初学者不免有个疑问&#xff1a;在实际编写代码的过程中&#xff0c;该如何选择数据类型呢&#xff1f; 回答这个问题之前&#xff0c;先来解决…

构建智能制造时代的制造行业数据中台总体架构

随着数字化技术的快速发展和智能制造的兴起&#xff0c;制造行业正面临着前所未有的机遇和挑战。在这一背景下&#xff0c;构建制造行业数据中台成为推动制造业转型升级的重要战略举措。本文将深入探讨制造行业数据中台的总体架构&#xff0c;助力制造企业把握数字化转型的主动…

vsCode 设置上下级文件夹目录分离展示?

默认情况下&#xff0c;vsCode目录文件夹会使用/合并展示在一行&#xff0c;这样视觉上看着并不直观&#xff0c;设置目录文件分离展示方法如下&#xff1a; 1、点击左下角设置图标&#xff0c;点击setting&#xff1b; 2、搜索栏输入compact&#xff1b; 3、取消勾选第一个选…

MySQL存储引擎介绍

查看MySQL数据库中创建表的信息 如上图所示&#xff0c;当我们使用命令show create table server_info\G;可以显示我们所创建的表的信息&#xff0c;只显示两个字段&#xff0c;第一个字段是Table 即是创建的表的名称&#xff0c;第二个字段是Create Table即是创建的表的字段的…

Spring Boot集成activiti快速入门Demo

1.什么事activiti&#xff1f; Activiti是一个工作流引擎,可以将业务系统中复杂的业务流程抽取出来,使用专门的建模语言BPMN2.0进行定义,业务流程按照预先定义的流程进行执行,实现了系统的流程流activiti进行管理,减少业务系统由于流程变更进行系统升级改造的工作量,从而提高系…

东方伟大思想家和哲学家颜廷利:有形的财富越与无形的财富

在世界的广阔舞台上&#xff0c;财富常被看作衡量国家与民族成就的重要尺度。然而&#xff0c;在物质和精神的天平上&#xff0c;真正具有深远意义的&#xff0c;往往是后者的累积与扩散。正如东方哲学的现代诠释者、当代中国教育界的杰出教授颜廷利所阐述&#xff0c;“有形的…

【C语言每日题解】输入n(1~9),再输入n个长度不超过50的字符串,给这n个字符串排序并输出它们

&#x1f970;欢迎关注 轻松拿捏C语言系列&#xff0c;来和 小哇 一起进步&#xff01;✊ &#x1f308;感谢大家的阅读、点赞、收藏和关注 解题思路&#xff1a; 首先&#xff1a;使用一个二维字符数组来存储输入的字符串。由于n的范围是1到9&#xff0c;我们可以直接定义一…

实锤,阿里云盾会拦截百度云防护的IP!

今天凌晨&#xff0c;一位站长联系上云加速客服&#xff0c;反馈说&#xff0c;网站突然出现了502的情况。 在检查云防护子域名配置没有问题、本地强制回源没有问题的情况下&#xff0c;我们得出结论是要么服务器内防火墙拦截了云防护的IP段&#xff0c;要么服务器商拦截了云防…

使用make_blobs生成数据并使用KNN机器学习算法进行分类和预测以及可视化

生成数据 使用make_blobs生成数据并使用matplotlib进行可视化 完整代码&#xff1a; from sklearn.datasets import make_blobs # KNN 分类器 from sklearn.neighbors import KNeighborsClassifier # 画图工具 import matplotlib.pyplot as plt # 数据集拆分工具 from sklea…