高效查找的秘密武器二:布隆过滤器

最近学了这个布隆过滤器,所以小编来分享下这个神奇的数据结构

引入:


在我们日常生活中,当然这里特指是编程中时,经常遇到要判断一个元素是否在集合中,比如判断一个单词/词语,是否在已知的字典中;在网络爬虫中,一个网址是否被访问过,最直接的方法就是说将集合中的元素全部放入到计算机中,遇到一个新网址的时候,将它和集合中的元素直接比较即可。

那么,这些情况,一般是使用哈希表来进行存储,毕竟它的好处就是快速准确地查询,缺点嘛,就是浪费存储空间。而且,这里还是涉及一个存储效率的问题,这个哈希表存储集合小的时候,不明显,一旦变得很大了,那么存储效率问题就上来了。

比如说,我们日常使用的邮件一般都具有垃圾邮件过滤功能,比如使用较为广泛的Gmail

如若我们像上述那样记录哪些发来的垃圾邮件地址,然后发送者们又不停创建,我们又要接着存储,一旦达到一个量级,比如几十亿那样的,这些数据放到普通电脑是行不通的,只能放到大量的网络服务器,

此时,Email一般是转换成一个八个字节的信息指纹,加上哈希表存储效率一般只有50%,那么又要16个字节存储这个信息指纹,一亿个,大约需要1.6G

几十亿个,大约需要上百G的内存,除非是超级计算机,否则一般的服务器是存储不下的。

所以我们需要一个新的数据结构来解决下这个问题。

那么就是今天要分享的布隆过滤器。

布隆过滤器,是由布隆在1970年提出的一种紧凑型,比较巧妙的概率型数据结构,其特点就是高效的插入和查找,它可以告诉你一样东西一定不存在,或者可能存在,它是由多个哈希函数通过映射关系到位图中的,大大的节省了存储空间。

那么刚刚讲到,为什么是它可以告诉你一样东西一定不存在,或者可能存在呢?

举个例子:

上述的例子是演示下,是如何存储的,其实是和之前的位图是有些类似的

那么再下来是告诉下是怎么出现可能存在的

就比如这里,这里给到的字符串:百度、字节

然后通过一些哈希函数,确实是出现了重叠,那么此时呢,我们的确也是判断不了百度或者字节是否是一定存在,毕竟它们的对应到的比特位都为1,肉眼看去是这样的,但是呢程序运行起来就带有不确定性了。

所以呢,这个布隆过滤器是带有一定的误判,与之对应的是,误判率也是需要考虑的。

那么接着呢,我们来模拟实现下这个布隆过滤器一些操作

初始化:


那么前面说到,这个要把我们的字符串数据放到位图中,是需要进行哈希的,所以呢,我们要创建几个哈希函数

那么我们单独对这个哈希函数的创建新起一个类,

在这个类里面呢,我们就要进行哈希函数的创建了,那么这个哈希函数呢,又得是不同哈希函数,可不敢一样的,一样的话,那就全部哈希到一个位置了

那么要这个哈希函数的不一样呢,我们可以根据容量和其模拟给定的随机种子来使得最终的哈希函数是不一样的,

class simpleHash{//这个是代表容量,与哈希有关public int cap;//随机值public int seed;public simpleHash(int cap,int seed){this.cap=cap;this.seed=seed;}public int hash(String val){int h;//利用源码中的hashMap来模仿hash值//seed的不同导致哈希函数不一样return (val == null) ? 0 : (seed*(cap-1))&(h = val.hashCode()) ^ (h >>> 16);}
}

直接上代码来理解下吧

这里定义的cap表示容量,seed表示随机种子。

值得说明是,我们这里是模拟实现,不是特别严格要求的,所以呢,随机种子也不是真的随机,是我们随便输入数字的。

那么接着实现了这个hash方法,这个哈希过程呢,是借鉴了Java中HashMap里一些思路

就是通过控制seed的不同,然后呢就可以确定最终的哈希函数返回的哈希值不同。

当然,还没有完的。

接着我们就真正的开始初始化哈希函数了

代码:

