Cpp || 布隆过滤器

布隆过滤器

一:布隆过滤器提出
  • 例如:我们在使用某些app刷视频的时候,它会给我们不断的推荐新的视频内容,但是我们会发现推荐内容很少有重复的.那么这个去重功能是如何实现的呢?用服务器记录了用户看过的所有记录,当推荐系统推荐热门视频的时候会从每个用户的历史记录里进行筛选,过滤掉那些已经存在的记录.如何快速查找呢?

  • 1.用哈希表存储用户记录,缺点:浪费时间

  • 2.用位图存储用户记录,缺点:不能处理哈希冲突

  • 3.将哈希与位图结合,既所谓的布隆过滤器

二:布隆过滤器概念
  • 布隆过滤器是由布隆(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(10*size)
 , _size(0)
 {}
 
private:
 BitMap _bmp;
 size_t _size; // 实际元素的个数
}
三:布隆过滤器的插入
  • 向布隆过滤器中插入"myname"

  • 原始位图

在这里插入图片描述

  • 向布隆过滤器中插入"myname"

在这里插入图片描述

  • 向布隆过滤器中插入"youname"

在这里插入图片描述

  • 代码实现
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++; }
四:布隆过滤器的查找
  • 布隆过滤器的查找
    布隆过滤器的思想是将一个元素用多种哈希函数映射到一个位图中,因此被映射到的位置的比特位一定为1.所以可以按照如下方式进行查找
  • 分别计算每个哈希值对应的比特位置存储的是否位零,只要有一个为零,代表该元素一定不在哈希表中,否则可能在哈希表中
  • 存在的误判:因为位图是通过将比特位置1的方式来表示元素存在与否,若所查找的不存在的元素的比特位刚好和某个存在元素的对应比特位都是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;
 }

注意

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

  • 比如:在布隆过滤器中查找"myname"时,假设3个哈希函数计算的哈希值为:1,3,7,刚好和其他元素的比特位重叠,此时布隆过滤器告诉我们该元素存在,但事实上该元素不存在.

五:布隆过滤器的删除

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

  • 例如:在插入的过程中"myname"和"youname"两个元素插入的是否都占用了4号比特位,若将:myname"删除的话,会将4号比特位置0,这也就影响了"youname"存在与否的判断结果

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

  • 缺陷

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

  • 2.存在计数回绕

六:布隆过滤器优点
  • 1.增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关
  • 2.哈希函数相互之间没有关系,方便硬件并行运算
  • 3.布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势
  • 4.在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势
  • 5.数据量很大时,布隆过滤器可以表示全集,其他数据结构不能
  • 6.使用同一组散列函数的布隆过滤器可以进行交、并、差运算
七:布隆过滤器的缺陷
  • 1.有误判率,即存在假阳性(False Position),即不能准确判断元素是否在集合中(补救方法:再建立一个白名单,存储可能会误判的数据)
  • 2.不能获取元素本身
  • 3.一般情况下不能从布隆过滤器中删除元素
  • 4.如果采用计数方式删除,可能会存在计数回绕问题
八:布隆过滤器的模拟实现
BloomFilter.hpp
#include"bitSet.hpp"
#include<string>

using namespace std;


template <class K,
class HashFun1,
class HashFun2,
class HashFun3>

class BloomFilter{
  public:

    //k=m*ln2/n 
    //k:哈希函数数量
    //n:range 
    //m:需要的bit位数量
    //m=k*n/ln2--->k*range/0.7 
    BloomFilter(size_t range)
      :_bs(range*5) //减少冲突的发生,采用多开空间的方式
       ,_bitCount(range*5)
  {

  }

    //布隆过滤器的插入
    void Set( const K& key){
      size_t index1=HashFun1()(key)%_bitCount;
      size_t index2=HashFun2()(key)%_bitCount;
      size_t index3=HashFun3()(key)%_bitCount;
      _bs.Set(index1);
      _bs.Set(index2);
      _bs.Set(index3);
    }

    //查找
    bool Test(const K& key){
      size_t index1=HashFun1()(key)%_bitCount;
      if(!_bs.TestSet(index1)){
        return false;
      }
      size_t index2=HashFun2()(key)%_bitCount;
      if(!_bs.TestSet(index2)){
        return false;
      }
      size_t index3=HashFun3()(key)%_bitCount;
      if(!_bs.TestSet(index3)){
        return false;
      }
      return true;  //存在误判
    }

  private:
    bitSet _bs;
    size_t _bitCount;
};

struct HF1{
  size_t operator()(const string& Str){
    size_t hash=0;
    for(const auto& e:Str){
      hash=hash*131+e;
    }
  return hash;
  }
};


struct HF2{
  size_t operator()(const string& Str){
    size_t hash=0;
    for(const auto& e:Str){
      hash=hash*65599+e;
    }
  return hash;
  }
};


struct HF3{
  size_t operator()(const string& Str){
    size_t hash=0;
    size_t magic=63689;
    for(const auto& e:Str){
      hash=hash*magic+e;
      magic*=378551;
    }
  return hash;
  }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值