C++之哈希 --- 哈希的应用(位图&布隆过滤器)

一、位图

1.1 位图的基本概念

在如今网络交通高度发达的时代,网购已经成为我们日常生活中的一部分。没当双11到来,各大平台都会迎来一次网购的高潮。这就会让服务器短时间内获得高达几十亿上百亿的数据,那我们该如何去处理这海量的数据呢?这里给大家上一个腾讯的面试题。

面对如此海量的数据,我们传统的容器(如unordered_map,map,vector等)似乎都无法驾驭这如此庞大的数据量。那我们该如何解决这个问题呢?

先给出两种常规的思路:

第三种方法当然是采用我们的位图啦,那什么是位图呢?其又是如何解决这个问题的呢?

位图概念

所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在的。

位图解决该问题

数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。

判断一个数是否存在,我们只用了一个比特位就可以判断,这大大降低了我们的内存消耗。原本40亿数据要占用16GB的空间。现在只需要0.5G的内存就能处理了。

2.2 位图的模拟实现

其实C++的库里也有对应的位图

这里我们主要实现3个功能:

  • set(size_t x) :将对应数字位置为1
  • reset(size_t x) :将对应数字位置清0
  • test(size_t x) :查找该数字是否存在

实现位图我们主要要解决的问题就是,处理好位置映射的问题,这里我用一张图来好好解释解释:

那么接下来直接上代码:

cpp 复制代码
template<size_t N>  //需要多少比特位
	class bitmap {
	public:
		bitmap() 
		{
			_bits.resize((N >> 5) + 1, 0);
		}

		void set(size_t x)//将一位置为1
		{
			int i = x / 32;
			int j = x % 32;
			_bits[i] |= (1 << j);
		}

		void reset(size_t x)//将一位置为0
		{
			int i = x / 32;
			int j = x % 32;
			_bits[i] &= (~(1 << j));
		}

		bool test(size_t x)//查找该数字是否存在
		{
			if (x > N) return false;
			int i = x / 32;
			int j = x % 32;
			return (_bits[i]>>j)&1;
		}

		~bitmap()
		{}

	private:
		vector<int> _bits;
	};

二、布隆过滤器

2.1 布隆过滤器的基本概念

我们先来了解一下布隆过滤器的背景:

于是布隆过滤器就诞生了:

布隆过滤器是由布隆(Burton Howard Bloom)在1970年提出的 一种紧凑型的、比较巧妙的概率型数据结构,特点是高效地插入和查询,可以用来告诉你 "某样东西一定不存在或者可能存在",它是用多个哈希函数,将一个数据映射到位图结构中。此种方式不仅可以提升查询效率,也可以节省大量的内存空间。展示一张示意图:

这里举一个具体的例子来说明布隆过滤器的插入操作:

要是我想查找香蕉在不在,只需要根据不同的hash算法,算出对应的红色位置是否都为1就可以了

同时根据这张图,要给大家传递的一个概念是:

可以发现香蕉和菠萝有一个映射位置重合了,那么也就是说在之后插入数据的过程中,有可能该数据的所有hash位置都与其他数据的hash位置重合,导致这个数据并没有加入到我们的布隆过滤器中。由此我们可以得出一个结论:布隆过滤器如果说某个元素不存在时,该元素一定不存在,如果该元素存在时,该元素可能存在,因为有些哈希函数存在一定的误判。

2.2 布隆过滤器的模拟实现

这块话不多说直接上代码(这里的布隆过滤器复用了刚刚的位图代码)

cpp 复制代码
struct BKDRHash
	{
		size_t operator()(const string& s)
		{
			// BKDR
			size_t value = 0;
			for (auto ch : s)
			{
				value *= 31;
				value += ch;
			}
			return value;
		}
	};
	struct APHash
	{
		size_t operator()(const string& s)
		{
			size_t hash = 0;
			for (long i = 0; i < s.size(); i++)
			{
				if ((i & 1) == 0)
				{
					hash ^= ((hash << 7) ^ s[i] ^ (hash >> 3));
				}
				else
				{
					hash ^= (~((hash << 11) ^ s[i] ^ (hash >> 5)));
				}
			}
			return hash;
		}
	};

	struct DJBHash
	{
		size_t operator()(const string& s)
		{
			size_t hash = 5381;
			for (auto ch : s)
			{
				hash += (hash << 5) + ch;
			}
			return hash;
		}
	};

	template<size_t N ,class HashFunc1 = BKDRHash,class HashFunc2 = APHash,class HashFunc3 = DJBHash,class K = string>
	class BloomFilter
	{
	public:
		BloomFilter() {}

		void set(const K& key)
		{
			size_t index1 = HashFunc1()(key) % (_ratio * N);
			size_t index2 = HashFunc2()(key) % (_ratio * N);
			size_t index3 = HashFunc3()(key) % (_ratio * N);

			_bt.set(index1);
			_bt.set(index2);
			_bt.set(index3);
			
		}

		bool test(const K& key)
		{
			size_t index1 = HashFunc1()(key) % (_ratio * N);
			if (_bt.test(index1) == false) return false;
			size_t index2 = HashFunc2()(key) % (_ratio * N);
			if (_bt.test(index2) == false) return false;
			size_t index3 = HashFunc3()(key) % (_ratio * N);
			if (_bt.test(index3) == false) return false;

			return true;
		}

		~BloomFilter() {}
	private:
		const static size_t _ratio = 5;
		bitmap<_ratio * N> _bt;
	};

2.3 布隆过滤器的优缺点

优点:

  1. 增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关
  2. 哈希函数相互之间没有关系,方便硬件并行运算
  3. 布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势
  4. 在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势
  5. 数据量很大时,布隆过滤器可以表示全集,其他数据结构不能
  6. 使用同一组散列函数的布隆过滤器可以进行交、并、差运算

缺点:

  1. 有误判率,即存在假阳性(False Position),即不能准确判断元素是否在集合中(补救方法:再 建立一个白名单,存储可能会误判的数据)

  2. 不能获取元素本身

  3. 一般情况下不能从布隆过滤器中删除元素(会影响其他数据)

  4. 如果采用计数方式删除,可能会存在计数回绕问题

相关推荐
鸣弦artha11 分钟前
蓝桥杯——杨辉三角
java·算法·蓝桥杯·eclipse
我是聪明的懒大王懒洋洋14 分钟前
力扣力扣力:动态规划入门(1)
算法·leetcode·动态规划
丶Darling.19 分钟前
Day44 | 动态规划 :状态机DP 买卖股票的最佳时机IV&&买卖股票的最佳时机III
算法·动态规划
TN_stark9321 小时前
多进程/线程并发服务器
服务器·算法·php
汉克老师2 小时前
GESP4级考试语法知识(贪心算法(四))
开发语言·c++·算法·贪心算法·图论·1024程序员节
smj2302_796826522 小时前
用枚举算法解决LeetCode第3348题最小可整除数位乘积II
python·算法·leetcode
爱吃生蚝的于勒2 小时前
C语言最简单的扫雷实现(解析加原码)
c语言·开发语言·学习·计算机网络·算法·游戏程序·关卡设计
秋说3 小时前
【数据结构 | C++】整型关键字的平方探测法散列
数据结构·c++·算法
weixin_478689764 小时前
【回溯法】——组合总数
数据结构·python·算法
戊子仲秋4 小时前
【LeetCode】每日一题 2024_11_14 统计好节点的数目(图/树的 DFS)
算法·leetcode·深度优先