数据结构之位图和布隆过滤器

系列文章目录

数据结构之ArrayList_arraylist o(1) o(n)-CSDN博客

数据结构之LinkedList-CSDN博客

数据结构之栈_栈有什么方法-CSDN博客

数据结构之队列-CSDN博客

数据结构之二叉树-CSDN博客

数据结构之优先级队列-CSDN博客

常见的排序方法-CSDN博客

数据结构之Map和Set-CSDN博客

数据结构之二叉平衡树-CSDN博客


目录

系列文章目录

前言

一、位图

1. 位图的概念

2. 位图的模拟实现

3. 位图的应用

二、布隆过滤器

1. 布隆过滤器的概念

2. 布隆过滤器的模拟实现

3. 布隆过滤器误判

4. 布隆过滤器和哈希表对比

5. 布隆过滤器的应用

三、海量数据问题的解决思路

问题 1:

问题 2:

问题 3:

问题 4: 

问题 5:

问题 6:

四、一致性哈希

1. 普通的哈希算法

2. 一致性哈希算法

五、哈希与加密


前言

本文介绍了两种高效处理海量数据的数据结构:位图和布隆过滤器。位图通过二进制位存储状态,适合整数查询,40亿数据仅需500M空间。布隆过滤器扩展到位图存储字符串,通过多个哈希函数减少冲突,但存在误判可能。文章还讨论了海量数据问题的解决思路,包括哈希切割、双位图统计等,并对比了一致性哈希与普通哈希的区别。最后区分了哈希算法(不可逆)与加密算法(可逆)的特性。这些数据结构和技术特别适用于需要高效查询和节省内存的大数据场景。


一、位图

1. 位图的概念

位图是利用某个位存放某种状态的数据结构,例如可以用一个字节中的 8 个位,用来表示 0 ~ 7 这 8 个数字是否存在,如果第 i 位是 1,表示 i 存在,是 0 表示不存在;

2. 位图的模拟实现

elem 定义一个 byte[] 数组,用来存放某个数字是否在位图中存在;

usedSize 表示存放数字的个数;

MyBitSet() 构造方法,默认开辟的空间为 1 个字节;

set(int val): void 将 val 存放到位图中;

思路:将位图中的第 val 个位置 1;

定义 byteIndex 表示第几个字节,bitIndex 表示第几个位;例如存放数字 12 到位图中,就需要将第 1 字节的第 4 位置 1;

存放数据是要注意是否存满,满了存放之前要扩容;存放完成后,usedSize++;

get(int val): boolean 表示 val 是否在位图中存在,存在返回 true,不存在返回 false;

reset(int val): void 将 val 在位图中删除,删除完成后 usedSize--;

getUsedSize():返回存放元素的个数;

public class MyBitSet {public byte[] elem;private int usedSize;public MyBitSet(){this.elem = new byte[1];}public MyBitSet(int n){this.elem = new byte[n / 8 + 1];}public void set(int val){if(val < 0){throw new PosOutOfBoundsException();}int byteIndex = val / 8;int bitIndex = val % 8;if(byteIndex >= this.elem.length){this.elem = Arrays.copyOf(this.elem, byteIndex + 1);}this.elem[byteIndex] |= (1 << bitIndex);this.usedSize++;}public boolean get(int val){if(val < 0 || val / 8 >= this.elem.length){throw new PosOutOfBoundsException();}int byteIndex = val / 8;int bitIndex = val % 8;if(((this.elem[byteIndex] >> bitIndex) & 1) == 1){return true;}return false;}public void reset(int val){if(val < 0 || val / 8 >= this.elem.length){throw new PosOutOfBoundsException();}int byteIndex = val / 8;int bitIndex = val % 8;this.elem[byteIndex] &= ~(1 << bitIndex);this.usedSize--;}public int getUsedSize(){return this.usedSize;}
}

3. 位图的应用

位图适合海量数据的查询:

假设有 40 亿个不重复的整数,并且没有进行过排序,如何判断某个数是否在这 40 亿个数中?