public class MyBloom {//给定默认的位图大小public final static int DEFAULT_Size = 1 << 24;public BitSet bitSet;public static final int[] seeds = new int[]{3, 7, 9, 11, 17, 21};public int size;public simpleHash[] simpleHashes;public MyBloom() {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]);}}

结合代码来讲解或许会好些

这个布隆过滤器是用到位图的,所以我们索性直接用标准库中的BitSet了

然后呢,创建一个随机的种子数组,并对其初始化

接着再创建一个随机种子数组,然后对其进行初始化,在MyBloom构造方法中

注意这里的new simpleHash第一个参数是指的是位图中的容量,所以我们一开始设置

public final static int DEFAULT_Size = 1 << 24;是代表位图的容量。

我们直接把 DEFAULT_Size这个作为参数给定就好了,

第二个参数嘛,就是作为随机种子,然后去生成一个哈希值。

这一个个simpleHash对象就放到了simpleHashes[]中

所有的初始化完了,那么接着来写add和contains方法吧

add:

这个方法是设置比特位,使其传入的字符串状态标记为存在
思路:

遍历随机哈希数组,即simpleHashes[],然后调用其hash()返回的值,作为其关键值,再让位图设置对应比特位

注意,这里是六个随机种子,所以有六个哈希函数,所以要遍历六次

代码:

