C++模拟实现位图&布隆过滤器以及海量数据处理的方式

位图的概念

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

布隆过滤器的概念

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

位图&布隆过滤器的代码

GitHub链接—>点我哇

bitset.hpp

template<size_t N>
class BitSet {
public:
	BitSet()
		:_count(0)
	{
		_bset.resize((N >> 3) + 1);
	}
	BitSet<N>& set(size_t pos) {
		if (pos >= N)
			return *this;
		size_t index = pos >> 3;
		size_t bitNo = pos % 8;
		if (0 == (_bset[index] & (1 << bitNo))) {
			++_count;
			_bset[index] |= (1 << itNo);
		}
		return *this;
	}
	BitSet<N>& reset(size_t pos) {
		if (pos >= N)
			return *this;
		size_t index = pos >> 3;
		size_t bitNo = pos % 8;
		if (_bset[index] & (1 << bitNo)){
			--_count;
			_bset[index] &= ~(1 << bitNo);
		}
		return *this;
	}
	size_t size()const {
		return N;
	}
	size_t count()const {
		return _count;
	}
	//检查pos位是0还是1
	bool test(size_t pos)const {
		if (pos >= N)
			return false;
		size_t index = pos >> 3;			
		size_t bitNo = pos % 8;
		return 0 != (_bset[index] & (1 << bitNo));
	}
private:
	size_t _count;
	std::vector<unsigned char> _bset;
};

BloomFilter.hpp

#pragma once
#include"bitset.hpp"
#include"Common.hpp"

template<class K, size_t N, class HF1 = Str2Int1,
	class HF2 = Str2Int2,
	class HF3 = Str2Int3,
	class HF4 = Str2Int4,
	class HF5 = Str2Int5>
class BloomFilter {
public:
	BloomFilter()
		:_size(0)
	{}
	void insert(const K& val) {
		size_t totalBits = N * 5;
		size_t hashAddr1 = HF1()(val) % totalBits;
		size_t hashAddr2 = HF2()(val) % totalBits;
		size_t hashAddr3 = HF3()(val) % totalBits;
		size_t hashAddr4 = HF4()(val) % totalBits;
		size_t hashAddr5 = HF5()(val) % totalBits;

		_bst.set(hashAddr1);
		_bst.set(hashAddr2);
		_bst.set(hashAddr3);
		_bst.set(hashAddr4);
		_bst.set(hashAddr5);
		
		++_size;
	}
	bool find(const K& val) {
		size_t totalBits = N * 5;
		size_t hashAddr = HF1()(val) % totalBits;
		if (!_bst.test(hashAddr))
			return false;
		hashAddr = HF2()(val) % totalBits;
		if (!_bst.test(hashAddr))
			return false;
		hashAddr = HF3()(val) % totalBits;
		if (!_bst.test(hashAddr))
			return false;
		hashAddr = HF4()(val) % totalBits;
		if (!_bst.test(hashAddr))
			return false;
		hashAddr = HF5()(val) % totalBits; 
		if (!_bst.test(hashAddr))
			return false;
		return true;
	}
	size_t size()const {
		return _size;
	}
private:
	LMJian::BitSet<N * 5> _bst;
	size_t _size;
};

位图的应用

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

布隆过滤器删除

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

  • 有一种支持删除的方法:将布隆过滤器中的每个比特位扩展成一个小的计数器,插入元素时给k个计数器(k个哈希函数计算出的哈希地址)加一,删除元素时,给k个计数器减一,通过多占用几倍存储空间的代价来增加删除操作。
    缺陷:(1)仍然无法确认元素是否真正在布隆过滤器中。(2)存在计数回绕

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

  • 布隆过滤器缺陷:(1)有误判率,不能准确判断元素是否在集合中(补救方法:再建立一个白名单,存储可能会误判的数据)(2)不能获取元素本身。(3)一般情况下不能从布隆过滤器中删除元素。(4)如果采用计数方式删除,可能会存在计数回绕问题。

海量数据处理的方式

  1. 100亿条IP地址,找出出现次数最多的IP地址以及topK
    答:采用哈希分割。用IP地址%文件份数。可以将相同的IP地址放在同一个文件中,借助多线程处理。利用unordered_map对IP出现的次数进行统计。

  2. 给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。
    答:使用位图解决,40亿个数如果在位图中标记过,可以快速判断一个数是否出现过。

  3. 给定100亿个整数,设计算法找到只出现一次的整数?
    答:(1)哈希分割,将数据分散到不同的文件中,再用unordered_map统计每个数据出现的次数,返回只出现过一次的数据即可。(2)将位图变形一下,采用两个比特位标记一个整形, 就可以表示出现0次,1次,多次的整形。返回只出现一次的整数即可。

  4. 给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法。
    答:(1)使用位图。先将一个文件中的query先映射到位图中,然后用另外一个文件中的query在位图中查找。如果query转化为整形数据对应的比特位是1,说明该条query在另外一个文件中也出现了,即就是交集。(但是有误差)(2)哈希分割文件。将两个文件使用同样的哈希函数分割成多个小文件,查找时在对应的小文件中进行查找。然后利用位图,将一个文件中的query向位图中进行映射,在映射时顺便将该query保存到该比特位对应的文件中,从另一个文件中取query,然后在位图中进行查找,如果该比特位为0则肯定不是交集。如果该比特位为1说明该条query在另一个文件中可能出现了,还得需要在该比特位对应的文件中查找。

  5. 如何扩展BloomFilter使得它支持删除元素的操作
    答:可以将底层的位图扩展成一个整形数组,整形数组中记录的就是该位置被使用的元素个数。

你可能感兴趣的:(C++模拟实现位图&布隆过滤器以及海量数据处理的方式)