通常使用遍历一遍进行查找的时间复杂度是 O(N);使用排序加二分的查找方式,时间复杂度是 O(N*logN) + O(logN);

如果使用位图,将这 40 亿个整数都放在位图当中,查询某个整数的时间复杂度只是 O(1),因为只需要判断该整数对应的位是否被置 1 即可;

位图的优势:存放 40 亿个整数大于需要 16G,通常来说内存是存不下的,如果使用位图,500M就能存下,因此位图更适用于海量数据的查询;

位图的缺点:位图只能存放整数,适用于数据无重复的场景;

二、布隆过滤器

1. 布隆过滤器的概念

布隆过滤器利用哈希函数,可以将字符串转化为某个整数,再存放在位图里,解决了位图不能存放字符串的问题,更适合实际应用;它是一种紧凑的,概率型数据结构,可以进行高效的插入和查询;

2. 布隆过滤器的模拟实现

DEFAULT_SIZE 默认开辟空间的大小;

bitSet 位图,用于存放通过哈希函数生成的整数;

usedSize 存放元素的个数;

seeds 随机种子,用于不同的哈希函数生成不同的哈希算法;

simpleHashes 简单的哈希函数;

MyBloomFilter() 构造方法,定义一个布隆过滤器,开辟默认大小的空间,生成多个不同的哈希函数;

add(String val): void 在布隆过滤器中存放元素;

思路:针对同一个字符串,利用不同的哈希函数,生成多个不同的整数,都存放再位图当中;

contains(String val): boolean 判断布隆过滤器中是否包含 val,包含返回 true,不包含返回 false;

思路:对同一个字符串,利用不同的哈希函数,生成多个不同的整数,在位图中检查这些整数对应的位是否被置 1;