 /*** 添加数据在布隆过滤器中* @param val*/public void add (String val){//利用不同的函数来将值放入到布隆过滤器中for (simpleHash simpleHash : simpleHashes) {int hashVal = simpleHash.hash(val);bitSet.set(hashVal);}}

contains:


这个是查看字符串是否包含在布隆过滤器中

思路:

首先还是得让字符串通过哈希函数,获取一个关键值,然后呢,再让位图的get()方法判断是否是为1

注意add的时候,是六个哈希函数,那么对应的是六个比特位被设置了,所以检查的时候,也要加检查六个比特位是否都是1,但凡有一个不是,那么这个字符串是不存在的

代码:

 public boolean contains (String val){//要知道这个val在哪,还是得拿到一个哈希值先for (simpleHash simpleHash : simpleHashes) {int hashVal = simpleHash.hash(val);boolean flg = bitSet.get(hashVal);if (flg) {return true;}}return false;}

删除:

布隆过滤器中,不能直接支持删除元素,因为删除元素,即是把对应的比特位设置为0,那么某些个比特位也是另外一个元素所以持有的,那么此时就是影响到了其他元素。

不过有这样的一个思路:

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

那么回顾一下,布隆过滤器有什么优缺点吧

优点:

1.增加和查询的复杂度为O(k),(k为哈希函数的个数,一般比较小),与数据量大小无关

2.哈希函数相互之间没有关系,方便硬件并行运算

3.布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势

4.数据量很大时,布隆过滤器可以表示全集,其他数据结构不能

5.使用同一组散列函数的布隆过滤器可以进行交,并,差运算

缺点:

无法确认元素是否真正在布隆过滤器中

最后,简要说明下优点5,如何理解

交集:

交集是指两个集合中同时存在的元素。

假设有两个布隆过滤器,比如A、B

那么A中对应比特位和B中对应的比特位,进行&操作,都为1,意思是一个东西存在两个布隆过滤器中,即交集就是它

并集:

并集是指将两个集合中的所有元素合并,去除重复的元素

假设有两个布隆过滤器,比如A、B

那么A中对应比特位和B中对应的比特位,进行 | 操作,只要A或者B中,存在1,那么表示该位有一个元素存在两者集合中,那么只要保存,其中的结果就是并集了

差集:

差集是指在一个集合中存在,但不在另一个集合中存在的元素。

假设有两个布隆过滤器,比如A、B

那么A中对应比特位和B中对应的比特位,进行 与非 操作,

即先与操作,再取非

此时,如若A中比特位是1,且B中的比特位是2,那么此时说明该元素在A中存在,不在B中。

源码:

class simpleHash{//这个是代表容量,与哈希有关public int cap;//随机值public int seed;public simpleHash(int cap,int seed){this.cap=cap;this.seed=seed;}public int hash(String val){int h;//利用源码中的hashMap来模仿hash值//seed的不同导致哈希函数不一样return (val == null) ? 0 : (seed*(cap-1))&(h = val.hashCode()) ^ (h >>> 16);}
}
public class MyBloom {//给定默认的位图大小public final static int DEFAULT_Size = 1 << 24;public BitSet bitSet;public static final int[] seeds = new int[]{3, 7, 9, 11, 17, 21};public int size;public simpleHash[] simpleHashes;public MyBloom() {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]);}}/*** 添加数据在布隆过滤器中* @param val*/public void add (String val){//利用不同的函数来将值放入到布隆过滤器中for (simpleHash simpleHash : simpleHashes) {int hashVal = simpleHash.hash(val);bitSet.set(hashVal);}}/**** @param val*/public boolean contains (String val){//要知道这个val在哪,还是得拿到一个哈希值先for (simpleHash simpleHash : simpleHashes) {int hashVal = simpleHash.hash(val);boolean flg = bitSet.get(hashVal);if (flg) {return true;}}return false;}
}

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

C++入门终

目录 一、引用 二、内联函数 三、auto关键字 四、指针空值nullptr 一、引用 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0c;编译器不会为引用变量开辟内存空间&#xff0c;它和它引用的变量共用同一块内存空间 类型&引用变量名(对象名)…

C++实现排序算法:冒泡排序

目录 前言 冒泡排序性质 C代码实现冒泡排序 冒泡图解 第一趟排序 第二趟排序 第三趟排序 排序结果 结语 前言 冒泡排序的基本思想是通过从前往后&#xff08;从后往前&#xff09;两两比较&#xff0c;若为逆序&#xff08;即arr[i] < arr[i 1]&#xff09;则交换…

selenium+python实现12306自动化抢火车票(二)

往期回顾&#xff1a; seleniumpython实现12306自动化抢火车票&#xff08;一&#xff09; 1、根据乘车人姓名匹配&#xff0c;支持1人或多人选择 定位出所有乘车人的元素集&#xff0c;根据姓名集合去元素集里循环迭代匹配&#xff0c;匹配上了操作选中 ele_alldriver.find_e…

基于openzeppelin插件的智能合约升级

一、作用以及优点 部署可升级合约&#xff0c;插件自动部署proxy和proxyAdmin合约&#xff0c;帮助管理合约升级和交互&#xff1b;升级已部署合约&#xff0c;通过插件快速升级合约&#xff0c;脚本开发方便快捷&#xff1b;管理代理管理员的权限&#xff0c;只有proxyAdmin的…

游戏引擎学习第36天

仓库 :https://gitee.com/mrxiao_com/2d_game 回顾之前的内容 在这个程序中&#xff0c;目标是通过手动编写代码来从头开始制作一个完整的游戏。整个过程不使用任何库或现成的游戏引擎&#xff0c;这样做的目的是为了能够全面了解游戏执行的每一个细节。开发过程中&#xff0…

MySQL-设置utf8mb4字符集以支持全面的字符显示

本文主要介绍如何通过统一使用utf8mb4字符集来实现在MySQL实例中存储emoji表情的最佳实践。 我们将从客户端、会话连接和MySQL实例等多个方面介绍如何配置和修改字符集以支持utf8mb4。 客户端和会话连接的字符集配置 为了确保能够正确存储和显示emoji表情&#xff0c;我们首…

【Linux从青铜到王者】数据链路层(mac,arp)以及ip分片

局域网通信 通过之前的学习&#xff0c;我们了解了应用层&#xff0c;传输层&#xff0c;网络层的协议和作用&#xff0c;这里先做个总结 应用层——http&#xff0c;https协议&#xff0c;也可以自己定义一套&#xff0c;作用是进行数据的处理传输层——tcp&#xff0c;udp协…

基于STM32的风速风向传感器设计

目录 引言系统设计 硬件设计软件设计系统功能模块 风速采集模块风向采集模块数据处理与显示模块控制算法 风速数据处理算法风向数据处理算法代码实现 风速数据采集与处理风向数据采集与处理数据显示与通信系统调试与优化结论与展望 1. 引言 随着气象监测需求的增加&#xff0…

13.在 Vue 3 中使用OpenLayers加载鹰眼控件示例教程

在 WebGIS 开发中&#xff0c;鹰眼控件 是一个常用的功能&#xff0c;它可以为用户提供当前地图位置的概览&#xff0c;帮助更好地定位和导航。在本文中&#xff0c;我们将基于 Vue 3 的 Composition API 和 OpenLayers&#xff0c;创建一个简单的鹰眼控件示例。 效果预览 在最…

安装certbot(ubuntu系统)

安装nginx 更新软件包列表 sudo apt update 更新软件包列表 sudo apt install nginx 更新软件包列表 sudo systemctl status nginx 注意&#xff1a;强烈推荐使用&#xff0c;系统直接安装nginx&#xff0c;&#xff08;不推荐使用docker安装nginx&#xff09;为后续更简单…

【C语言】C语言的变量和声明系统性讲解

声明和定义的概念 在C语言中&#xff0c;**声明&#xff08;Declaration&#xff09;和定义&#xff08;Definition&#xff09;**是两个重要的基础概念&#xff0c;它们都涉及到变量、函数、结构体等的使用&#xff0c;但功能和作用存在明显区别&#xff1a; 声明&#xff1a…

【Linux】文件的内核级缓冲区、重定向、用户级缓冲区(详解)

一.文件内核级缓冲区 在一个struct file内部还要有一个数据结构-----文件的内核级缓冲区 打开文件&#xff0c;为我们创建struct file&#xff0c;与该文件的所对应的操作表函数指针集合&#xff0c;还要提供一个文件的内核级缓冲区 1.write写入具体操作 当我们去对一个文件写…

MCU、ARM体系结构,单片机基础,单片机操作

计算机基础 计算机的组成 输入设备、输出设备、存储器、运算器、控制器 输入设备&#xff1a;将其他信号转换为计算机可以识别的信号&#xff08;电信号&#xff09;。输出设备&#xff1a;将电信号&#xff08;&#xff10;、&#xff11;&#xff09;转为人或其他设备能理解的…

JDK8新特性之Stream流01

Stream 流介绍 目标 了解集合的处理数据的弊端 理解Stream流的思想和作用 集合处理数据的弊端 当我们需要对集合中的元素进行操作的时候&#xff0c;除了必须的添加&#xff0c;删除&#xff0c;获取外&#xff0c;最典型的就是遍历集合。我们来体验集合操作的弊端&#xff…

【C++】—— map 与 multimap

【C】—— map 与 multimap 1 map1.1 map 和 multimap 参考文档1.2 map 类的介绍1.3 pair 类型介绍1.4 map的构造1.5 map的插入1.5.1 map 的插入方法1.5.2 验证1.5.3 再探pair1.5.4 make_pair 1.6 operator[]1.6.1 样例1.6.2 认识operator[]1.6.3 operator[] 的功能 1.7 map 的…

VTK知识学习(20)- 数据的存储与表达

1、数据的存储 1)、vtkDataArray VTK中的内存分配采用连续内存&#xff0c;可以快速地创建、删除和遍历&#xff0c;称之为数据数组(DataArray)&#xff0c;用类 vtkDataArray 实现。数组数据的访问是基于索引的&#xff0c;从零开始计数。 以 vtkFloatArray 类来说明如何在 …

HCIP-以太网交换安全

端口隔离&#xff1a;实现同一VLAN下的不同用户在二层不能互通&#xff08;可以实现在三层互通&#xff09;&#xff0c;同一个隔离组内是相互隔离的&#xff0c; MAC地址表功能&#xff1a;动态MAC地址表项&#xff0c;接口通告报文中的源MAC地址学习获得&#xff0c;表项可老…

电机功率、电压与电流的换算方法

在电气工程和相关行业中&#xff0c;电机的功率、电压和电流是三个重要的基本参数。它们之间有着密切的关系&#xff0c;而理解这些关系对于电机的选型、设计和应用至关重要。本文将详细阐述这三者之间的换算关系&#xff0c;以及相关公式的应用。 一、电机功率的定义 电机功…

【CKS最新模拟真题】获取多个集群的上下文名称并保存到指定文件中

文章目录 前言一、TASK二、解题过程1、问题一解题2、问题二解题 前言 月底考CKS,这是最新版的CKS模拟题 环境k8s版本ubuntu1.31 一、TASK 题目要求 Solve this question on: ssh cks3477 You have access to multiple clusters from your main terminal through contexts. …

智能合约的离线签名(EIP712协议)解决方案

一、解决核心问题 项目方不支付gas费&#xff0c;由用户自己发起交易&#xff0c;用户支付gas费。用户的数据保存在链下服务器中&#xff0c;token合约在链上&#xff0c;交易是由用户通过网页的DAPP发起。 后台服务、token合约、dapp如何配合工作是本方案的重点 二、总架构…