位图布隆过滤器的原理及实现

目录

位图的概念:

位图的前置知识:位运算

位图的实现:

位图的基本参数和构造方法:

位图的插入:

位图的查找:

位图的删除:

布隆过滤器概念:

布隆过滤器的实现:

布隆过滤器的基本参数:

布隆过滤器的插入:

布隆过滤器的查找:

布隆过滤器的删除:

布隆过滤器优点:

布隆过滤器缺陷:

布隆过滤器使用场景:


前言:

由于布隆过滤器是由位图实现的且这一数据结构相对其他的内容比较少,故这里就把位图和布隆过滤器一起写下。🌸🌸🌸

有需要本文章源码的友友可以前往:位图源码 / 布隆过滤器源码

位图的概念:

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

面试题:

给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。【腾讯】

方法1:遍历,时间复杂度O(N)

主要是内存存储不下这么大的数据,故直接遍历不行。

方法2:排序(O(NlogN)),利用二分查找: logN

同样的内存存储不下。

方法3:位图解决

可以解决👍👍👍:数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比 特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。这样就能极大的利用空间。例如(10亿个字节大概是0.9G,可看做是1G,10亿个比特位大概是119兆,看做128兆 )。

🌸位图的前置知识:位运算

下面位图的实现会经常涉及到位运算,故这里讲解一下下面会用到的位运算,如果友友对着部分已经有所了解的话可以跳过❤️❤️❤️。大致可以分为下面三种情况。

(1)确定二进制表示中第x位是0,还是1

这种情况我们可以将1左移k位,最后将 n & (1 << k)即可根据结果是零还是非零(不能==1,因为每个二进制位的权重不一样😭😭😭,这个非常重要)确定二进制表示中第x位是0,还是1。

公式为: n & (1 << k)

(2)将二进制表示的第x位修改成1

不用想了这张图和上面的一样😎为了整齐换了个色。

图是一样的但是运算方法是不一样的这里是 n | (1 << k) 。

公式为: n | (1 << k)

(3)将二进制表示的第x位修改成0

公式为:n & (~(1 << x))

上面三个公式可以说是位运算最基本的公式了,建议大家一定要理解后记住,有一些面试题就是考这个的希望友友一定要熟悉。

🍓位图的实现:

位图的基本参数和构造方法:

在源码中是利用long类型的数组来实现的。

为了方便叙述我们采用byte类型数组进行叙述,其实都一样的无非就是除数不一样而已。 

没有给定大小的话默认给出1个字节也就是8个比特位,有传入参数的话要给n / 8 + 1,这样可能会导致多给一个字节的情况例如(n == 16)理论只要给两个byte但是这里多给了一个(没有关系的)。

public byte[] elem;//利用byte数组来做private int usedSize;//表示当前位图有多少个数据/*** 默认构造方法给出1个字节。*/public MyBitSet(){elem = new byte[1];}/*** 根据需要创建大小可能会多创建一个字节,但是没有关系。* @param n*/public MyBitSet(int n){elem = new byte[n / 8 + 1];}

位图的插入:

养成好习惯🌸🌸🌸,如果传入一些明显不符合实际情况的值,要抛一个异常。

异常的名字就根据实际情况来写,异常要单独写一个类。

public class negativeException extends RuntimeException{public negativeException(){super();}public negativeException(String message){super(message);}
}

set:位图的插入,传入负数给出异常(我们这里只写无符号整数的情况,如果要实现负数的话,那就要给对应的数,加上值给他映射成正数的情况) 。因为我们是byte数组8个字节所以除以8,如果是long的话就要除以64,除找到在数组中的对应下标,模是找到具体的bit位。

插入元素一定一定要考虑扩容😎,位图具有去重的作用故如果val已经存入的话直接return即可。

至于如何将对应bit位设置成1在上面位运算已经讲的很明白了,这里就不再赘述。