public class MyBloomFilter {public static final int DEFAULT_SIZE = 1 << 20;// 位图public BitSet bitSet;public int usedSize;public static final int[] seeds = {5, 7, 11, 13, 27, 33};public SimpleHash[] simpleHashes;public MyBloomFilter(){bitSet = new BitSet(DEFAULT_SIZE);simpleHashes = new SimpleHash[seeds.length];for(int i = 0; i < seeds.length; i++){simpleHashes[i] = new SimpleHash(DEFAULT_SIZE, seeds[i]);}}public void add(String val){for(int i = 0; i < simpleHashes.length; i++){int index = simpleHashes[i].hash(val);bitSet.set(index);}this.usedSize++;}public boolean contains(String val){for(int i = 0; i < simpleHashes.length; i++){int index = simpleHashes[i].hash(val);if(!bitSet.get(index)) return false;}return true;}
}class SimpleHash{public int cap;public int seed;public SimpleHash(int cap, int seed){this.cap = cap;this.seed = seed;}public int hash(String key){int h;// (n - 1) & hashreturn (key == null) ? 0 : (seed * (cap - 1)) & ((h = key.hashCode()) ^ (h >>> 16));}
}

3. 布隆过滤器误判

布隆过滤器中存在多个哈希函数,但仍然会有一定的概率多个哈希函数针对不同的字符串 s1 和 s2计算出了相同的哈希值,如果容器里面存放了 s1,进行查询的时候就会误以为也存了 s2;

因此布隆过滤器确定某个元素不存在时,该元素一定不存在;确定某个元素存在时,该元素只是可能存在,并不一定存在,因为会存在误判的情况;

4. 布隆过滤器和哈希表对比

哈希表查询速度很快,但是对空间的利用率并不高,通常只有 50%;当在海量数据的应用场景下,哈希表存储效率的问题就会显现出来;

而布隆过滤器使用哈希加位图的思想,通过不同的哈希函数,计算出不同对象的哈希值,在位图当中存储,既解决了哈希表空间利用率低的问题,又保证了查询效率,但是它会存在误判的情况,在准确性方面是不如哈希表的。

5. 布隆过滤器的应用

布隆过滤器是在哈希和位图的基础上实现的,因此布隆过滤器也适用于海量数据的查询,并且占用的内存较小;

布隆过滤器的优点:

  • 查询元素的时间复杂度为 O(k),k 为哈希函数的个数;
  • 哈希函数之间没有关系,方便硬件并行运算;
  • 布隆过滤器不需要存储元素本身,在对保密要求比较严格的场合有很大优势;
  • 在能够承受一定的误判率时,布隆过滤器比其他的数据结构有很大的空间优势;

布隆过滤器的缺点:

  • 布隆过滤器会存在误判的情况,因为哈希函数针对不同的字符串有可能计算出相同的哈希值,即哈希冲突;
  • 布隆过滤器不能获取元素本身;
  • 一般情况不能从布隆过滤器中删除元素;

google 的 guava 包中有对布隆过滤器的实现;

布隆过滤器可用于网页爬虫时,对 URL 进行去重,避免重复爬相同的 URL;

垃圾邮件过滤,可以从十几亿个垃圾邮件地址判断某邮箱是否是垃圾邮箱;

三、海量数据问题的解决思路

问题 1:

假设有一个超过 100G 大小的 log file,log 中存放这 IP地址,如何找到出现次数最多的 IP 地址,及如何找到出现频率前 k 的IP地址? 

解法一:哈希表

如果不考虑占用空间的大小,可以使用哈希表来统计每一个 IP 的数量,找到出现次数最多的即可;

解法二:哈希切割

但 100G 的数据是无法一次性加载到内存当中,需要将 100G 的数据分割成若干个小文件;分别统计每一个文件中出现次数最多的 IP 地址及次数,找出出现次数前 k 个 IP 地址即可;

注意:分割不能采用均分方式,因为一个小文件中出现最多次数的 IP 不一定是所有 IP 中出现次数最多的IP;

分割文件采用哈希切割的方式,即对不同的 IP 计算哈希值,哈希值相同的 IP 存放到一个文件当中,读取每一个文件,统计每个 IP 出现的次数,找到前 k 个即可;


问题 2:

给定 100 亿个整数,设计算法找到其中只出现一次的数字;

解法一:哈希切割

使用哈希切割的方式,分成若干个小文件,计算所有数字的哈希值,将具有相同哈希值的数字放到一个文件中,读取每一个文件,找到只出现一次的数字;

解法二:两个位图

使用两个位图 bitSet1 和 bitSet2,遍历 100 亿整数:

出现一次就将 bitSet1 中的对应位置 1;

出现两次则将 bitSet2 中的对应位置 1,bitSet1 中的对应位置 0;

出现三次或者三次以上,就将 bitSet1 和 bitSet2 中的对应位都置 1;

遍历位图,找到只出现一次的数字。

解法三:一个位图

使用位图中连续的两个位来表示出现的次数,00 表示没出现,01 表示出现一次,10 表示出现两次,11 表示出现三次及三次以上;

遍历位图,找到出现一次的整数;

在位图中存放时:

字节序号:byteIndex = val / 4;位序号:bitIndex = (val % 4)* 2;


问题 3:

给两个文件,每个文件有 100 亿个整数,只有 1G 内存,如何找到两个文件的交集?

解法一:哈希切割

遍历第一个文件,将所有的整数都通过哈希计算,具有相同哈希值的数字,都放到一个文件当中,形成若干个小文件,用 A[i] 表示;

遍历第二个文件,将所有的整数都通过哈希计算,具有相同哈希值的数字,都放到一个文件当中,形成若干个小文件,用 B[i] 表示;

将 A[i] 和 B[i] 缓存到内存中,找到相同的数字,都存放到结果文件中;

最终遍历完所有的小文件,得到的就是结果文件就是交集; 

解法二:位图

创建一个位图 bitSet,遍历第一个文件,将第一个文件的整数都放到 bitSet 中;

遍历第二个文件,并在 bitSet 中查找,找到的所有数据就是两个文件的交集;


衍生问题:如果求上述问题中数据的并集和差集?

创建一个位图 bitSet1,遍历第一个文件,将第一个文件的所有的数字都放到 bitSet1 中;

创建一个位图 bitSet2,遍历第二个文件,将第二个文件的所有的数字都放到 bitSet2 中;

遍历位图,将第一个位图和第二个位图的每一位进行按位或操作,得到的新的位图 bitSet3;

遍历 bitSet3,将位图中的数字都存到新的文件当中,新的文件就是并集;

如果进行按位与操作,得到的数据就是交集;

如果进行按位异或操作,得到的数据就是差集;


问题 4: 

一个文件有 100 亿个整数,现有 1G 内存,设计算法找到出现不超过两次的所有整数;

解法一:哈希切割

遍历文件,将所有的整数都通过哈希计算,具有相同哈希值的数字,都放到一个文件当中,形成若干个小文件;

遍历每一个小文件,统计所有数字的出现次数,找出出现次数不超过两次的整数;

解法二:两个位图

创建两个位图 bitSet1 和 bitSet2:

出现一次就将 bitSet1 中的对应位置 1;

出现两次则将 bitSet2 中的对应位置 1,bitSet1 中的对应位置 0;

出现三次或者三次以上,就将 bitSet1 和 bitSet2 中的对应位都置 1;

遍历位图,找到只出现一次和出现两次的数字。


问题 5:

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

精确算法:哈希切割

遍历第一个文件,使用哈希函数计算每一个 query 对应的哈希值,相同哈希值的 query 放到同一个文件中,形成若干个小文件 A[i];

遍历第二个文件,使用哈希函数计算每一个 query 对应的哈希值,相同哈希值的 query 放到同一个文件中,形成若干个小文件 B[i];

分别求 A[i] 和 B[i] 的交集,得到所有的交集的并集就是两个大文件的交集;

近似算法:布隆过滤器

遍历第一个文件,将所有的 query 都映射到布隆过滤器中,读取第二个文件,去布隆过滤器中查找,得到的所有 query 就是交集,但有可能会出现误判;


问题 6:

如何扩展布隆过滤器,使它能够支持删除操作?

将布隆过滤器的每个位都扩展成一个小的计数器,插入元素时给 k 个计数器加 1,删除元素时给每个计数器减 1;

但是当哈希冲突多了,计数器有可能溢出;

四、一致性哈希

1. 普通的哈希算法

假设有一张图片需要缓存到服务器上,如果有三台服务器,通过哈希函数求到图片对应的哈希值,哈希值 % 3 就找到了对应的服务器,将图片缓存即可。“哈希值 % 服务器数量”的这种算法,就是普通的哈希算法;

普通哈希算法的缺陷:

如果新增服务器,或者某台服务器损坏,服务器的数量就会发生改变;

如果哈希函数不变,同一张图片算出来的哈希值就会改变,那么所有的缓存就会失效,造成缓存的雪崩;

应用获取不到这些图片,就会向后端发送请求,大量的请求会带给服务器巨大的压力,有可能导致服务器宕机。

2. 一致性哈希算法

一致性哈希算法:使用服务器的 IP 或者编号,主机名等 % 2^32;

一致性哈希算法将整个哈希值空间按照顺时针方向组织成一个虚拟的圆环,称为 Hash 环;

将各个主机使用哈希函数进行哈希,确定主机在哈希环上的位置;

将图片使用同样的哈希函数进行哈希,确定图片在哈希环上的位置;因为图片不一定正好和主机在同一个位置,因此需要将图片存到它顺时针旋转遇到的第一台服务器;

优点:即使出现服务器宕机,也会只有一部分图片需要缓存到下一台服务器,不会出现大量缓存雪崩;也不会向后端发送大量的请求,带给服务器巨大压力;

缺点:如果哈希环出现倾斜(服务器分布不均匀),大部分数据缓存在某一台服务器上,可能导致这台服务器宕机,之后所有图片再缓存到下一台服务器,再造成下一台服务器宕机,最终会导致服务器集群宕机;

解决方法:使用服务器虚拟节点,每个服务器创建多个虚拟节点,均匀分布在哈希环上,这样图片缓存就会均匀分布在多态服务器上;

五、哈希与加密

哈希算法往往设计成输出相同长度的哈希值,例如 MD5 算法,是不可逆的;

加密算法输出的加密数据往往和输入数据的长度成正比,不一定都是长度相同的,是可逆的;

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

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

相关文章

Web攻防-PHP反序列化魔术方法触发条件POP链构造变量属性修改黑白盒角度

知识点&#xff1a; 1.WEB攻防-PHP反序列化-序列化和反序列化 2.WEB攻防-PHP反序列化-常见魔术方法触发规则 3.WEB攻防-PHP反序列化-反序列化漏洞产生原因 4.WEB攻防-PHP反序列化-黑白盒&POP链构造 一、演示案例-WEB攻防-PHP反序列化-序列化和反序列化 什么是反序列化操作…

C# VB.NET多进程-管道通信,命名管道(Named Pipes)

要向已运行的进程发送特定命令&#xff08;如/exit&#xff09;&#xff0c;而不是启动新进程&#xff0c;需要使用进程间通信&#xff08;IPC&#xff09;机制。以下是几种常见的实现方法&#xff1a;一、使用命名管道&#xff08;Named Pipes&#xff09;如果ABC.EXE支持通过…

C++ 右值引用 (Rvalue References)

右值引用是C11引入的革命性特性&#xff0c;它彻底改变了C中资源管理和参数传递的方式。下面我将从多个维度深入讲解右值引用。一、核心概念1. 值类别(Value Categories)lvalue (左值): 有标识符、可取地址的表达式int x 10; // x是左值 int* p &x; // 可以取地址rvalue…

反激变换器设计全流程(一)——电路拓扑及工作流程

一、电路拓扑原理 拓扑结构概述 开关反激电源采用反激式拓扑结构&#xff0c;主要由开关管&#xff08;通常为 MOSFET&#xff09;、变压器、输出整流二极管、输出滤波电容以及控制电路等组成。其基本工作原理是通过开关管的周期性开关动作&#xff0c;将输入直流电压转换为高…

uniapp语音播报天气预报微信小程序

1.产品展示2.页面功能(1)点击上方按钮实现语音播报4天天气情况。3.uniapp代码<template><view class"container"><view class"header"><text class"place">地址:{{city}}</text><text class"time"&g…

Pycharm 报错 Environment location directory is not empty 如何解决

好长时间不看不写代码了&#xff0c;人也跟着犯糊涂。今天在Pycharm 导入虚拟环境时&#xff0c;一直报错&#xff1a;“Environment location directory is not empty”&#xff0c;在网上百度很多很多方法都无法解决&#xff0c;直到我翻出我之前自己写的导入虚拟环境的详细过…

React强大且灵活hooks库——ahooks入门实践之场景类(scene)hook详解

什么是 ahooks&#xff1f; ahooks 是一个 React Hooks 库&#xff0c;提供了大量实用的自定义 hooks&#xff0c;帮助开发者更高效地构建 React 应用。其中场景类 hooks 是 ahooks 的一个重要分类&#xff0c;专门针对特定业务场景提供解决方案。 安装 ahooks npm install …

大模型之Langchain篇(二)——RAG

写在前面 跟着楼兰老师学习【LangChain教程】2025吃透LangChain框架快速上手与深度实战&#xff0c;全程干货无废话&#xff0c;三天学完&#xff0c;让你少走百分之99弯路&#xff01;_哔哩哔哩_bilibili 计算相似度 一般用的余弦相似度&#xff0c;这里只是演示计算。 fr…

深入理解图像二值化:从静态图像到视频流实时处理

一、引言&#xff1a;图像分析&#xff0c;从“黑与白”开始在计算机视觉任务中&#xff0c;**图像二值化&#xff08;Image Binarization&#xff09;**是最基础也是最关键的图像预处理技术之一。它通过将灰度图像中每个像素转换为两个离散值&#xff08;通常是0和255&#xf…

云蝠智能 VoiceAgent重构企业呼入场景服务范式

在数字化转型浪潮中&#xff0c;企业呼入场景面临客户服务需求激增与人力成本攀升的双重挑战。传统呼叫中心日均处理仅 300-500 通电话&#xff0c;人力成本占比超 60%&#xff0c;且服务质量受情绪波动影响显著。云蝠智能推出的 VoiceAgent 语音智能体&#xff0c;通过全栈自研…

java进阶(一)+学习笔记

1.JAVA设计模式1.1 什么是设计模式设计模式是软件开发过程中前辈们在长期实践中针对重复出现的问题总结出来的最佳解决方案。这些模式不是具体的代码实现&#xff0c;而是经过验证的、可重用的设计思想&#xff0c;能够帮助开发者更高效地解决特定类型的问题。设计模式的重要性…

Pandas-数据清洗与处理

Pandas-数据清洗与处理一、数据清洗的核心目标二、缺失值处理1. 缺失值检测2. 缺失值处理策略&#xff08;1&#xff09;删除法&#xff08;2&#xff09;填充法三、异常值识别与处理1. 异常值检测方法&#xff08;1&#xff09;统计法&#xff08;2&#xff09;业务规则法2. 异…

在 MacOS 上安装和配置 Kafka

消息代理是一种软件&#xff0c;充当在不同应用程序之间发送消息的中介。它的功能类似于服务器&#xff0c;从一个应用程序&#xff08;称为生产者&#xff09;接收消息&#xff0c;并将其路由到一个或多个其他应用程序&#xff08;称为消费者&#xff09;。消息代理的主要目的…

基于Leaflet调用天地图在线API的多层级地名检索实战

目录 前言 一、天地图在线检索 1、在线检索功能 2、再谈后后接口 二、Leaflet多层级实现实例 1、层级调用实现原理 2、Leaflet中多层级调用 3、成果展示 三、总结 前言 “地图是世界的索引&#xff0c;而地名则是索引中的索引。”当互联网地图进入 Web 2.0 时代&#x…

基于Prompt结构的语校解析:3H日本语学校信息建模实录(4/500)

基于Prompt结构的语校解析&#xff1a;3H日本语学校信息建模实录&#xff08;4/500&#xff09; 系列延续&#xff1a;500所日本语言学校结构数据工程 关键词&#xff1a;招生结构、JLPTEJU、国籍比例、认定校、Prompt训练集 一、我们在构建什么样的语言学校语料&#xff1f; …

Leaflet面试题及答案(61-80)

查看本专栏目录 文章目录 🟢 面试问题及答案(61-80)61. 如何在地图上显示一个动态更新的图层?62. 如何实现地图上的热力图(Heatmap)?63. 如何自定义地图控件的位置?64. 如何处理地图加载失败的情况?65. 如何实现地图的离线功能?66. 如何将地图导出为图片?67. 如何实…

MIG_IP核的时钟系统

MIG_IP核的时钟系统时钟的种类和配置时钟的种类和配置 整体框图 DDR_PHY_CLK&#xff1a;DDR3的工作频率&#xff0c;用来得到想要的线速率。假设此时钟为800M&#xff0c;那么DDR双沿采样&#xff0c;线速率为1600Mbit&#xff1b; UI_CLK&#xff1a;DDR_PHY_CLK的四分之一…

若依框架集成阿里云OSS实现文件上传优化

背景介绍 在若依框架目前的实现中&#xff0c;是把图片存储到了服务器本地的目录&#xff0c;通过服务进行访问&#xff0c;这样做存储的是比较省事&#xff0c;但是缺点也有很多&#xff1a; 硬件与网络要求&#xff1a;服务器通常需要高性能的硬件和稳定的网络环境&#xff0…

Mac如何连接惠普M126a打印机(教程篇)

这里写自定义目录标题Mac如何连接惠普M126a打印机&#xff08;教程篇&#xff09;教程配置如下&#xff1a;Mac如何连接惠普M126a打印机&#xff08;教程篇&#xff09; 惠普M126a连接Mac&#xff08;教程篇&#xff09; 教程配置如下&#xff1a; 首先&#xff0c;先获取与HP打…

感恩日记:记录生活中的美好时刻

感恩日记的landing page登录注册填写感恩事项私信可以体验一下