题目:
40亿 大整数,组成了一个大文件。
想找到其中的 上中位数该怎么办?
内存:10MB,怎么办?
内存:20K,怎么办?
内存:有限几个字符,怎么办?
条件:按行读取文件,读取操作不占用内存。
应该具备的能力:
2^k = ? 应该都能够熟记,达到反射性反应的程度。
字节数 对应计算机中的 容量(T, G, M, K)
内存只有 10MB 的情况
接下来我们来解题:
看到大数据容量限制的,首先想到的是从范围入手。
1. 数据是 有符号? / 无符号?
2. 我们知道一个 4字节的无符号整数 范围为:0~42亿
那么我们可以用一个 unsigned int 来表示一个数出现的次数
(一个数最多出现 40亿 次,故能够表示,不会溢出)
3. 我们来计算 10MB 内存可以存几个 unsigned int 的数。 => 250万
因此我们知道 10MB 内存足够我们在 250W 范围内进行 精细的词频统计(每个数出现几次)
4. 0~42亿 内总共有 1680个 250W 范围的段。因此我们将 42亿 的范围按照 250W 进行分段。([0, 250w), [250w,500w)...)
建立一个 1680 的数组,遍历大文件一次,来统计每个段中出现数的个数。
count[i] 就表示在第 i 段的 连续的250W 的范围内,出现了多少个数。
比如:一个数字值为10亿,那么它应该就在 count[400] 这个段中,那么进行操作 count[400]++ 即可。
5. 对 count 进行累加,直到 sum >= 20亿。这样我们就能确定第 20亿 个数是来自哪个范围的。
6. 释放该数组,再次遍历大文件,利用 10MB 的空间对该范围内的数进行 精细的词频统计。这样便能够找到中位数了。
由此可见,以上做法是借用了 桶排序 的思想。
总结:
1. 利用限制的范围计算出我们能够在多大的范围内进行 精细的词频统计。
2. 利用 精细的范围 对整体范围进行划分,建立一个粗略的统计数组。
3. 遍历大文件,找出中位数粗略的范围。
4. 对该粗略的范围进行精细的统计。
内存只有 20K 的情况:
我们依然用上面的方法来分析:
20K 的内存可以支持 5000范围大小 的精细词频统计。
然后我们用 40亿 / 5000 => 发现该数值已经怨愿你大于 5000 了,我们根本无法进行粗略范围的统计。
于是,我们不妨逆过来思考。
直接从 粗略的范围 出发进行统计。将其分成 5000 份。
我们依然能够得知 中位数 是在哪个部分。然后看该范围能否被精细统计。
若不能一直循环下去。
内存只有 有限几个字符 的情况(比如就8个字节,两个变量):
采用二分的方法。最多二分 32 次,即需要读 32 次文件。
1. 首先对 0~42 亿二分(注意是对整个范围进行二分),用一个变量 k 记下当前的值是多少。
遍历文件一边遍,计算当前小于当前值得数据个数 n。
2. 若 n < 21亿,该数在小半部分。若 n > 21亿,该数在大部分。
以此循环下去。