【C++】布隆过滤器 浅浅的花香味﹌ 2024-04-26 11:03 90阅读 0赞 ![在这里插入图片描述][6ac7346dc5944908b5dd227e55fab217.gif_pic_center] > ?write in front? > ?所属专栏: [C++学习][C] > ?️博客主页:[睿睿的博客主页][Link 1] > ?️代码仓库:?[VS2022\_C语言仓库][VS2022_C] > ?您的点赞、关注、收藏、评论,是对我最大的激励和支持!!! > **关注我,关注我,关注我**,你们将会看到更多的优质内容!! ![在这里插入图片描述][85d8f838a7524c8bb8bed80b7ac059c1.gif_pic_center] #### 文章目录 #### * 前言 * 一.布隆过滤器的概念 * 二.布隆过滤器的实现: * * 注意: * 三.布隆过滤器的误判 * 四.布隆过滤器的优缺点: * * 布隆过滤器优点 * 布隆过滤器缺陷 * 五.哈希切割: * * 题目1: * 题目2: * 总结 ## 前言 ## 哈希还有一个重要的用途就是布隆过滤器。之前的位图只能对于整数进行判断是否存在,整形有一个固定的范围,但是对于字符串这些没有范围的数据怎么判断在不在呢?字符串在通过哈希函数转化成的数据对应的位置可能和其他字符串通过哈希函数转化的数据相同,这样就会造成误判。 ![在这里插入图片描述][4aa41bc348df471b849e37b4fd829591.png] 所以我们可以让多个哈希函数对一个字符串转化成多个数据进行标记,这样误判就会少了不少。而这就是布隆过滤器的初形。 在大多数游戏里面,取名都不能取相同的名字。所以就要判断这个昵称是否存在,底层可以用哈希表,但是玩家的数量太多了,都是以**亿**这个量级来就算,那么采用哈希表就会**造成大量的空间浪费**;如果用位图,但**位图一般只能用于处理整型数据**。 那么我们就可以采用**哈希表+位图**,即布隆过滤器来完成检测这个昵称是否已经被注册。上面的字符串的举例就是布隆过滤器,在实际中判断名称是否重复都是用下面的方式: ![在这里插入图片描述][8f7226a6b8d342b1ad9cd6e5576876d8.png] ## 一.布隆过滤器的概念 ## **布隆过滤器**是由布隆在1970年提出的 一种紧凑型的、比较巧妙的概率型数据结构,特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”,它是用**多个哈希函数**,将**一个数据**映射到**位图结构**中。此种方式不仅可以提升查询效率,也可以节省大量的内存空间。 ## 二.布隆过滤器的实现: ## 布隆过滤器的关键就是他使用了多个哈希函数(可以参考这篇文章:[哈希函数][Link 2])在这里挑选了三个误判率最低的哈希函数。 ![在这里插入图片描述][9dccd9bf91c646ac9f8c9fcb41dfa34d.png] #include<iostream> #include<bitset> using namespace std; //这里采用仿函数的形式封装哈希函数 struct BKDRHash { size_t operator()(const string& str) { size_t hash = 0; for (auto ch : str) { hash = hash * 131 + ch; } //cout <<"BKDRHash:" << hash << endl; return hash; } }; struct APHash { size_t operator()(const string& str) { size_t hash = 0; for (size_t i = 0; i < str.size(); i++) { size_t ch = str[i]; if ((i & 1) == 0) { hash ^= ((hash << 7) ^ ch ^ (hash >> 3)); } else { hash ^= (~((hash << 11) ^ ch ^ (hash >> 5))); } } //cout << "APHash:" << hash << endl; return hash; } }; struct DJBHash { size_t operator()(const string& str) { size_t hash = 5381; for (auto ch : str) { hash += (hash << 5) + ch; } //cout << "DJBHash:" << hash << endl; return hash; } }; //非类型模板参数 template<size_t N, class K=string,class Hash1= BKDRHash, class Hash2= APHash,class Hash3= DJBHash> class BloomFilter { public: void set(const K& key) { size_t hash1 = Hash1()(key)%N; _bs.set(hash1); size_t hash2 = Hash2()(key) % N; _bs.set(hash2); size_t hash3 = Hash3()(key) % N; _bs.set(hash3); } bool test(const K& key) { size_t hash1 = Hash1()(key) % N; if (_bs.test(hash1)==false) { return false; } size_t hash2 = Hash2()(key) % N; if (_bs.test(hash2) == false) { return false; } size_t hash3 = Hash3()(key) % N; if (_bs.test(hash3) == false) { return false; } return true; } private: bitset<N> _bs; }; 因为这里采用的是仿函数,所以我们将这个几个哈希函数定义为结构体,然后重载`operator()`进行调用。 ### 注意: ### 这里需要注意的是这里不支持删除操作,因为删除一个元素的时候,可能会影响其他元素。 另外,布隆过滤器的设计初衷就是为了快速查找元素是否存在于集合中,并在一些特定应用中提供高效的去重和查询功能。它不被设计用来维护可变的数据集,因此不支持删除操作。 ## 三.布隆过滤器的误判 ## ![在这里插入图片描述][dab737074d7c4248918d34e7dce11ecf.png] 这里参考了这篇文章:[详解布隆过滤器的原理,使用场景和注意事项][Link 3] 这篇文章指出,最好的状态(空间消耗小和误判率最低)是: ![在这里插入图片描述][3195d210b65244fa8ca4b44b34e57f14.png] 当我们使用三个哈希函数的时候最佳状态如下: ![在这里插入图片描述][42a08d590b4d404daaa0e5289fea49ab.png] 大家可以通过下面代码测试一下 void TestBloomFilter2() { srand(time(0)); const size_t N = 10000; BloomFilter<N*4> bf; std::vector<std::string> v1; std::string url = "https://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html"; //std::string url = "猪八戒"; for (size_t i = 0; i < N; ++i) { v1.push_back(url + std::to_string(i)); } for (auto& str : v1) { bf.set(str); } // v2跟v1是相似字符串集(前缀一样),但是不一样 std::vector<std::string> v2; for (size_t i = 0; i < N; ++i) { std::string urlstr = url; urlstr += std::to_string(9999999 + i); v2.push_back(urlstr); } size_t n2 = 0; for (auto& str : v2) { if (bf.test(str)) // 误判 { ++n2; } } cout << "相似字符串误判率:" << (double)n2 / (double)N << endl; // 不相似字符串集 std::vector<std::string> v3; for (size_t i = 0; i < N; ++i) { //string url = "zhihu.com"; string url = "孙悟空"; url += std::to_string(i + rand()); v3.push_back(url); } size_t n3 = 0; for (auto& str : v3) { if (bf.test(str)) { ++n3; } } cout << "不相似字符串误判率:" << (double)n3 / (double)N << endl; } int main() { //testfilter(); TestBloomFilter2(); } ## 四.布隆过滤器的优缺点: ## ### 布隆过滤器优点 ### 1. 增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关 2. 哈希函数相互之间没有关系,方便硬件并行运算 3. 布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势 4. 在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势 5. 数据量很大时,布隆过滤器可以表示全集,其他数据结构不能 6. 使用同一组散列函数的布隆过滤器可以进行交、并、差运算 ### 布隆过滤器缺陷 ### 1. 有误判率,即存在假阳性(False Position),即不能准确判断元素是否在集合中(补救方法:再**建立一个白名单,存储可能会误判的数据**) 2. 不能获取元素本身 3. 一般情况下**不能从布隆过滤器中删除元素** ## 五.哈希切割: ## ### 题目1: ### ![在这里插入图片描述][c0f05ce52a1c43e0a3732e3da8c06ca3.png] 在这里一个query大约是30个字节,100亿个query也就是3000亿个字节。1G大约是10亿字节,所以这里有300G的数据。所以这个地方我们无法直接用位图直接解决。题目给了1G的内存,就说明还是要通过位图来解决,这里就使用了布隆过滤器。 这里我们将两个300G的数据,先通过哈希函数转化为整数,找到该query所在1000个小文件的位置,随后将query放到小文件里面 ![在这里插入图片描述][c5146182cd3e4e41bded587d2e5a542b.png] ![在这里插入图片描述][d02a20b003384f68acae019bce040781.png] 此时,我们可以读取Ai的数据放入set,在依次读取Bi的query,在通过哈希函数转化之后,在set里面寻找在不在,在就是交集,找到交集之后就将set里面的这个值删掉(避免相同的数据进入交集)。 **存在的问题:** 1. 大多数都是冲突的 2. 大多数都是相同的 **解决方法:** * 先把Ai的query放到set里面,如果set的`insert`抛异常`bad_alloc`,就说明大量数据冲突,插入都溢出了。此时换一个哈希函数进行二次切分即可(重复上述操作) * 如果插入不失败,那说明Ai有大量相同数据。 ### 题目2: ### ![在这里插入图片描述][089afa0d47d341ddb6492b2367e2b89e.png] 这里和上面一样,将其分别放到不同的小文件里面,这里我们就放在300个小文件里面: ![在这里插入图片描述][9a5b4ee264394251b4a70b22478adc8b.png] 此时相同的ip肯定会放在同一个小文件里面,此时我们就使用`map`统计次数就可以了。如果想要找到topk数据,直接用堆来操作就可以了。 ## 总结 ## 更新不易,辛苦各位小伙伴们动动小手,?三连走一走?? ~ ~ ~ 你们真的对我很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正! > **专栏订阅:** > [每日一题][Link 4] > [C语言学习][C 1] > [算法][Link 5] > [智力题][Link 6] > [初阶数据结构][Link 7] > [Linux学习][Linux] > [C++学习][C] > 更新不易,辛苦各位小伙伴们动动小手,?三连走一走?? ~ ~ ~ 你们真的对我很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正! ![在这里插入图片描述][55270e2552774a17a711aa978da84193.gif_pic_center] [6ac7346dc5944908b5dd227e55fab217.gif_pic_center]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/f0c78a2a70e74f58ac73d0e23a5d4e6f.gif [C]: https://blog.csdn.net/qq_74310471/category_12289978.html [Link 1]: https://blog.csdn.net/qq_74310471?spm=1000.2115.3001.5343 [VS2022_C]: https://gitee.com/zhangxuruihhhh/c-language-learning [85d8f838a7524c8bb8bed80b7ac059c1.gif_pic_center]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/72fa2e4162df4845b170d366479b3d5f.gif [4aa41bc348df471b849e37b4fd829591.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/363a3ee45de44b7692fd7571aee4f3fc.png [8f7226a6b8d342b1ad9cd6e5576876d8.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/175f14750fbf4daf917972b82c9a971c.png [Link 2]: https://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html [9dccd9bf91c646ac9f8c9fcb41dfa34d.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/5089c5d079884d1d80573f8883a96b5c.png [dab737074d7c4248918d34e7dce11ecf.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/2a6724bcde8d4b4f90a52b855bcc5cb8.png [Link 3]: https://zhuanlan.zhihu.com/p/43263751/ [3195d210b65244fa8ca4b44b34e57f14.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/17e8f459d4fe4b9eaf06031d3b154281.png [42a08d590b4d404daaa0e5289fea49ab.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/1aefc84438c64d0999a85a74c339b9f6.png [c0f05ce52a1c43e0a3732e3da8c06ca3.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/0e9dbf2b174e4dada4ffd339b8e0e5a5.png [c5146182cd3e4e41bded587d2e5a542b.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/223ed7f692dc40b59c74db647c480ee6.png [d02a20b003384f68acae019bce040781.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/d7ad5cb9db464f33992701ff90088ad9.png [089afa0d47d341ddb6492b2367e2b89e.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/dfd4cc408ca941e992ae6d7711cf6580.png [9a5b4ee264394251b4a70b22478adc8b.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/a9e3fc755e014654a9843de437f89a4d.png [Link 4]: https://blog.csdn.net/qq_74310471/category_12166401.html?spm=1001.2014.3001.5482 [C 1]: https://blog.csdn.net/qq_74310471/category_12157207.html?spm=1001.2014.3001.5482 [Link 5]: https://blog.csdn.net/qq_74310471/category_12144452.html?spm=1001.2014.3001.5482 [Link 6]: https://blog.csdn.net/qq_74310471/category_12166402.html [Link 7]: https://blog.csdn.net/qq_74310471/category_12216969.html?spm=1001.2014.3001.5482 [Linux]: https://blog.csdn.net/qq_74310471/category_12291120.html [55270e2552774a17a711aa978da84193.gif_pic_center]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/7a54e34c08ef47238f2afc7d83961c3d.gif
相关 布隆过滤器 - Redis 布隆过滤器,Guava 布隆过滤器 BloomFilter - 代码实践 文章目录 布隆过滤器 - Redis 布隆过滤器,Guava 布隆过滤器 BloomFilter - 代码实践 1、通过guava 实现的布 ゝ一世哀愁。/ 2023年10月09日 04:22/ 0 赞/ 115 阅读
相关 布隆过滤器 布隆过滤器 什么是布隆过滤器? 布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的`二进制向量`和`一系列随机映射函数`。布 古城微笑少年丶/ 2022年10月15日 12:53/ 0 赞/ 276 阅读
相关 布隆过滤器 > 布隆过滤器\[1\](Bloom Filter)是由布隆在1970年提出的。它实际上是由一个很长的二进制向量和一系列随机映射函数组成,布隆过滤器可以用于检索一个元素是否在一 r囧r小猫/ 2022年07月14日 13:15/ 0 赞/ 251 阅读
相关 布隆过滤器 布隆过滤器实际上就是哈希和位图的结合 它的优点:速度快并且节省空间 它的缺点:存在误判(比如存在不同的字符串可能存在相同的ASCII,这样我们在判断的时候就会出现误判) 末蓝、/ 2022年06月12日 22:14/ 0 赞/ 316 阅读
相关 布隆过滤器 布隆过滤器原理 布隆过滤器有什么用? 布隆过滤器是可以用于判断一个元素是不是在一个集合里,并且相比于其它的数据结构,布隆过滤器在空间和时间方面都有巨大的优势。 特 系统管理员/ 2022年03月25日 07:37/ 0 赞/ 314 阅读
相关 布隆过滤器 使用场景 ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4u 缺乏、安全感/ 2022年03月01日 11:28/ 0 赞/ 332 阅读
相关 布隆过滤器 布隆过滤器介绍 > 布隆过滤器在wiki上的介绍: 布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布 喜欢ヅ旅行/ 2021年12月16日 01:01/ 0 赞/ 385 阅读
相关 布隆过滤器 布隆过滤器常常被用来检测某个元素是否是巨量数据集合中的成员 1、基本原理: (1)将长度为m的位数组元素全部置为0; (2)对集合S中的某个成员a,分别用k个哈希函数对其 亦凉/ 2021年12月15日 12:59/ 0 赞/ 405 阅读
相关 布隆过滤器 布隆过滤器 前言 布隆过滤器原理 布隆过滤器优缺点 优点 缺点 使用场景 前言 Hash(散列)函数在计算机 本是古典 何须时尚/ 2021年11月04日 13:34/ 0 赞/ 446 阅读
还没有评论,来说两句吧...