C++海量数据处理——判读40亿数字中是否有某个数字(位图BitMap)

判读40亿数字中是否有某个数字(位图BitMap)

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

需求分析:Int类型在Java中的存储占用4个Byte,32Bit,如果在内存中定义40亿个int类型数组来读取文件,占用大小:(40*100000000*4/1024/1024/1024)G=14.901G。这已经远远超出了机器的内存限制,这时候常规思路是,将数据存在磁盘,分批读取数据到内存中,但这样就会产生磁盘IO,对于追求速率的我们来讲,这种情况我们不考虑。

那么我们怎么来查找呢。既然是不重复,就说明整数要么就不出现,要么就出现一次。整数的最大值是42亿多,即2^32。此时我们就可以用每一位来表示这个数存在或者不存在。如果将32位为一个编号时,原本16G的数据使用位图可以节省到500M(下面计算说明)的空间。大概我们刚刚学过哈希表,用访问地址的方法来快速的查找出地址对应的值。这里也一样,用到了哈希表中的新的解决海量数据的方法—位图

  • 对于数据来说,某个数据有还是没有,我们可以用0和1来表示,这正好符合二进制的0 1属性,我们知道1 Int = 4 Byte = 32Bit,这种情况下,我们需要的内存空间是,(40*100000000/8/1024/1024)M = 476.8

那么问题来了?什么是位图呢?

  • 我们用每一位标志这个数存在的状态,设为0(不存在)和1(存在)
  • 40亿条数据为无符号整形,其最大取值范围为0到0xffffffff(2^23个无符号整形数据),若每个数字对应一个比特位
  • 1个int占4字节即4*8=32位,那么我们只需要申请一个int数组长度为 int tmp[1+N/32]即可存储完这些数据,其中N代表要进行查找的总数

如何判断int数字在tmp数组的哪个下标

这个其实可以通过直接除以32取整数部分,例如:整数8除以32取整等于0,那么8就在tmp[0]上。另外,我们如何知道了8在tmp[0]中的32个位中的哪个位,这种情况直接mod上32就ok,又如整数8,在tmp[0]中的第8 mod上32等于8,那么整数8就在tmp[0]中的第八个bit位(从右边数起)时间复杂度为o(n) 查找时间为o(1)

在这里插入图片描述
位图的基本结构:
是一个size_t类型的vector数组;

vector<size_t> _array;

位图的基本函数:

在这里插入图片描述
对于判断一个无符号整数,是否存在这40亿个数中。
(1)需要存入这40亿个数,使用Set将对应的40亿个位置为1;
(2)使用Test将判断某个位是否为0或1;
注:位图只是考虑了整数类型


#pragma once
#include<iostream>
#include<vector>

using namespace std;
 

//位图的每一位的0,1标志这个数存在或不存在的状态

class BitMap
{
public:
	BitMap(size_t Size = 1024)
	{
		_array.resize(Size/32+1);
	}

	~BitMap()
	{}
public:
	//将这个数存在的状态置为1
	void Set(const size_t& value)
	{
		size_t index = value>>5;//相当于除以32,取value在数组中的索引
		size_t bit = value % 32;//判断在32位中的哪一位
		_array[index] |= (1<<bit);
	}
	//将这个数不存在的状态置为0
	void Reset(const size_t& value)
	{
		size_t index = value>>5;
		size_t bit = value % 32;
		_array[index] &= (~(1<<bit));//~按位取反
	}
	//测试某个数是否出现过
	bool Test(const size_t& value)
	{
		size_t index = value>>5;
		size_t bit = value % 32;
		return (_array[index] & (1<<bit));
	}
private:
	vector<size_t> _array;
};

 

 

void BitMapTest()

{
	BitMap bm(size_t(-1));   //64位系统下表示的整数的最大值
	bm.Set(10);
	bm.Set(100);
	bm.Set(20);
	bm.Set(500);
	cout<<bm.Test(10)<<endl;
	cout<<bm.Test(200)<<endl;
	cout<<bm.Test(500)<<endl;
	cout<<bm.Test(40)<<endl;
}

判读40亿数字中,统计只出现一次的数(位图BitMap)

== 题目==

10亿int整型数,以及一台可用内存为1GB的机器,时间复杂度要求O(n),统计只出现一次的数?

分析

首先分析多大的内存能够表示10亿的数呢?一个int型占4字节,10亿就是40亿字节(很明显就是4GB),也就是如果完全读入内存需要占用4GB,而题目只给1GB内存,显然不可能将所有数据读入内存。
我们先不考虑时间复杂度,仅考虑解决问题。那么接下来的思路一般有两种。

  • 位图法:用一个bit位来标识一个int整数。
  • 分治法:分批处理这10亿的数。

一种是位图法,如果各位老司机有经验的话很快会想到int整型数是4字节(Byte),也就是32位(bit),如果能用一个bit位来标识一个int整数那么存储空间将大大减少。另一种是分治法,内存有限,我想办法分批读取处理。下面大致分析一下两种思路。

1、位图法(Bitmap)

从上面我们知道,只需要用512MB的内存就能存储所有的int的范围数。

具体方案

那么接下来我们只需要申请一个int数组长度为 int tmp[N/32+1]即可存储完这些数据,其中N代表要进行查找的总数(这里也就是2^32),tmp中的每个元素在内存在占32位可以对应表示十进制数0~31,所以可得到BitMap表:

  • tmp[0]:可表示0~31
  • tmp[1]:可表示32~63
  • tmp[2]可表示64~95
  • ~~

假设这10亿int数据为:6,3,8,32,36,……,那么具体的BitMap表示为:
在这里插入图片描述
(1). 如何判断int数字放在哪一个tmp数组中:将数字直接除以32取整数部分(x/32),例如:整数8除以32取整等于0,那么8就在tmp[0]上;

(2). 如何确定数字放在32个位中的哪个位:将数字mod32取模(x%32)。上例中我们如何确定8在tmp[0]中的32个位中的哪个位,这种情况直接mod上32就ok,又如整数8,在tmp[0]中的第8 mod上32等于8,那么整数8就在tmp[0]中的第八个bit位(从右边数起)。

然后我们怎么统计只出现一次的数呢?每一个数出现的情况我们可以分为三种:0次、1次、大于1次。也就是说我们需要用2个bit位才能表示每个数的出现情况。此时则三种情况分别对应的bit位表示是:00、01、11

我们顺序扫描这10亿的数,在对应的双bit位上标记该数出现的次数。最后取出所有双bit位为01的int型数就可以了。

2、分治法

分治法目前看到的解决方案有哈希分桶(Hash Buckets)和归并排序两种方案。

哈希分桶的思想是先遍历一遍,按照hash分N桶(比如1000桶),映射到不同的文件中。这样平均每个文件就10MB,然后分别处理这1000个文件,找出没有重复的即可。一个相同的数字,绝对不会夸文件,有hash做保证。因为算法具体还不甚了解,这里先不做详细介绍。

参考

1、https://blog.csdn.net/xxpresent/article/details/56488881

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值