• 算法通关村第十五关——从40亿个数中产生一个不存在的数的处理方法


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

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

    解题中心思想:存储的不是这40亿个数据本身,而是其对应的位置。

    本题不用写代码,能把方法过程说清楚就可以。

    1.1 位图存储大数据的原理

    方法8 bit1B,一个32位整数需要4B的存储空间,40亿个数就是 40亿 * 4B,约为16GB,用位图来做的话会更节省空间,因为位图的每个位置只能用0或1进行状态表示,这样就只需要40亿 / 8 = 5亿字节,也就是大约500M的存储空间。

    过程:具体来做就是先遍历这40亿个数,并把遍历的每个数在位图上的相对位置设置为1。这40亿个数遍历结束后,开始遍历位图,看看哪个位置上的状态为0,就说明这个位置对应的数没有在40亿个数中出现,位图遍历结束后就能得到所有未在40亿个数中出现过的数。

    1.2 使用10MB来存储呢?

    如果使用10MB来存储,位图也搞不定了,这个时候就得使用分块思想,用时间换空间,通过两次遍历来处理。

    40亿个数需要约500MB的空间,如果只有10MB的空间,至少需要50个块才可以。一般划分块都使用2的幂次方的整数倍,此处划分为64个块是合理的。

    首先将 0 − 2 32 0-2^{32} 0232 这个范围的数平均分成64个区间,每个区间是67 108 864个数,因为一共只有40亿个数,所以在统计每一个区间上的数有多少时,肯定会有至少一个区间上的计数小于67 108 864。利用这一点可以找出其中一个没出现过的数。具体过程如下:

    第一次遍历:先申请长度位64的整型数组countArr[0, ..., 63]countArr[i]用来统计区间i上的数有多少。遍历40亿个数,跟去当前数是多少来决定哪一个区间上的计数增加。比如,如果当前数为2 567 278 1892 567 278 189 / 67 108 864 = 38 ,所以第38个区间上计数增加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 / 67108864 = == 37),其他区间的数全部忽略。
    3. 如果步骤2的num在第37区间上,将bitArr[num - 67108864 * 37]的值设置为1,也就是只做第37区间上的数的bitArr映射。
    4. 遍历完40亿个数之后,在bitArr上必然存在没被设置成1的位置,假设第i个位置上的值没被设置成1,那么67108864 * 37 + i这个数就是一个没出现过的数

    步骤小结:

    • 根据 10MB 的内存限制,确定统计区间的大小,就是第二次遍历时的 bitArr 大小。
    • 利用区间计数的方式,找到那个计数不足的区间,这个区间上肯定有没出现的数。
    • 对这个区间上的数做 bit map 映射,再遍历bit map,找到一个没出现的数即可。

    1.3 如何确定分块的区间

    上面的例子中,采用两次遍历,第一次将数据分成64块刚好解决问题,为什么不是128块,32块,16块或者其他块数呢?

    这是主要为了保障第二次遍历时每个块都能放进这10MB的空间中。 2 23 < 10 M B < 2 24 2^{23} < 10MB < 2^{24} 223<10MB<224,而 2 23 = 8388608 2^{23} = 8388608 223=8388608 大约是8MB,也就是说我们一次的分块大小只能为8MB左右。我们在第二次遍历时分成64块刚好满足要求,这是最少得分成64块,当然如果分成128块、256块也是可以的。

  • 相关阅读:
    全新锂电池充电板,让充电更加安全
    9.1.2 浮点数类型
    c++ vector
    【算法集训专题攻克篇】第十二篇之链表
    前端食堂技术周刊第 99 期:Remix 2.0、v0、2023 组件库盘点、TS Config 备忘录、Hello 算法
    云计算 2月26号 (进程管理和常用命令)
    端口探测技术总结
    图片转文字在线怎么转换?在线转换方法分享
    算法leetcode|85. 最大矩形(rust重拳出击)
    期权定价模型系列【6】:欧式期权、百慕大期权、美式期权的定价模型与对比【蒙特卡洛模拟、二叉树模型】
  • 原文地址:https://blog.csdn.net/qq_41488033/article/details/132865638