[数据结构] 位图&布隆过滤器

文章目录

  • 1. 位图
    • 1.1 位图概念
    • 1.2 位图实现
    • 1.3 位图应用
  • 2. 布隆过滤器
    • 2.1 布隆过滤器概念
    • 2.2 布隆过滤器插入
    • 2.3 布隆过滤器查找删除
    • 2.4 布隆过滤器优缺点
  • 3. 海量数据处理

1. 位图


1.1 位图概念

C++标准库中自带bitset, 首先查看参考文档分析位图
在这里插入图片描述位集
一种 位集存储位(只有两个可能值的元素:0或1 true或false,…)。

该类模拟bool元素数组,但针对空间分配进行了优化:通常,每个元素仅占用一位(在大多数系统上,它比最小元素类型少八倍char)。

每个位的位置可以单独访问:例如,对于名为的给定位集foo,表达式foo[3]访问其第四位,就像常规数组访问其元素一样。但是,因为在大多数C ++环境中没有元素类型是单个位,所以单个元素作为特殊引用类型进行访问

  • 正如上述所言, 位图在存储大量数据时能够有效减少内存的消耗, 只需在相应比特位上置1即可. 位图适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在的
  • 例如: 将array[]数组放入位图, 仅需3 * 8 bit, 相比map等节省了大量空间
    [数据结构] 位图&布隆过滤器_第1张图片

1.2 位图实现

class bitset
{
public:
	bitset(size_t bitCount)
		: _bit((bitCount>>5)+1), _bitCount(bitCount)
	{}
	// 将which比特位置1
	void set(size_t which)
	{
		if(which > _bitCount)
			return;
		size_t index = (which >> 5);
		size_t pos = which % 32;
		_bit[index] |= (1 << pos);
	}
	// 将which比特位置0
	void reset(size_t which)
	{
		if(which > _bitCount)
			return;
		size_t index = (which >> 5);
		size_t pos = which % 32;
		_bit[index] &= ~(1<<pos);
	}
	// 检测位图中which是否为1
	bool test(size_t which)
	{
		if(which > _bitCount)
			return false;
		size_t index = (which >> 5);
		size_t pos = which % 32;
		return _bit[index] & (1<<pos);
	}

private:
	vector<int> _bit;
	size_t _bitCount;
};

1.3 位图应用

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

2. 布隆过滤器


2.1 布隆过滤器概念

  • 问题引入: 在邮件场景下, 有一份垃圾邮件黑名单, 上面有10亿的发件箱. 现在又来了一份邮件, 请判断是否在黑名单里.
  • 分析: 在大数据场景下, 当然是使用位图来进行, 但是, 怎样确定位图的大小呢? 我们知道位图是有限的, 而邮件箱的名称可无上限. 那么我们可以按照哈希的处理方法, 将数据取余再放入. 但是此时不同名称的邮箱哈希值可能相同又有可能会造成误判. 故, 我们可以将一个邮件箱名称分别进行多种不同的哈希函数, 产生不同的哈希值再放入. 那么一个邮箱就会被放入多出位图点. 这样就能很大程度保证正确性

2.2 布隆过滤器插入

一个可能的位图如图

[数据结构] 位图&布隆过滤器_第2张图片
如果我们要映射一个值到布隆过滤器中,我们需要使用多个不同的哈希函数生成多个哈希值,并对每个生成的哈希值指向的 bit 位置 1,例如针对值 “baidu” 和三个不同的哈希函数分别生成了哈希值 1、4、7,则上图转变为:
[数据结构] 位图&布隆过滤器_第3张图片
我们现在再存一个值 “tencent”,如果哈希函数返回 3、4、8 的话,图继续变为:
[数据结构] 位图&布隆过滤器_第4张图片
这样就可查找tencent baidu是否在位图中, 可以注意的是: 若有一个邮箱的hash值为3, 4, 7 那么再去查询返回的也是存在. 这就涉及到布隆过滤器的一大缺点, 即: 误判

  • 若查找不存在, 则真不存在
  • 若查找存在, 则有可能存在

2.3 布隆过滤器查找删除

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

布隆过滤器不能直接支持删除工作,因为在删除一个元素时,可能会影响其他元素
比如:删除上图中"tencent"元素,如果直接将该元素所对应的二进制比特位置0,“baidu”元素也被删除了,因为这两个元素在多个哈希函数计算出的比特位上刚好有重叠

一种支持删除的方法:将布隆过滤器中的每个比特位扩展成一个小的计数器,插入元素时给k个计数器(k个哈希函数计算出的哈希地址)加一,删除元素时,给k个计数器减一,通过多占用几倍存储空间的代价来增加删除操作

2.4 布隆过滤器优缺点

布隆过滤器优点

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

布隆过滤器缺陷

  1. 有误判率,即存在假阳性(False Position),即不能准确判断元素是否在集合中(补救方法:再建立一个白名单,存储可能会误判的数据)
  2. 不能获取元素本身
  3. 一般情况下不能从布隆过滤器中删除元素

3. 海量数据处理


通过位图和布隆过滤器的学习, 我们知道了他们适用于海量数据处理的场景, 下面就来介绍一些场景以及应对措施

  1. 给定100亿个整数,设计算法找到只出现一次的整数?
    思路: 应对海量数据应当想到位图来解决, 可位图只能表示在与不在, 故使用两个位图, 出现零次两个位图对应部分都为0, 出现一次第一个位图为1, 出现多次则第二个位图为1. 即可解决

  2. 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
    思路: 通过位图解决, 整数范围最大42亿bit, 即5亿byte, 即0.5G = 500M, 故只需500M就能放下第一个文件的数据, 在将第二个文件的数据在第一个文件中去比较.

  3. 位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数
    思路: 同问题1, 出现零次 0 0, 出现一次 0 1, 出现两次 1 0, 两次以上 1 1

  4. 给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出近似算法和精确算法
    近似算法: 使用布隆过滤器. 将一个文件的所有数据放入布隆过滤器, 之后一次将第二个文件数据跟第一个文件进行比较. 问题: 有些找到的交集可能不存在.
    精确算法: 使用哈希切分. 将两个文件的数据分别进行哈希运算, 将得到hash值相同的数据分别分到一个文件中. 这样再到hash值相同的两个文件进行比较, 如将第一个放入set , 在遍历另一个. 如图所示
    [数据结构] 位图&布隆过滤器_第5张图片

  5. 给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址?如何找到top K的IP?如何直接用Linux系统命令实现?
    思路:
    找到出现次数最多的IP地址: 使用哈希拆分, 将再对切分的小文件统计次数 i = hashfunc(ip) % 1000 ,模1000, 切分成1000份平均每份100M, 内存可以放下, 再使用map处理. (注意不能平均切分, 平均切分一个ip可能会在多个小文件中, map统计出一个文件中的次数就是不准确的).
    找到top K的IP: 使用k大小的堆即可

[数据结构] 位图&布隆过滤器_第6张图片
[数据结构] 位图&布隆过滤器_第7张图片

你可能感兴趣的:(数据结构,算法)