【发布时间】:2011-11-01 11:47:49
【问题描述】:
我收到了这个面试问题:
给定一个包含四十亿整数的输入文件,提供一个算法来生成一个不包含在文件中的整数。假设您有 1 GB 内存。跟进如果您只有 10 MB 内存,您会做什么。
我的分析:
文件大小为 4×109×4 字节 = 16 GB。
我们可以进行外部排序,从而让我们知道整数的范围。
我的问题是在已排序的大整数集中检测缺失整数的最佳方法是什么?
我的理解(阅读所有答案后):
假设我们谈论的是 32 位整数,则有 232 = 4*109 个不同的整数。
案例 1:我们有 1 GB = 1 * 109 * 8 位 = 80 亿位内存。
解决办法:
如果我们用一位来表示一个不同的整数,这就足够了。我们不需要排序。
实施:
int radix = 8;
byte[] bitfield = new byte[0xffffffff/radix];
void F() throws FileNotFoundException{
Scanner in = new Scanner(new FileReader("a.txt"));
while(in.hasNextInt()){
int n = in.nextInt();
bitfield[n/radix] |= (1 << (n%radix));
}
for(int i = 0; i< bitfield.lenght; i++){
for(int j =0; j<radix; j++){
if( (bitfield[i] & (1<<j)) == 0) System.out.print(i*radix+j);
}
}
}
案例 2:10 MB 内存 = 10 * 106 * 8 位 = 8000 万位
解决办法:
对于所有可能的 16 位前缀,有 216 个 整数 = 65536,我们需要 216 * 4 * 8 = 200 万位。我们需要构建 65536 个存储桶。对于每个桶,我们需要 4 个字节来保存所有可能性,因为最坏的情况是所有 40 亿个整数都属于同一个桶。
- 通过第一次遍历文件构建每个桶的计数器。
- 扫描桶,找到第一个命中数少于 65536 的桶。
- 构建新的桶,我们在步骤 2 中找到了高 16 位前缀 通过文件的第二遍
- 扫描step3构建的bucket,找到第一个没有的bucket 成功了。
代码和上面的很相似。
结论: 我们通过增加文件传递来减少内存。
对迟到的人的澄清:正如所问的那样,这个问题并不是说文件中不包含确切的一个整数——至少大多数人不是这样解释的。不过,评论线程中的许多 cmet都 讨论了该任务的变体。不幸的是,将其引入评论线程的评论后来被其作者删除,所以现在看起来对它的孤立回复只是误解了一切。非常混乱,抱歉。
【问题讨论】:
-
@trashgod,错了。对于 4294967295 个唯一整数,您将剩下 1 个整数。要找到它,您应该对所有整数求和,然后从预先计算的所有可能整数的总和中减去它。
-
这是“编程珍珠”中的第二颗“珍珠”,我建议您阅读本书中的整个讨论。见books.google.com/…
-
@Richard 一个 64 位的 int 就足够大了。
-
int getMissingNumber(File inputFile) { return 4; }(reference) -
你不能存储从 1 到 2^32 的所有整数的总和并不重要,因为像 C/C++ 这样的语言中的整数类型总是保留关联性和交流性等属性。这意味着虽然总和不是正确的答案,但如果你计算溢出的预期值,溢出的实际总和,然后减去,结果仍然是正确的(只要它本身没有溢出)。
标签: algorithm file search out-of-memory memory-limit