算法通关村——海量数据处理办法

从40个亿中产生一个不存在的整数

题目:给定一个输入文件,包含40亿个非负整数,请设计一个算法,产生一个不存在该文件中的整数,假设你由1GB的内存来完成任务。

用位图去存储大数据

在数据量很大时,采用位方式(俗称位图)存储数据是常用的思路。

我们可以使用bit map的方式来表示数出现的情况。具体来说,是申请一个长度为4 294 967 295(32位无符号整数的十进制最大值,即0xFFFFFFFF)的bit类型的数组bitArr(就是boolean类型),bitArr上的每个位置只可以表示0或1状态,0表示没有这个数,1表示有这个数。在遍历40亿个无符号数时,遇到所有的数时,就把bitArr相应的位置的值设置为1。例如遇到1000,就将bitArr[1000]设置为1。8个bit为1B,所以长度为4 294 967 295的bit类型的数组占用500MB空间,这就满足题目给定的要求了。

我们可以先遍历一次数据,将所有的数都存入位图中,再依次遍历bitArr,如果哪个位置上的值没被设置为1,这个数就不在40亿个数中。例如,发现bitArr[8001] = 0,那么8001就是没出现过的数,遍历完bitArr后,所有没出现过的数就都找出来了。

位存储的核心是:我们存储的不是这40亿个数据本身,而是其对应的位置。

用10MB来存储

把上面的问题进阶一下,如果只用10MB的内存,那么位图也不能搞定了,需要另寻他法。

这里使用分块的思想,时间换空间,通过两次遍历来搞定。

40亿 / 8字节 = 5亿字节,大约0.5GB也就是500MB的空间才能存下所有的数。如果只有10MB的空间,至少需要50个块才可以。

一般来说,划分都是使用2的整数倍,因此划分为64个块是合理的。

首先,将0~4 294 967 295这个范围平均划分为64个区间,每个区间是67 108 864个数,例如:

  • 第0 区间(0~67 108 863)
  • 第 1 区间(67 108 864~134 217 728)
  • 第 i 区间(67 108 864´I~67 108 864´(i+1)-1)
  • ……
  • 第 63 区间(4 227 858 432~4 294 967 295)

因为一共只有40亿个数,所以如果统计落在每一个区间上的数有多少,肯定有至少一个区间上的计数少于67 108 864。利用这一点可以找到其中一个没出现过的数。具体过程如下:

第一次遍历,先申请长度为 64 的整型数组 countArr[0…63],countArr[i]用来统计区间 i 上的数有多少。遍历 40 亿个数,根据当前数是多少来决定哪一个区间上的计数增加。例如,如果当前数是 3 422 552 090 , 3 422 552 090/67 108 864=51 , 所以第 51 区间上的计数增加countArr[51]++。遍历完 40 亿个数之后,遍历 countArr,必然会有某一个位置上的值(countArr[i]) 小于 67 108 864,表示第 i 区间上至少有一个数没出现过。我们肯定会找到至少一个这样的区间。

此时使用的内存就是countArr 的大小(64*4B),是非常小的。

假设找到第 37 区间上的计数小于 67 108 864,那么我们对这40亿个数据进行第二次遍历:

  1. 申请长度为 67 108 864 的 bit map,这占用大约 8MB 的空间,记为 bitArr[0…67108863]。
  2. 遍历这 40 亿个数,此时的遍历只关注落在第 37 区间上的数,记为 num(num满足num/67 108 864==37),其他区间的数全部忽略。
  3. 如果步骤 2 的 num 在第 37 区间上,将 bitArr[num - 67108864*37]的值设置为 1,也就是只做第 37 区间上的数的 bitArr 映射。
  4. 遍历完 40 亿个数之后,在 bitArr 上必然存在没被设置成 1 的位置,假设第 i 个位置上的值没设置成 1,那么 67 108 864´37+i 这个数就是一个没出现过的数。

总之就是先分块,根据内存限制去决定分块的数量,以及统计区间的大小,即第二次遍历的时候bitArr大小,然后利用区间计数的方式,找到哪个计数不足的区间,这个区间上肯定有没出现的数,最后最这个区间做位图映射,遍历一遍后就能找到一次都没出现的数了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Molche

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值