海量数据处理算法之BloomFilter
算法介绍 Bloom Filter的中文名称叫做布隆过滤器,因为他最早的提出者叫做布隆(Bloom),因而而得此名。布隆过滤器简单的说就是为了检索一个元素是否存在于某个集合当中,以此实现数据的过滤。也许你会想,这还不简单,判断元素是否存在某集合中,遍历集合,
算法介绍
Bloom Filter的中文名称叫做布隆过滤器,因为他最早的提出者叫做布隆(Bloom),因而而得此名。布隆过滤器简单的说就是为了检索一个元素是否存在于某个集合当中,以此实现数据的过滤。也许你会想,这还不简单,判断元素是否存在某集合中,遍历集合,一个个去比较不就能得出结果,当然这没有任何的问题,但是当你面对的是海量数据的时候,在空间和时间上的代价是非常恐怖的,显然需要更好的办法来解决这个问题,而Bloom Filter就是一个不错的算法。具体怎么实现,接着往下看。
Bloom Filter
先来说说传统的元素检索的办法,比如说事先在内存中存储了一堆的url字符数组,然后给定一个指定的url,判断是否存在于之前的集合中,我们肯定是加载整个数组到内存中,然后一个个去比较,假设每条url字符平均所占的量只有几个字节,但是当数据变为海量的时候,也足以撑爆整个内存,这是空间上的一个局限。再者,逐次遍历的方式本身就是一种暴力搜索,搜索的时间将会随着集合容量的本身而线性扩展,一旦数据量变大,查询时间上的开销也是非常恐怖的。针对时间和空间上的问题,Bloom Filter都给出了完美的解决办法。首先第一个空间的问题,原本的数据占用的是字符,在这里我们用1个位占据,也就是说1个元素我用1/8的字节表示,不管你的url长度是10个字符,100字符,统统用一个位表示,所以在这里我们需要能够保证每个字符所代表的位不能冲突。因为用到了位的存储,我们需要对数据进行一个hash映射,以此得到他的位置,然后将此位置上的位置标为1(默认都是为0)。所以说白了,Bloom Filter就是由一个很长的位数组和一些随机的哈希函数构成。位数组你可以想象成下面的这种形式:
你可以想象这个长度非常长,反正1个单位就占据1个位,1k的空间就已经能够表示1024*8=8192位了。所以说内存空间得到了巨大的节约。现在一个问题来了,为什么我刚刚用了一些随机的哈希函数这个词而不是说一个呢,因为会有哈希碰撞,再好的哈希函数也不能保证不会发生哈希冲突,所以这里需要采用多个哈希函数,所以元素是否存在的判断条件就变为了只有所有的哈希函数映射的位置的值都是true的情况下,此元素才是存在于集合中的,这样判断的准确率就会大大提升了,哈希映射之后的效果图如下:
假设我们的程序采用了如上图所示的3个随机独立的哈希函数,1个元素需要进行3次不同的哈希函数的映射算法,对3个位置进行标记,对此元素的误判概率我们做个计算,要使此元素误判,就是说,他的这3个位置都有人占据了,就是说都与别的哈希函数有冲突,这最糟糕的情况就是他的3个映射位置与某个其他的元素通过哈希函数计算完全重叠,假设位空间长度1W位。每个位置被映射的概率就为1/1w,所以最糟糕的情况的冲突概率就是1/1w*1/1w*1/1w=1/10的12次方,如果最大的冲突概率的可能性呢,就是每个位置都与其中的某个哈希函数映射冲突,那误差概率就是叠加的情况1/1w+1/1w+1/1w=0.0003。结果已经非常明显了,通过3个哈希函数就已经能够保证足够低的误判率了,更别说当你用4个,5个哈希函数做映射的情况。下面问题又转移到了我们用什么方式去作为位数组呢,int数组,字符char数组,答案都不是。结果在下面。
BitSet
这个是java中的某个数据类型,C,C++我目前不清楚有没有这样的类,为什么选用这个而不是前面说的int,或char数组,首先int当然不行,1个int本身就有32位,占了4个字节,用他做出0,1的存储显然相当于没省下空间,自然我们就想到了用字符数组char[],在C语言中1个char占一个字节,而在java中由于编码方式的不同,一个char占2个字节,用char做存储也只是稍稍比int介绍了一半的空间,并没有真正的做到一个元素用一个位来表示,后来查了一下,java里面就有内置了BitSet专门就是做位存储的,还能够进行位相关的许多操作,他的操作其实就是和数组一样,也是从0开始的。不熟悉的同学可以自行上网查阅相关资料,其实int数组也可以实现类似的功能,不过自己要做转换,把int当成32位来算,之前我写过相关的文章,是关于位示图法存储大数据。
算法的实现
算法其实非常的简单,我这里用一组少量的数据进行模拟。
输入数据input.txt:
mike study day get last exam think fish he
play mike study day get Axis last exam think fish he
算法的工具类BloomFilterTool.java:
package BloomFilter; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.BitSet; import java.util.HashMap; import java.util.Map; /** * 布隆过滤器算法工具类 * * @author lyq * */ public class BloomFilterTool { // 位数组设置为10w位的长度 public static final int BIT_ARRAY_LENGTH = 100000; // 原始文档地址 private String filePath; // 测试文档地址 private String testFilePath; // 用于存储的位数组,一个单元用1个位存储 private BitSet bitStore; // 原始数据 private ArrayList<String> totalDatas; // 测试的查询数据 private ArrayList<String> queryDatas; public BloomFilterTool(String filePath, String testFilePath) { this.filePath = filePath; this.testFilePath = testFilePath; this.totalDatas = readDataFile(this.filePath); this.queryDatas = readDataFile(this.testFilePath); } /** * 从文件中读取数据 */ public ArrayList<String> readDataFile(String path) { File file = new File(path); ArrayList<String> dataArray = new ArrayList<String>(); try { BufferedReader in = new BufferedReader(new FileReader(file)); String str; String[] tempArray; while ((str = in.readLine()) != null) { tempArray = str.split(" "); for(String word: tempArray){ dataArray.add(word); } } in.close(); } catch (IOException e) { e.getStackTrace(); } return dataArray; } /** * 获取查询总数据 * @return */ public ArrayList<String> getQueryDatas(){ return this.queryDatas; } /** * 用位存储数据 */ private void bitStoreData() { long hashcode = 0; bitStore = new BitSet(BIT_ARRAY_LENGTH); for (String word : totalDatas) { // 对每个词进行3次哈希求值,减少哈希冲突的概率 hashcode = BKDRHash(word); hashcode %= BIT_ARRAY_LENGTH; bitStore.set((int) hashcode, true); hashcode = SDBMHash(word); hashcode %= BIT_ARRAY_LENGTH; bitStore.set((int) hashcode, true); hashcode = DJBHash(word); hashcode %= BIT_ARRAY_LENGTH; bitStore.set((int) hashcode, true); } } /** * 进行数据的查询,判断原数据中是否存在目标查询数据 */ public Map<String, Boolean> queryDatasByBF() { boolean isExist; long hashcode; int pos1; int pos2; int pos3; // 查询词的所属情况图 Map<String, Boolean> word2exist = new HashMap<String, Boolean>(); hashcode = 0; isExist = false; bitStoreData(); for (String word : queryDatas) { isExist = false; hashcode = BKDRHash(word); pos1 = (int) (hashcode % BIT_ARRAY_LENGTH); hashcode = SDBMHash(word); pos2 = (int) (hashcode % BIT_ARRAY_LENGTH); hashcode = DJBHash(word); pos3 = (int) (hashcode % BIT_ARRAY_LENGTH); // 只有在3个哈希位置都存在才算真的存在 if (bitStore.get(pos1) && bitStore.get(pos2) && bitStore.get(pos3)) { isExist = true; } // 将结果存入map word2exist.put(word, isExist); } return word2exist; } /** * 进行数据的查询采用普通的过滤器方式就是,逐个查询 */ public Map<String, Boolean> queryDatasByNF() { boolean isExist = false; // 查询词的所属情况图 Map<String, Boolean> word2exist = new HashMap<String, Boolean>(); // 遍历的方式去查找 for (String qWord : queryDatas) { isExist = false; for (String word : totalDatas) { if (qWord.equals(word)) { isExist = true; break; } } word2exist.put(qWord, isExist); } return word2exist; } /** * BKDR字符哈希算法 * * @param str * @return */ private long BKDRHash(String str) { int seed = 31; /* 31 131 1313 13131 131313 etc.. */ long hash = 0; int i = 0; for (i = 0; i < str.length(); i++) { hash = (hash * seed) + (str.charAt(i)); } hash = Math.abs(hash); return hash; } /** * SDB字符哈希算法 * * @param str * @return */ private long SDBMHash(String str) { long hash = 0; int i = 0; for (i = 0; i < str.length(); i++) { hash = (str.charAt(i)) + (hash << 6) + (hash << 16) - hash; } hash = Math.abs(hash); return hash; } /** * DJB字符哈希算法 * * @param str * @return */ private long DJBHash(String str) { long hash = 5381; int i = 0; for (i = 0; i < str.length(); i++) { hash = ((hash << 5) + hash) + (str.charAt(i)); } hash = Math.abs(hash); return hash; } }
package BloomFilter; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Map; /** * BloomFileter布隆过滤器测试类 * * @author lyq * */ public class Client { public static void main(String[] args) { String filePath = "C:\\Users\\lyq\\Desktop\\icon\\input.txt"; String testFilePath = "C:\\Users\\lyq\\Desktop\\icon\\testInput.txt"; // 总的查询词数 int totalCount; // 正确的结果数 int rightCount; long startTime = 0; long endTime = 0; // 布隆过滤器查询结果 Map<String, Boolean> bfMap; // 普通过滤器查询结果 Map<String, Boolean> nfMap; //查询总数据 ArrayList<String> queryDatas; BloomFilterTool tool = new BloomFilterTool(filePath, testFilePath); // 采用布隆过滤器的方式进行词的查询 startTime = System.currentTimeMillis(); bfMap = tool.queryDatasByBF(); endTime = System.currentTimeMillis(); System.out.println("BloomFilter算法耗时" + (endTime - startTime) + "ms"); // 采用普通过滤器的方式进行词的查询 startTime = System.currentTimeMillis(); nfMap = tool.queryDatasByNF(); endTime = System.currentTimeMillis(); System.out.println("普通遍历查询操作耗时" + (endTime - startTime) + "ms"); boolean isExist; boolean isExist2; rightCount = 0; queryDatas = tool.getQueryDatas(); totalCount = queryDatas.size(); for (String qWord: queryDatas) { // 以遍历的查询的结果作为标准结果 isExist = nfMap.get(qWord); isExist2 = bfMap.get(qWord); if (isExist == isExist2) { rightCount++; }else{ System.out.println("预判错误的词语:" + qWord); } } System.out.println(MessageFormat.format( "Bloom Filter的正确个数为{0},总查询数为{1}个,正确率{2}", rightCount, totalCount, 1.0 * rightCount / totalCount)); } }
BloomFilter算法耗时2ms 普通遍历查询操作耗时0ms Bloom Filter的正确个数为11,总查询数为11个,【本文来自鸿网互联 (http://www.68idc.cn)】正确率1
BloomFilter算法耗时16ms 普通遍历查询操作耗时47ms Bloom Filter的正确个数为2,743,总查询数为2,743个,正确率1
算法小结
算法在实现的过程中遇到了一些小问题,第一就是在使用哈希函数的时候,因为我是随机的选了3个字符哈希函数,后来发现老是会越界,一越界数值就会变为负的再通过BitSet就会报错,原本在C语言中可以用unsigned int来解决,java中没有这个概念,于是就直接取hash绝对值了。Bloom Filter算法的一个特点是数据可能会出现误判,但是绝对不会漏判,误判就是把不是存在集合中的元素判定成有,理由是哈希冲突可能造成此结果,而漏判指的是存在的元素判定成了不存在集合中,这个是绝对不可能的,因为如果你存在,你所代表的位置就一定会有被哈希映射到,一旦映射到了,在你再去查找就不会漏掉。算法的应用范围其实挺多的,典型的比如垃圾邮箱地址的过滤。

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

写在前面&笔者的个人理解目前,在整个自动驾驶系统当中,感知模块扮演了其中至关重要的角色,行驶在道路上的自动驾驶车辆只有通过感知模块获得到准确的感知结果后,才能让自动驾驶系统中的下游规控模块做出及时、正确的判断和行为决策。目前,具备自动驾驶功能的汽车中通常会配备包括环视相机传感器、激光雷达传感器以及毫米波雷达传感器在内的多种数据信息传感器来收集不同模态的信息,用于实现准确的感知任务。基于纯视觉的BEV感知算法因其较低的硬件成本和易于部署的特点,以及其输出结果能便捷地应用于各种下游任务,因此受到工业

C++中机器学习算法面临的常见挑战包括内存管理、多线程、性能优化和可维护性。解决方案包括使用智能指针、现代线程库、SIMD指令和第三方库,并遵循代码风格指南和使用自动化工具。实践案例展示了如何利用Eigen库实现线性回归算法,有效地管理内存和使用高性能矩阵操作。

C++sort函数底层采用归并排序,其复杂度为O(nlogn),并提供不同的排序算法选择,包括快速排序、堆排序和稳定排序。

Golang通过并发性、高效内存管理、原生数据结构和丰富的第三方库,提升数据处理效率。具体优势包括:并行处理:协程支持同时执行多个任务。高效内存管理:垃圾回收机制自动管理内存。高效数据结构:切片、映射和通道等数据结构快速访问和处理数据。第三方库:涵盖fasthttp和x/text等各种数据处理库。

01前景概要目前,难以在检测效率和检测结果之间取得适当的平衡。我们就研究出了一种用于高分辨率光学遥感图像中目标检测的增强YOLOv5算法,利用多层特征金字塔、多检测头策略和混合注意力模块来提高光学遥感图像的目标检测网络的效果。根据SIMD数据集,新算法的mAP比YOLOv5好2.2%,比YOLOX好8.48%,在检测结果和速度之间实现了更好的平衡。02背景&动机随着远感技术的快速发展,高分辨率光学远感图像已被用于描述地球表面的许多物体,包括飞机、汽车、建筑物等。目标检测在远感图像的解释中

一、58画像平台建设背景首先和大家分享下58画像平台的建设背景。1.传统的画像平台传统的思路已经不够,建设用户画像平台依赖数据仓库建模能力,整合多业务线数据,构建准确的用户画像;还需要数据挖掘,理解用户行为、兴趣和需求,提供算法侧的能力;最后,还需要具备数据平台能力,高效存储、查询和共享用户画像数据,提供画像服务。业务自建画像平台和中台类型画像平台主要区别在于,业务自建画像平台服务单条业务线,按需定制;中台平台服务多条业务线,建模复杂,提供更为通用的能力。2.58中台画像建设的背景58的用户画像

狗狗币是一种基于互联网模因创建的加密货币,没有固定的供应上限,交易时间快速,交易费用低,拥有庞大的模因社区。用途包括小额交易、打赏和慈善捐赠。然而,其无限供应量、市场波动和作为笑话币的地位也带来风险和担忧。什么是狗狗币?狗狗币是一种基于互联网模因和笑话创建的加密货币。起源和历史:2013年12月,两位软件工程师BillyMarkus和JacksonPalmer创建狗狗币。灵感来自于当时流行的"Doge"模因,一个以一只柴犬为特征的滑稽照片加上破碎英语。特征和优势:无限供应量:与比特币等其他加密货

快捷的成绩查询工具、这为学生和家长提供了更方便,随着互联网的发展,越来越多的教育机构和学校开始提供网上查成绩的服务。让您轻松掌握孩子的学业进展,本文将介绍几个常用的网上查成绩平台。一、便捷——通过网上查成绩平台可以随时随地查询孩子的考试成绩家长可以方便地随时查询孩子的考试成绩,通过在电脑或手机上登录相应的网上查成绩平台。只要有网络连接、无论是在工作中还是在外出时、家长都可以及时了解孩子的学习情况,对孩子进行针对性地辅导和帮助。二、多种功能——除了成绩查询,还提供课程表、考试安排等信息许多网上查成
