C++海量数据处理:位图和布隆过滤器

前言

有一道非常经典的题目:给40亿个不重复的无序的无符号整数,再给一个无符号整数,如何快速判断这个数是否在这40亿个数中。

关于这道题目我们首先想到的解法就是排序二分查找,或者借助关联式容器进行求解,但是40亿个数据的数据量是非常巨大的(16G),没有办法放入内存中,所以在面对这种海量数据处理的时候,我们今天来学习位图和布隆过滤器的相关知识,用来解决海量数据处理的问题

一:位图

位图其实就是哈希结构的变形,同样通过映射来处理数据,采用直接定址法存储数据,只不过位图本身并不存储数据,而是通过存储一个比特位来标记这个数据是否存在,1代表存在,0代表不存在

位图通常情况下用在海量数据的处理上,且数据不重复的情景下判断某个数据是否存在。

代码实现:

class bitmap{
public:
	bitmap(size_t N){
		_bits.resize(N / 32 + 1, 0); // 多开一个整型32bit
		_num = 0;
	}

	// 标记
	void set(size_t x){
		// 寻找x的标记存放在第几个整型
		size_t index = x / 32;
		// 寻找x的标记在这个整型的第几个位
		size_t pos = x % 32;

		//左移是向高位移动
		_bits[index] |= (1 << pos);
	}

	void reset(size_t x){
		// 寻找x的标记存放在第几个整型
		size_t index = x / 32;
		// 寻找x的标记在这个整型的第几个位
		size_t pos = x % 32;

		// 第pos个位置置为0
		_bits[index] &= ~(1 << pos);
	}

	// 判断x的映射位是否为1
	bool test(size_t x){
		// 寻找x的标记存放在第几个整型
		size_t index = x / 32;
		// 寻找x的标记在这个整型的第几个位
		size_t pos = x % 32;

		return _bits[index] & (1 << pos);
	}

private:
	std::vector<int> _bits;
	size_t _num; // 存储的数据个数
};

位图的应用:

  1. 快速查找某个数据是否在这个集合中
  2. 排序+去重
  3. 求两个集合的交集和并集
  4. 操作系统中的磁盘块标记

位图的优缺点分析:

  1. 节省空间,效率高
  2. 只能处理整型并且不能处理哈希冲突

二:布隆过滤器

布隆过滤器是由布隆提出的 一种概率型的数据结构,布隆过滤器实则就是位图与哈希表的结合,特点是高效地插入和查询,它是用多个哈希函数,将一个数据映射到位图结构中。这种方式不仅可以提升查询效率,还可以节省大量的内存空间。

代码实现:

template<class K = std::string, class Hash1 = HashStr1, class Hash2 = HashStr2, class Hash3 = HashStr3>
class bloomfilter{
public:
	bloomfilter(size_t num){
		_bs(-1);
	}
	void set(const K& key){
		// 通过多个哈希函数将数据映射到位图中
		size_t index1 = Hash1()(key);
		size_t index2 = Hash2()(key);
		size_t index3 = Hash3()(key);

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

	// 存在误删的问题
	void reset(const K& key){
		// 不支持删除
	}

	bool test(const K& key){
		// 判断在是不准确的,可能存在误判,判断不在是准确的。
		size_t index1 = Hash1()(key);
		if (_bs.test(index1) == false)
			return false;
		size_t index2 = Hash2()(key);
		if (_bs.test(index2) == false)
			return false;
		size_t index3 = Hash3()(key);
		if (_bs.test(index3) == false)
			return false;

		return true;
	}
private:
	// 底层其实是一个位图
	bitmap _bs;
	size_t len;
};

布隆过滤器的应用:

  1. 减少磁盘IO或者网络请求(一个值不存在,可以不用进行后续的查询请求)
  2. 客户端内容推荐浏览内容

布隆过滤器的优缺点分析:

  1. 节省空间并且高效
  2. 可能会存在误判,不支持删除。

三:海量数据的处理

3.1 位图的应用

问题:给定100亿个整数,设计算法找到只出现一次的整数。

1G大概是10亿个字节,100亿个整数就是400亿个字节,400亿个字节40G。

方法一:
出现0次的,出现1次的,出现2次及以上的,这里存在3种数据状态,出现0次的标志为00,出现1次的标志为01,出现2次及以上的标志为10。

方法二:
设置两个位图,位图1和位图2的对应位都提供一个位来标记数据。再遍历两个位图找出所有对应位映射为01的整数。

问题:给两个文件分别有100亿个整数,我们只有1G内存,如何找到两个文件的交集。

方法一:
将其中一个文件中的数据放到一个位图当中,读取另一个文件中的整数,判断在不在位图当中,若在就是交集,不在就不是交集。

方法二:
将其中一个文件的整数映射到一个位图种,将另一个文件的整数映射到另一个位图种,再将两个位图中的对应位按位与,与之后位为1的位就是交集。

3.2 布隆过滤器的应用

问题:给两个文件分别有100亿个query(请求),只有1G内存,找到两个文件的交集,分别给出精确算法和近似算法。

方法一:
将一个文件中的query映射到一个布隆过滤器中,读取另一个文件中的query,判断在不在布隆过滤器中,在就是交集,不在就不是交集。

缺陷:布隆过滤器判断数据在可能会存在误判,所以交集中有可能有些数据不准确

方法二:
将大文件切分成小文件加载到内存中(平均切割),一般切出来的小文件的大小能放进内存就可以,但是这里要不断地进行比较(不是暴力比较,小文件中数据放入set当中,搜索效率还是要高出不少)。

用同一个哈希函数处理query,i = hashstr(query) % 份数,则两个文件中相同的query一定会进入编号相同的小文件,将一个大文件分成的小文件放进一个set中,读取另一个大文件切割成的小文件中对应的小文件中的query,在第一个小文件中就是交集,不在则不是交集。

问题:如何扩展布隆过滤器使得它支持删除元素的操作。

解法一:
每个位标记为计数器
那么到底用几个位来表示计数器呢?若位数太少可能会导致计数器溢出,但如果使用更多的位来映射一个位置,那么空间的消耗就会增大。

一般采用16位来标记计数器

3.3 哈希切割的应用

问题:给一个超过100G大小的log file,log中存着IP地址,设计算法找到出现次数最多的IP地址。

统计次数一般用KV模型的map解决,但是100G大小放不进内存中。
IP有可能出现在不同的小文件当中,平均切分是不合理的。

读取IP用哈希算法计算编号,IP就会进入相同编号的小文件,运用map<string, int> 读取小文件中的IP次数,设置中间变量存储相对出现次数较多的IP地址出现的次数,统计完成则得出结果。

问题:给一个超过100G大小的log file,log中存着IP地址,设计算法找到topK的IP地址,如何直接用Linux系统命令实现。

统计次数一般用KV模型的map解决,但是100G大小放不进内存中。
IP有可能出现在不同的小文件当中,平均切分是不合理的。

读取IP用哈希算法计算编号,IP就会进入相同编号的小文件,出现次数最多的IP,就建小堆进行操作。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值