题目
- 原文:
Given an input file with four billion integers, provide an algorithm to generate an integer which is not contained in the file. Assume you have 1 GB of memory.
FOLLOW UP
What if you have only 10 MB of memory? - 译文:
给你一个文件,里面包含40亿个整数,写一个算法找出该文件中不包含的一个整数, 假设你有1GB内存可用。
更进一步,
如果你只有10MB的内存呢?
分析
首先我们看下40亿个整数大概占用多少内存?
40 * 10^8 * 4B = 16GB
这个明显无法一次性装入内存中。但是,如果我们用计算机中的一位来表示某个数出现与否, 就可以减少内存使用量。比如在一块连续的内存区域,19出现,则将第19位置1。 这个就是Bit Map算法。此时所需内存是:
40 * 10^8 b = 5 * 10^8 B = 0.5GB
即使有负数我们也是可以用1M完成的,代码见代码块1。
但是如果我们只有10MB的内存,明显使用Bit Map也无济于事了。 内存这么小,注定只能分块处理。我们可以将这么多的数据分成许多块, 比如每一个块的大小是2000,那么第一块保存的就是0到1999的数,第2块保存的就是2000 到3999的数……实际上我们并不保存这些数,而是给每一个块设置一个计数器。 这样每读入一个数,我们就在它所在的块对应的计数器加1。处理结束之后, 我们找到一个块,它的计数器值小于块大小(2000), 说明了这一段里面一定有数字是文件中所不包含的。然后我们单独处理这个块即可。【注意该方法仅支持没有重复数字出现的情况!】
接下来我们就可以用Bit Map算法了。我们再遍历一遍数据, 把落在这个块的数对应的位置1(我们要先把这个数通过求模,归约到0到blocksize之间)。 最后我们找到这个块中第一个为0的位,其对应的数就是一个没有出现在该文件中的数,代码见代码块2.
代码
代码块1
#include<iostream>
#include<cstdio>
using namespace std;
int main() {
freopen("C:/Users/Iris/Desktop/12.3.in", "r", stdin);
//int_len is the num of every int can storage bit
//and bit_len is how many int we need
int int_len = sizeof(int) * 8;
int bit_len = 0xFFFFFFFF / int_len;
int* bit = new int[bit_len];
int v;
while(scanf("%d", &v) != EOF) {
bit[v / int_len] |= 1 << (v % int_len);
}
bool bfind = false;
for(int i = 0; i < bit_len; i++) {
for(int j = 0; j < int_len; j++) {
if((bit[i] & (1 << j)) == 0) {
cout << i*int_len+j << endl;
bfind = true;
break;
}
}
if(bfind) {
break;
}
}
delete[] bit;
fclose(stdin);
return 0;
}
代码库2:
#include<iostream>
#include<cstdio>
using namespace std;
int main() {
freopen("C:/Users/Iris/Desktop/12.3.in", "r", stdin);
int total_num = 20000;
int block_size = 2000;
int block_num = total_num / block_size;
int block_count[block_num] = {0};
int int_len = sizeof(int) * 8;
int block[block_size/int_len] = {0};
int v;
while(scanf("%d", &v) != EOF) {
++block_count[v/block_size];
}
fclose(stdin);
int i;
for(i = 0; i < block_num; i++) {
if(block_count[i] != block_size) {
break;
}
}
freopen("C:/Users/Iris/Desktop/12.3.in", "r", stdin);
while(scanf("%d", &v) != EOF) {
if(v >= (block_size * i) && v < (block_size * (i+1))) {
block[(v % block_size)/int_len] |= (1 << (v % block_size)%int_len);
}
}
bool bfind = false;
for(int j = 0; j < block_size/int_len; j++) {
for(int k = 0; k < int_len; k++) {
if((block[j] & (1 << k)) == 0) {
cout << block_size * i + int_len * j +k << endl;
bfind = true;
break;
}
}
if(bfind) {
break;
}
}
fclose(stdin);
return 0;
}
结果
注意
这里之所以写成int block_count[block_num] = {0};而没有用int* = new int[]的形式是因为我用的DevC++编译器在new int[]会有时不会自动初始化为0,但是vs没有这个问题。