【C++】哈希应用之布隆过滤器

03-27 1681阅读

【C++】哈希应用之布隆过滤器

👀樊梓慕:个人主页

 🎥个人专栏:《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C++》《Linux》《算法》

🌝每一个不曾起舞的日子,都是对生命的辜负


目录

前言

1.布隆过滤器的提出

2.布隆过滤器的概念

3.布隆过滤器的模拟实现

3.1布隆过滤器的插入

3.2布隆过滤器的查找

3.3布隆过滤器不能删除

4.布隆过滤器的优点

5.布隆过滤器的缺陷

6.使用场景

7.源码


前言

布隆过滤器是哈希的又一重要应用,上篇文章我们谈到位图只能处理整型数据的问题,那么对于布隆过滤器来说它结合了哈希与位图,使数据处理扩展到了字符串甚至其他数据类型上。


欢迎大家📂收藏📂以便未来做题时可以快速找到思路,巧妙的方法可以事半功倍。 

=========================================================================

GITEE相关代码:🌟樊飞 (fanfei_c) - Gitee.com🌟

=========================================================================


1.布隆过滤器的提出

在面对海量数据时,红黑树等数据结构虽然查找效率高效,但不可避免的有空间上的巨大开销,所以我们提出了位图这种概念。

但对于位图来说,只能处理整型数据,因为数字采用『 直接定址法』计算哈希值几乎不会产生哈希冲突的问题,而假若是一个字符串呢?虽然我们可以通过不同的哈希函数将字符串转换为整型,但是字符串的组合形式复杂多样,无论通过哪种哈希函数都不可避免地会出现大量哈希冲突。

这里的哈希冲突就是不同的昵称最终被转换成了相同的整型,此时就可能会引发误判,即某个字符串明明不在数据中,却被系统判定为在,于是就出现了布隆过滤器。


2.布隆过滤器的概念

布隆过滤器是由布隆(Burton Howard Bloom)在1970年提出的一种紧凑型的、比较巧妙的概率型数据结构,特点是高效地插入和查询。

布隆过滤器其实就是位图的一个变形和延申,虽然无法避免存在哈希冲突,但我们可以想办法降低误判的概率。

原理:当一个数据映射到位图中时,『 布隆过滤器』会用多个哈希函数将其映射到多个比特位,当判断一个数据是否在位图当中时,需要分别根据这些哈希函数计算出对应的比特位,如果这些比特位都被设置为1则判定为该数据存在,否则则判定为该数据不存在。

布隆过滤器使用多个哈希函数进行映射,目的就在于『 降低哈希冲突的概率』,一个哈希函数产生冲突的概率可能比较大,但多个哈希函数同时产生冲突的概率可就没那么大了。

例子:假设布隆过滤器使用三个哈希函数进行映射,那么“张三”这个昵称被使用后位图中会有三个比特位会被置1,当有人要使用“李四”这个昵称时,就算前两个哈希函数计算出来的位置都产生了冲突,但由于第三个哈希函数计算出的比特位的值为0,此时系统就会判定“李四”这个昵称没有被使用过。


虽然布隆过滤器已经极大的降低了哈希冲突的概率,但是仍然可能会产生误判:

  • 当布隆过滤器判断一个数据存在可能是不准确的,因为这个数据对应的比特位可能被其他一个数据或多个数据占用了。
  • 当布隆过滤器判断一个数据不存在是准确的,因为如果该数据存在那么该数据对应的比特位都应该已经被设置为1了。

    显然布隆过滤器的效率与哈希函数的个数与过滤器长度息息相关,那么他们之间究竟存在怎样的关系呢?

    网上有大佬经过计算得到如下图这样的结论:

    【C++】哈希应用之布隆过滤器

    感兴趣的这是原文链接->详解布隆过滤器的原理,使用场景和注意事项 - 知乎 (zhihu.com)


     为了简化,我们取k为3,即设计3个哈希函数,最终得到m与n的关系:『 m≈4*n』。

    注意:为了简化计算我们取ln2≈0.7。


    3.布隆过滤器的模拟实现

    注意这里我们需要三个哈希函数,所以需要三个仿函数设计:

    //布隆过滤器
    template
    class BloomFilter
    {
    public:
    	//...
    private:
    	bitset _bs;
    };
    

    这三种哈希函数的实现如下,原理感兴趣的自行研究:

    struct BKDRHash
    {
    	size_t operator()(const string& s)
    	{
    		size_t value = 0;
    		for (auto ch : s)
    		{
    			value = value * 131 + ch;
    		}
    		return value;
    	}
    };
    struct APHash
    {
    	size_t operator()(const string& s)
    	{
    		size_t value = 0;
    		for (size_t i = 0; i  3));
    			}
    			else
    			{
    				value ^= (~((value > 5)));
    			}
    		}
    		return value;
    	}
    };
    struct DJBHash
    {
    	size_t operator()(const string& s)
    	{
    		if (s.empty())
    			return 0;
    		size_t value = 5381;
    		for (auto ch : s)
    		{
    			value += (value  3));
    			}
    			else              // 奇数位字符
    			{
    				hash ^= (~((hash > 5)));
    			}
    		}
    		return hash;
    	}
    };
    struct HashFuncDJB
    {
    	// DJB
    	size_t operator()(const string& s)
    	{
    		size_t hash = 5381;
    		for (auto ch : s)
    		{
    			hash = hash * 33 ^ ch;
    		}
    		return hash;
    	}
    };
    template
    class BloomFilter
    {
    public:
    	void Set(const K& key)
    	{
    		size_t hash1 = Hash1()(key) % M;
    		size_t hash2 = Hash2()(key) % M;
    		size_t hash3 = Hash3()(key) % M;
    		_bs->set(hash1);
    		_bs->set(hash2);
    		_bs->set(hash3);
    	}
    	bool Test(const K& key)
    	{
    		size_t hash1 = Hash1()(key) % M;
    		if (_bs->test(hash1) == false)
    			return false;
    		size_t hash2 = Hash2()(key) % M;
    		if (_bs->test(hash2) == false)
    			return false;
    		size_t hash3 = Hash3()(key) % M;
    		if (_bs->test(hash3) == false)
    			return false;
    		return true; // 存在误判(有可能3个位都是跟别人冲突的,所以误判)
    	}
    private:
    	static const size_t M = 10 * N;
        //STL库中位图实现为静态数组(即int arr[]这种),存储在对象中,数据量大时可能会导致栈溢出,所以这里我们new一个,使用堆空间避免栈溢出
    	std::bitset* _bs = new std::bitset;
    };

    =========================================================================

    如果你对该系列文章有兴趣的话,欢迎持续关注博主动态,博主会持续输出优质内容

    🍎博主很需要大家的支持,你的支持是我创作的不竭动力🍎

    🌟~ 点赞收藏+关注 ~🌟

    =========================================================================

VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]