public void set(int val){if(val < 0){throw new negativeException("传入负数异常");}int arrayIndex = val / 8;//计算在byte数组对应下标int bitIndex = val % 8;//计算在byte数的具体哪个bit位。//插入就有可能会越界//要考虑arrayIndex越界的情况。if(arrayIndex + 1 > elem.length){//越界需要扩容elem = Arrays.copyOf(elem,arrayIndex + 1);}if((elem[arrayIndex] & (1 << bitIndex)) != 0){//这个数已经存在return;}else{//这个数不存在//把这个数对应关系存入位图this.elem[arrayIndex] |= (1 << bitIndex);usedSize++;}}

位图的查找:

判断val数是否在位图中,这个简单,唯一注意点就是越界直接return false不用扩容。其他代码里面都有注释了。

/*** 判断val数是否在位图中* @param val* @return*/public boolean get(int val){if(val < 0){throw new negativeException("传入负数异常");}int arrayIndex = val / 8;//计算在byte数组对应下标int bitIndex = val % 8;//计算在byte数的具体哪个bit位。//防止越界if(arrayIndex + 1 > elem.length){return false;}if((elem[arrayIndex] & (1 << bitIndex)) == 0){//不存在return false;}else{//存在return true;}}

位图的删除:

运用位图的前置知识:位运算(3)将二进制表示的第x位修改成0来实现位图的删除,具体代码如下所示:🎉🎉🎉

/*** 将位图中val对应关系删除* @param val*/public void reSet(int val){if(val < 0){throw new negativeException("负数异常");}int arrayIndex = val / 8;//计算在byte数组对应下标int bitIndex = val % 8;//计算在byte数的具体哪个bit位。if(arrayIndex + 1 > elem.length){//超过存储大小必定不存在,不存在直接返回return;}else{//先判断存不存在if((elem[arrayIndex] & (1 << bitIndex)) != 0){//存在elem[arrayIndex] &= (~(1 << bitIndex));usedSize--;}}}

到这我们位图就已经学完了🎉🎉🎉 ,下面我们将开始介绍布隆过滤器,是基于位图实现的。

效果如下: 

 

布隆过滤器提出:

日常生活中,包括在设计计算机软件时,我们经常要判断一个元素是否在一个集合中。比如在字处理软件中,需要检查一个英语单词是否拼写正确(也就是要判断它是否在已知的字典中);在 FBI,一个嫌疑人的名字是否已经在嫌疑名单上;在网络爬虫里,一个网址是否被访问过等等。最直接的方法就是将集合中全部的元素存在计算机中,遇到一个新元素时,将它和集合中的元素直接比较即可。

一般来讲,计算机中的集合是用哈希表(hash table)来存储的。它的好处是快速准确,缺点是费存储空间。当集合比较小时,这个问题不显著,但是当集合巨大时,哈希表存储效率低的问题就显现出来了。

比如说,一个像 Yahoo,Hotmail 和 Gmai 那样的公众电子邮件(email)提供商,总是需要过滤来自发送垃圾邮件的人(spamer)的垃圾邮件。一个办法就是记录下那些发垃圾邮件的 email 地址。由于那些发送者不停地在注册新的地址,全世界少说也有几十亿个发垃圾邮件的地址,将他们都存起来则需要大量的网络服务器。

布隆过滤器概念:

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

实现布隆过滤器需要多个哈希函数,为了方便描述我们下面布隆过滤器的代码实现采用下面的哈希函数:

利用随机种子容量来创建不同的哈希函数(可以不一样,只要能区分开来就行)。

class SimpleHash{private int cap;//容量private int seed;//随机种子public SimpleHash(int cap,int seed){this.cap = cap;this.seed = seed;}/*** 把val字符串变成一个哈希值* @param val* @return*/public int hash(String val){int result = 0;for(int i = 0;i < val.length();i++){result = result * seed + val.charAt(i);}return result & (cap - 1);}
}

🍒布隆过滤器的实现:

布隆过滤器的基本参数:

 我们定义一个容量大小为2^24次方大小的位图,将多个函数设置为数组(SimpleHash[])方便调用,在调用MyBloomFilter类似通过传进构造方法的参数实现对哈希函数的初始化。😎😎😎

private static final int DEFAULT_SIZE = 1 << 24;
private static final int[] seeds = new int[]{5,7,11,13,31,37,61};//随机数
private BitSet bitSet;//位图
private SimpleHash[] hashFun;
public MyBloomFilter(){//创建位图bitSet = new BitSet(DEFAULT_SIZE);hashFun = new SimpleHash[seeds.length];//初始化哈希方法for(int i = 0;i < seeds.length;i++){hashFun[i] = new SimpleHash(DEFAULT_SIZE,seeds[i]);}
}

布隆过滤器的插入:

在位图的插入时是利用多个哈希函数将得到的哈希值对应到位图位置置为1,可能会存在两个不同元素哈希值相同的情况,所以我们说布隆过滤器是比较巧妙的概率型数据结构,它只能表明某样东西一定不存在或者可能存在。

 通过多个哈希函数将对应哈希值映射到位图中。

/*** 将字符串value添加到布隆过滤器* @param value*/public void set(String value){if(value == null){return;}for(SimpleHash fun:hashFun){bitSet.set(fun.hash(value));}}

布隆过滤器的查找:

布隆过滤器的思想是将一个元素用多个哈希函数映射到一个位图中,因此被映射到的位置的比特位一定为1所以可以按照以下方式进行查找:分别计算每个哈希值对应的比特位置存储的是否为零,只要有一个为零, 代表该元素一定不在哈希表中,否则可能在哈希表中。

注意:布隆过滤器如果说某个元素不存在时,该元素一定不存在,如果该元素存在时,该元素可能存在,因为有些哈希函数存在一定的误判

利用位图进行查找。

/*** 判断value元素在不在布隆过滤器中,可能有误判* @param value* @return*/public boolean contains(String value){for(SimpleHash fun:hashFun){int index = fun.hash(value);if(bitSet.get(index) == false){return false;}}//会有误判return true;}

效果如下: 

布隆过滤器的删除:

布隆过滤器不能直接支持删除工作,因为在删除一个元素时,可能会影响其他元素。

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

👍布隆过滤器优点:

(1)增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关。

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

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

(4)在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势。

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

(6)使用同一组散列函数的布隆过滤器可以进行交、并、差运算。

😭布隆过滤器缺陷:

(1)有误判率,即存在假阳性(False Position),即不能准确判断元素是否在集合中(补救方法:再建立一个白名单,存储可能会误判的数据)。

(2)不能获取元素本身。

(3)一般情况下不能从布隆过滤器中删除元素。

(4)如果采用计数方式删除,可能会存在计数回绕问题。

😎布隆过滤器使用场景:

(1)google的guava包中有对Bloom Filter的实现。

(2) 网页爬虫对URL的去重,避免爬去相同的URL地址。

(3)垃圾邮件过滤,从数十亿个垃圾邮件列表中判断某邮箱是否是垃圾邮箱。       

(4)解决数据库缓存击穿,黑客攻击服务器时,会构建大量不存在于缓存中的key向服务器发起请求,在数 据量足够大的时候,频繁的数据库查询会导致挂机。

(5)秒杀系统,查看用户是否重复购买。

总结:布隆过滤器和位图主要是使用在大量数据的情况,布隆过滤器和位图相比布隆过滤器支持存储除了整形之外的数据类型,而位图只能存储整形🎉🎉🎉。

结语:

其实写博客不仅仅是为了教大家,同时这也有利于我巩固知识点,和做一个学习的总结,由于作者水平有限,对文章有任何问题还请指出,非常感谢。如果大家有所收获的话还请不要吝啬你们的点赞收藏和关注,这可以激励我写出更加优秀的文章。

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

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

相关文章

TI-ST论文速读

Domain Adaptation for Medical Image Segmentation Using Transformation-Invariant Self-training 摘要 能够利用未标记数据的模型对于克服不同成像设备和配置中获取的数据集之间的巨大分布差距至关重要。在这方面&#xff0c;基于伪标记的自训练技术已被证明对半监督域适应…

微商商城源码小程序好用么?

商城APP作为电子商务行业的重要组成部分&#xff0c;已经成为了人们购物的主要方式之一。为了在竞争激烈的市场中脱颖而出&#xff0c;开发一款专业且思考深度的商城APP方案显得尤为关键。本文将从专业性和思考深度两个方面&#xff0c;探讨商城APP的开发方案。 一、专业性的重…

HTML+CSS+JS实现京东首页[web课设代码+模块说明+效果图]

系列文章目录 Web前端大作业htmlcss静态页面–掌****有限公司 Web前端大作业起点小说静态页面 Web前端大作业网易云页面 Web前端大作业商城页面 Web前端大作业游戏官网页面 Web前端大作业网上商城页面 HTMLCSS淘宝首页[web课设代码模块说明效果图] 文章目录 系列文章目录前言一…

Java Set基础篇

目录 前言一、常用Set1.1 Set1.1.1 特点 1.2 HashSet1.2.1 特点1.2.2 使用 1.3 TreeSet1.3.1 特点1.3.2 使用 1.4 LinkedHashSet1.4.1 特点1.4.2 使用 二、对比总结 目录 前言 一、常用Set 1.1 Set Set是一个继承自Collection的接口&#xff1a; public interface Set<…

如何在Rust中操作JSON

❝ 越努力&#xff0c;越幸运 ❞ 大家好&#xff0c;我是「柒八九」。一个「专注于前端开发技术/Rust及AI应用知识分享」的Coder。 前言 我们之前在Rust 赋能前端-开发一款属于你的前端脚手架中有过在Rust项目中如何操作JSON。 由于文章篇幅的原因&#xff0c;我们就没详细介绍…

基于SSM的教材管理系统

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM的教材管理系统2拥有两种角色 管理员&#xff1a;教师管理、分类管理、教材管理、入库和出库管理、教材申请管理等 教师&#xff1a;查看教材、申请教材 1.1 背景描述 SSM教材管…

服务器数据恢复—EqualLogic PS6100系列存储数据恢复案例

服务器数据恢复环境&#xff1a; 某品牌EqualLogic PS6100系列存储阵列是一款容错功能较强的存储设备&#xff0c;具有较高的安全性能。一些硬件故障或者误操作也会破坏该系列存储内的数据&#xff0c;下面分享一个北亚企安数据恢复工程师接到的一个关于EQ PS6100存储的数据恢复…

java版数字藏品深色UI仿鲸探数藏盲盒合成短视频卡牌模式支持高并发

Java版数字藏品深色UI仿鲸探数藏盲盒合成短视频卡牌模式支持高并发&#xff0c;是一种结合了Java技术、深色用户界面&#xff08;UI&#xff09;设计、数字藏品概念、盲盒合成玩法以及短视频卡牌模式的综合性应用。该模式旨在为用户提供一种新颖、有趣的数字藏品体验&#xff0…

LC 450.删除二叉搜索树中的节点

450.删除二叉搜索树中的节点 给定一个二叉搜索树的根节点 root 和一个值 key&#xff0c;删除二叉搜索树中的 key 对应的节点&#xff0c;并保证二叉搜索树的性质不变。返回二叉搜索树&#xff08;有可能被更新&#xff09;的根节点的引用。 一般来说&#xff0c;删除节点可分…

解锁CodeGeeX智能问答中3项独有的隐藏技能

CodeGeeX是一款强大的AI编程助手&#xff0c;它能帮你快速解决编程中的各种问题。无论是代码调试、功能实现&#xff0c;还是学习新语言&#xff0c;CodeGeeX都能助你一臂之力。 在IDE中搜索“CodeGeeX”就可以免费下载使用 CodeGeeX在侧边栏提供的Ask CodeGeeX智能问答功能&a…

代码随想录-算法训练营day04【链表02:两两交换链表中的节点、删除链表的倒数第N个节点、链表相交、环形链表II】

代码随想录-035期-算法训练营【博客笔记汇总表】-CSDN博客 第二章 链表part02● day 1 任务以及具体安排&#xff1a;https://docs.qq.com/doc/DUG9UR2ZUc3BjRUdY ● day 2 任务以及具体安排&#xff1a;https://docs.qq.com/doc/DUGRwWXNOVEpyaVpG ● day 3 任务以及具体安排…

C++第十六弹---string使用(上)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1、标准库中的string类 1.1、string类的常用接口说明 1.1.1、string类对象的常见构造 1.1.2、string类对象的容量操作 1.1.3、string类对象的…

网上打印a4纸大概多少钱一张

在快节奏的现代生活中&#xff0c;网上打印服务因其便捷性而受到越来越多用户的青睐。对于许多需要打印大量文件的用户来说&#xff0c;了解网上打印A4纸的价格至关重要。那么&#xff0c;网上打印A4纸300页大概需要多少钱呢&#xff1f; 网上打印价格取决于多个因素&#xff…

Severt和tomcat的使用(补充)

打包程序 在pom.xml中添加上述代码之后打包时会生成war包并且包的名称是test 默认情况打的是jar包.jar里量但是tomcat要求的是war包. war包Tomcat专属的压缩包. war里面不光有.class还有一些tomcat要求的配置文件(web.xml等)还有前端的一些代码(html, css, js) 点击其右边的m…

前端学习<四>JavaScript基础——14-基本包装类型

基本数据类型不能绑定属性和方法 属性和方法只能添加给对象&#xff0c;不能添加给基本数据类型。我们拿字符串来举例。 1、基本数据类型&#xff1a; 基本数据类型string是无法绑定属性和方法的。 var str qianguyihao;​str.aaa 12;console.log(typeof str); //打印结果…

十大经典排序算法及其C语言实现--带动图展示

排序算法大概算起来有以下十种 一、冒泡排序&#xff08;Bubble Sort&#xff09; 冒泡排序&#xff08;Bubble Sort&#xff09;也是一种简单直观的排序算法。它重复地走访过要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果他们的顺序错误就把他们交换过来。走访数…

python统计分析——一般线性回归模型

参考资料&#xff1a;python统计分析【托马斯】 当我想用一个或多个其他的变量预测一个变量的时候&#xff0c;我们可以用线性回归的方法。 例如&#xff0c;当我们寻找给定数据集的最佳拟合线的时候&#xff0c;我们是在寻找让下式的残差平方和最小的参数(k,d)&#xff1a; 其…

基于51单片机的秒表设计—0.01精度99.99s

基于51单片机的秒表设计 &#xff08;仿真&#xff0b;程序&#xff0b;原理图&#xff0b;PCB&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 1.数码管显示秒表&#xff0c;精度为0.01&#xff1b; 2.记时最大为99.99s,过后清零记数。 3.一个按键实现开始…

突破传统,创新营销:淘宝天猫详情接口API的无限可能

随着互联网的迅猛发展&#xff0c;电子商务平台成为了现代商业营销的重要组成部分。淘宝和天猫作为中国最大的电子商务平台之一&#xff0c;拥有庞大的商品数据库和海量的买家资源。为了更好地实现个性化、精准化的营销策略&#xff0c;淘宝天猫推出了详情接口API&#xff0c;联…

Kibana管理ES生命周期

希望通过Kibana界面管理ES索引的生命周期 版本&#xff1a;7.15.2 创建索引模板 创建索引模板方便匹配索引&#xff0c;对匹配到的一批索引采用同一套生命周期管理策略&#xff0c;例如开发环境的所有索引以dev-开头&#xff0c;可以创建样式为dev-*的索引模板以匹配开发环境…