【C++】布隆过滤器简单操纵模拟以及常见题目

博客主页: 主页
系列专栏: C++
❤️感谢大家点赞收藏⭐评论✍️
期待与大家一起进步!


文章目录

  • 前言
  • 一、求下标仿函数的建议
  • 二、布隆过滤器代码
  • 面试题
    • 1.近似算法:
    • 2.精确算法


前言

`

布隆过滤器特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”,它是用多个哈希函数,将一个数据映射到位图结构中。此种方式不仅可以提升查询效率,也可以节省大量的内存空间。布隆过滤器一般用来操作的对象类型为string,因为string在位图中不好被标记
【C++】布隆过滤器简单操纵模拟以及常见题目_第1张图片

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

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

为了减小失误,我们可以用多种方法计算对应字符串的下标所对应位置

一、求下标仿函数的建议

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)
    //检测字符串是否存在
    {
    //你每个哈希下标都为1,不能说明这个字符串一定存在,
    //但若你有一个下标检测为0,那么一定不存在
        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;
};

面试题

给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法

1.近似算法:

这里直接就是布隆过滤器,因为其可能存在冲突,所以为近似算法,因为结果有小概率是错误的

2.精确算法

【C++】布隆过滤器简单操纵模拟以及常见题目_第2张图片
先将大文件进行分割,取出一部分进行找交集
【C++】布隆过滤器简单操纵模拟以及常见题目_第3张图片

利用哈希切分:A与B中相同的query一定会分别进入Ai与Bi相同编号的小文件

找交集,Ai读出来放进一个set,再依次读取B的query,在就是交集并且删掉,就可以找到Ai与Bi的交集。

可能存在的情况:
两个场景:Ai有5G
1.4G都是相同的query,1G冲突
2.大多数都是冲突的,这里冲突指的是两个不同的字符串算哈希下标的时候,所有哈希下标均相同。

解决方案:
1.先把Ai的query读到一个set中,再依次读取Bi的query,如果set的insert报错抛异常(因为超出的最大容量),那么大多数的都是冲突的,因为如果我是相同的话,哪怕我有4G给相同数据,最后只插入一个,因为set有去重的功能
如果能全部insert到里面说明Ai大部分都是相同的
2.如果抛异常,说明有大量冲突,哈希下标全都重了,这个时候我们需要新换一个哈希函数,继续进行切分

你可能感兴趣的:(c++,哈希算法,散列表)