IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> [C++数据结构](23)哈希:位图,布隆过滤器,哈希切割 -> 正文阅读

[数据结构与算法][C++数据结构](23)哈希:位图,布隆过滤器,哈希切割

哈希特殊应用

位图(BitMap)

题目

给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断这个数是否在这40亿个数中。

思路

把这40亿个数放进 unordered_set 里?显然不现实,因为光是40亿个整型就需要约15GB空间,更别提哈希表的其他资源消耗,内存空间不够。

注意到我们需要找的是一个数存不存在,记录一个数是否存在只需要一个bit位,这样的话,一个bit位一个地址,采用直接定址法只需要开 2 32 2^{32} 232 (整型能表示的数据个数) 个bit,也就是 512MB 空间

代码实现

注意一些位运算的技巧

template<size_t N> // 非类型模板参数,表示要开的位数
class bit_set
{
public:
	bit_set()
	{
		_bits.resize(N / 8 + 1, 0); // N/8会省略小数,导致开的空间不够,所以+1多开一个字节
	}

	// x位置设为1
	void set(size_t x)
	{
		// 计算x位置在第几个字节
		size_t i = x / 8;
		// 计算在这一字节的第几个bit位
		size_t j = x % 8;

		_bits[i] |= 1 << j;
	}

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

	// 判断x在不在
	bool test(size_t x)
	{
		size_t i = x / 8;
		size_t j = x % 8;
		return _bits[i] & (1 << j);
	}

private:
	vector<char> _bits;
};

其实库里面提供了位图容器

详情参考文档:bitset - C++ Reference (cplusplus.com)


位图应用

  1. 从100亿个整数中,找到只出现一次的整数

重写一个位图,让两个bit表示一个状态,一共三种状态:出现0次、出现1次、出现多次。

这样这个位图需要开的空间是原来位图的2倍,也就是1GB

代码实现:

使用两个bitset实现,就不需要重写映射关系了

template<size_t N>
class two_bitset
{
public:
	//出现0次、1次、多次分别表示为:00、01、10
	void set(size_t x)
	{
		bool in1 = _bs1.test(x);
		bool in2 = _bs2.test(x);
		if (!in1 && !in2) // 如果还未出现
		{
			// 设为01
			_bs2.set(x);
		}
		else if (!in1 && in2) // 如果已经出现一次
		{
			// 设为10
			_bs1.set(x);
			_bs2.reset(x);
		}
		// 如果已经出现2次及以上,不动
	}

	bool isOnce(size_t x)
	{
		return !_bs1.test(x) && _bs2.test(x);
	}

private:
	bitset<N> _bs1;
	bitset<N> _bs2;
};
  1. 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件的交集?

    分别建立两个位图,两个位图中都记为1的值就是交集

  2. 1个文件有100亿个整数,我们有1G内存,从中找出出现次数不超过2次的所有整数。

    在第一题的基础上加一个状态,让 10 表示出现2次,11 表示出现3次即以上

位图优点

节省空间,快

位图缺点

只能针对整型。

布隆过滤器

位图只能针对整型,因为整型可以使用直接定址法,且不存在哈希冲突。如果将字符串转换成整型则可能出现哈希冲突,而位图中没有真正存入这个字符串,无法进行比对,所以不能解决哈希冲突的问题。

布隆过滤器则是对位图的一种改造,可以让一个 key 映射多个地址(使用多个哈希函数),降低哈希冲突的概率。不过它依然有一定的误判概率。在查找的时候,它只能告诉你这个值一定不在可能在

具体映射多少个地址,要看具体情况,映射的越多,哈希冲突的概率越小,但是占用的空间就越大。

如何选择合适的哈希函数个数呢,这里直接给出公式:
m = ? n ln ? p ln ? 2 2 k = m n ln ? 2 m=-\frac{n\ln p}{\ln^22}\\ k=\frac mn\ln2 m=?ln22nlnp?k=nm?ln2
k k k 为哈希函数个数, m m m 为布隆过滤器长度, n n n 为插入元素的个数, p p p 为误报率。

实现

三个哈希函数的版本:

template<class K, size_t M,
	class HashFunc1,
	class HashFunc2,
	class HashFunc3>
class BloomFilter
{
public:
	void Set(const K& key)
	{
		size_t hash1 = HashFunc1()(key) % M;
		size_t hash2 = HashFunc2()(key) % M;
		size_t hash3 = HashFunc3()(key) % M;

		_bs.set(hash1);
		_bs.set(hash2);
		_bs.set(hash3);
	}

	bool Test(const K& key)
	{
		// 三个位只要有一个为0就是不存在
		size_t hash1 = HashFunc1()(key) % M;
		if (!_bs.test(hash1)) return false;
		size_t hash2 = HashFunc2()(key) % M;
		if (!_bs.test(hash2)) return false;
		size_t hash3 = HashFunc3()(key) % M;
		if (!_bs.test(hash3)) return false;
		// 都为1则表示可能存在
		return true;
	}

private:
	bitset<M> _bs;
};

布隆过滤器支持删除吗,如何删除?

  • 可以支持,但是不能直接删,因为删除一个值可能影响其他值,如果两个值的三个映射有交集,那么删除一个另一个就也找不到了。
    • 解决方案:可以使用多个位图来表示一个地址被映射的次数,删除一个值的时候只要在相应的映射位置减1即可,不过这样还存在一个误判,如果删除操作之后这个值的三个映射位置的计数都没有减为0,那么这个值会被误判为存在。
  • 布隆过滤器的一大优点就是节省空间,支持删除需要多开额外的空间,所以它一般不去实现删除操作。

布隆过滤器应用场景

  1. 注册的时候,快速判断一个昵称是否使用过。
    • 不在,是确定的,可以使用
    • 在,则再去数据库确认一遍
  2. 黑名单
    • 不在,同行
    • 在,再去系统确认

哈希切割

问题:给一个超过100G的log文件,log中存着IP地址,如何找出出现次数最多的IP地址。

这是个 kv 模型,不可使用位图和布隆过滤器。

解决方案:创建100个文件,然后依次读取ip,算出 i = HashFunc(ip) % 100,把这个 ip 放到第 i 个文件里面。这样可以保证同样的 ip 进入同一个的文件,最后使用 unordered_map 对每个文件进行统计(统计完一个文件,clear,再统计下一个文件)。

缺陷:

  • 某个小文件太大

    原因:

    • 某个ip相同的太多
    • 映射冲突到这个文件的ip太多

解决方案:

  • 捕获内存不足的异常,说明内存不够
    • 针对这个小文件,换个哈希函数再次进行哈希切割,分成更细小的文件。

应用:给两个文件,分别有100亿个ip,我们只有1G内存,如何找两个文件的交集

解决:创建A0 A1 ··· A999,B0 B1 ··· B999,共2000个小文件,分别计算两个文件中的 i = HashFunc(ip) % 1000,分别放到Ai,Bi中,最后对每一对小文件求交集。

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-09-21 00:52:38  更:2022-09-21 00:53:13 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/28 18:47:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计