bit_set位图|布隆过滤器

位图

对于海量整形数据的处理,通常是上百个G的代码。

通常有如下的应用:

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

如果将数据加载到内存中,运用基本数据结构处理,那就需要百G的内存,这是非常庞大的。

例如:要在40亿的无符号整型数据中,判断某个数在不在。

判断在否只需要0或1标记,利用hash直接定址法或hash映射,对每一个整形开辟一个char的空间。简单计算空间利用: 无符号整形的范围是2^32 约有42亿9千万 也就是开辟42亿9千万字节/大约要4G的内存空间......

优化:一个字节有8个bit位,每一个bit位记录0/1标记在否  也就是需要42亿9千万的bit位  计算得需要大约500M的内存

bitset的框架

首先要对数据处理的范围0-N开空间。bitset需要开N/32 + 1的整形来容纳这些数据。(1个整形=32bit)。这里我们用vector容器作为底层。

位图的成员函数有set(将某位置为1) reset(将某位置为0)  test(判断是否存在 1为存在,0为不存在)      flip(翻转指定位)

构造函数

提前开 N/32 + 1的整形空间,并且初始化为全0.

bit_set位图|布隆过滤器_第1张图片

set

1.计算在第i个int的类型

2.计算在该int类型上j的bit位上

3.利用或运算 ,将第j位置为1

 bit_set位图|布隆过滤器_第2张图片

bit_set位图|布隆过滤器_第3张图片

reset

与set相反,将某一位置为0

1.计算i _bits上

2.计算_bits的jbit位

3.与运算 与上 第j位的0,其它位的全1    ~(1<

bit_set位图|布隆过滤器_第4张图片

bit_set位图|布隆过滤器_第5张图片

test()

得到某一位的标记 移位操作和与操作

bit_set位图|布隆过滤器_第6张图片

filp()

翻转指定位

1.计算i _bits上

2.计算_bits的jbit位

3._bits[i]异或上 1的左移j位

bit_set位图|布隆过滤器_第7张图片

位图的优缺点

优点:
判断海量数据的存在,判断出现次数有极大的优势。

节省空间,速度快。本质就是hash映射

缺点:

只能映射整形对于字符串,浮点型不能存储

布隆过滤器

海量处理数据,哈希表的存储占用太多空间。

位图不适用于字符串类型的处理

所以将hash和位图相结合。布隆提出过滤器

设计思路

将字符串通过hash函数映射到位图上。这时会产生hash冲突。对于位图上,判断字符串的结果可以是在于不在。

判断不在是准确的,因为hash映射上标志为0,就代表没有发生hash冲突,也没有这个字符串。 

判断在是不准确的。会发生hash冲突。多个字符串有可能映射到这个位上。

bit_set位图|布隆过滤器_第8张图片

为了降低发生hash冲突的概率 布隆过滤器降低冲突概率

一个值映射到一个位置,容易误判(将在不在判断为在)。将一个值映射到多个位置,多个位的结果判断一个值,这样的误判的概率会大大降低。

结论

布隆过滤器是无法彻底解决误判问题。一个key值 通过hash函数映射到多个位,只能降低误判的概率,无法去除误判。

hash函数个数和布隆过滤器的长度选择

过小的过滤器所有的bit位均为1,查询任何结构都是“可能存在”,起不到过滤的结果。过滤器的长度直接影响误判的结果,越长的过滤器,发生哈希冲突的概率越小,误判几率越小。

hash函数的个数与key映射到位图上的速度有关,如果个数过多,位图上变为1的速度也越快,很快就发生hash碰撞。如果个数过少,误判的几率会变高。

bit_set位图|布隆过滤器_第9张图片

该图展示hash个数和过滤器长度与误判率的关系

由大佬推导出来的公式

bit_set位图|布隆过滤器_第10张图片

当我们设置hash函数为3个时候

k=3,ln2=0.7  m=n*k*0.7 =4.3n;

所以在hash函数为3时,布隆过滤器的长度大致为插入元素个数的4倍。

布隆过滤器的模拟

框架

布隆过滤器要实现成为一个模板类,因为布隆过滤器插入的元素是不固定的(整形,字符串)。而位图要求是整形的存储。所以通过hash函数将字符串转整形。一般情况下,布隆过滤器用于处理字符串,故在模板参数的处理,默认给string的处理。

假定传入三个hash函数,那么布隆过滤器的长度为插入元素的4倍

hash函数传入三个稳定的默认参数。

bit_set位图|布隆过滤器_第11张图片

hash函数的作用就是把字符串数据转化为整形,便于建立映射关系。如果一个字符串能转化出不同的整形值,那么就有不同的映射位置。所以这里通过BKDRHash\APHash\DJBHash

bit_set位图|布隆过滤器_第12张图片

选用hash冲突概率最优的字符串转化hash函数

字符串转hash文章:

字符串转hash

struct BKDRHash
{
	size_t operator()(const string& key)
	{
		// BKDR
		size_t hash = 0;
		for (auto e : key)
		{
			hash *= 31;
			hash += e;
		}

		return hash;
	}
};

struct APHash
{
	size_t operator()(const string& key)
	{
		size_t hash = 0;
		for (size_t i = 0; i < key.size(); i++)
		{
			char ch = key[i];
			if ((i & 1) == 0)
			{
				hash ^= ((hash << 7) ^ ch ^ (hash >> 3));
			}
			else
			{
				hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));
			}
		}
		return hash;
	}
};

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

set

插入字符串“猪八戒”,如果hash函数映射出的三个位置分别为 1   4    7,那么这几个位置为1

bit_set位图|布隆过滤器_第13张图片

此时再存入“tencent”,位图就继续发生改变

bit_set位图|布隆过滤器_第14张图片

bit_set位图|布隆过滤器_第15张图片

test 验证某一位

判断某一位在与否;

判断在——必须三个hash函数对应的位置同时为1   不是一定正确

判断不在——有一个hash函数的映射位为0     一定正确

bit_set位图|布隆过滤器_第16张图片

支持删除么

由于布隆过滤器存在冲突,在删除一个元素时,同时也有可能影响其它元素。

是不支持删除的

  • 一种支持删除的方法(计数删除)

将布隆过滤器中的每位bit扩展成一个小的计数器,插入元素时,k个hash函数映射出的值都+1。删除时,给K个映射位都减一。

缺陷:

存在计数回绕

无法真正确定元素是否真在

布隆过滤器的使用场景

可以误判的场景

比如

用户注册名称,需要判断用户的名称是否被用过

这时候如果一个数据不在 ,那么就是准确的。那如果一个名称通过布隆过滤器被判断出在,(实际上不存在),这时候只要通过数据库的再查找。相比于全部再数据库查找,布隆过滤器极大提高效率。

布隆过滤器的优缺点

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

缺点

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

gitee:位图和布隆过滤器的模拟实现

参考资料:

详解布隆过滤器的原理,使用场景和注意事项 - 知乎 (zhihu.com)

各种字符串Hash函数 - clq - 博客园 (cnblogs.com)

你可能感兴趣的:(C++,哈希算法,算法,c++)