赞
踩
作者:爱写代码的刚子
时间:2023.9.30
前言:本篇博客介绍hash应用部分——位图和布隆过滤器,利用位图和布隆过滤器解决一些特定场景的问题。
所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在的。
数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比 特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。比如:
template<size_t N> class bitset { public: bitset() { _a.resize(N/32+1);//不要忘了+1,默认初始化成0 } void set( size_t x) { int i=x/32; int j=x%32; _a[i] |=(1<<j); } void reset(size_t x) { int i=x/32; int j=x%32; _a[i] &= (~(1<<j)); } bool test(size_t x) { int i=x/32; int j=x%32; return _a[i] &(1<<j); } private: vector<int> _a; };
用两个位图来测试数据个数
template<size_t N> class twobitset { public: void set(size_t x) { //00->01 if(!_b1.test(x)&&!_b2.test(x)) { _b2.set(x); }//01->10 else if(!_b1.test(x)&&_b2.test(x)) { _b1.set(x); _b2.reset(x); } } bool is_once(size_t x) { return !_b1.test(x)&&_b2.test(x); } bool is_or_above_twice(size_t x) { return _b1.test(x)&&!_b2.test(x); } private: bitset<N> _b1; bitset<N> _b2; };
我们在使用新闻客户端看新闻时,它会给我们不停地推荐新的内容,它每次推荐时要去重,去掉那些已经看过的内容。问题来了,新闻客户端推荐系统如何实现推送去重的? 用服务器记录了用户看过的所有历史记 录,当推荐系统推荐新闻时会从每个用户的历史记录里进行筛选,过滤掉那些已经存在的记录。 如何快速查找呢?
布隆过滤器是由布隆(Burton Howard Bloom)在1970年提出的 一种紧凑型的、比较巧妙的概率型数据结 构,特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”,它是用多个哈希函 数,将一个数据映射到位图结构中。此种方式不仅可以提升查询效率,也可以节省大量的内存空间
// 假设布隆过滤器中元素类型为K,每个元素对应5个哈希函数 template<class K, class KToInt1 = KeyToInt1, class KToInt2 = KeyToInt2, class KToInt3 = KeyToInt3, class KToInt4 = KeyToInt4, class KToInt5 = KeyToInt5> class BloomFilter { public: BloomFilter(size_t size) // 布隆过滤器中元素个数 : _bmp(5*size), _size(0) {} bool Insert(const K& key) { size_t bitCount = _bmp.Size(); size_t index1 = KToInt1()(key)%bitCount; size_t index2 = KToInt2()(key)%bitCount; size_t index3 = KToInt3()(key)%bitCount; size_t index4 = KToInt4()(key)%bitCount; size_t index5 = KToInt5()(key)%bitCount; _bmp.Set(index1); _bmp.Set(index2);_bmp.Set(index3); _bmp.Set(index4);_bmp.Set(index5); _size++; } private: bitset _bmp; size_t _size;// 实际元素的个数 }
布隆过滤器的思想是将一个元素用多个哈希函数映射到一个位图中,因此被映射到的位置的比特位一定为1。 所以可以按照以下方式进行查找:分别计算每个哈希值对应的比特位置存储的是否为零,只要有一个为零, 代表该元素一定不在哈希表中,否则可能在哈希表中。
bool IsInBloomFilter(const K& key) { size_t bitCount = _bmp.Size(); size_t index1 = KToInt1()(key)%bitCount; if(!_bmp.Test(index1)) return false; size_t index2 = KToInt2()(key)%bitCount; if(!_bmp.Test(index2)) return false; size_t index3 = KToInt3()(key)%bitCount; if(!_bmp.Test(index3)) return false; size_t index4 = KToInt4()(key)%bitCount; if(!_bmp.Test(index4)) return false; size_t index5 = KToInt5()(key)%bitCount; if(!_bmp.Test(index5)) return false; return true; // 有可能在 }
注意:布隆过滤器如果说某个元素不存在时,该元素一定不存在,如果该元素存在时,该元素可能存在,因为有些哈希函数存在一定的误判。比如:在布隆过滤器中查找"alibaba"时,假设3个哈希函数计算的哈希值为:1、3、7,刚好和其他元素的比特位重叠,此时布隆过滤器告诉该元素存在,但实该元素是不存在的。
布隆过滤器不能直接支持删除工作,因为在删除一个元素时,可能会影响其他元素。
比如:删除上图中"tencent"元素,如果直接将该元素所对应的二进制比特位置0,“baidu”元素也被删除了, 因为这两个元素在多个哈希函数计算出的比特位上刚好有重叠。
一种支持删除的方法:将布隆过滤器中的每个比特位扩展成一个小的计数器,插入元素时给k个计数器(k个哈希函数计算出的哈希地址)加一,删除元素时,给k个计数器减一,通过多占用几倍存储空间的代价来增加删除操作。
缺陷:
增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关
哈希函数相互之间没有关系,方便硬件并行运算
布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势
在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势
数据量很大时,布隆过滤器可以表示全集,其他数据结构不能
使用同一组散列函数的布隆过滤器可以进行交、并、差运算
附:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。