【发布时间】:2011-07-28 03:52:29
【问题描述】:
在2.6节和问题2中,原来的问题是这样的:
“给定一个包含 4,300,000,000 个 32 位整数的顺序文件,如何找到至少出现两次的文件?”
我对这个练习的问题是:上述问题的技巧是什么,这个问题属于哪种通用算法类别?
【问题讨论】:
-
书中给出的解决方案是二分查找
标签: algorithm programming-pearls
在2.6节和问题2中,原来的问题是这样的:
“给定一个包含 4,300,000,000 个 32 位整数的顺序文件,如何找到至少出现两次的文件?”
我对这个练习的问题是:上述问题的技巧是什么,这个问题属于哪种通用算法类别?
【问题讨论】:
标签: algorithm programming-pearls
鸽笼原则——如果你在 M 个鸽笼中有 N 只鸽子,并且 N>M,那么一个洞里至少有 2 只鸽子。这组 32 位整数是我们的 2^32 个鸽笼,我们文件中的 43 亿个数字是鸽子。由于 4.3x10^9 > 2^32,我们知道有重复。
您可以应用此原则来测试我们正在寻找的副本是否在数字的子集中,代价是读取整个文件,而不是一次加载超过一点到 RAM 中 - 只需计算您在测试范围内看到数字的次数,并与该范围内的整数总数进行比较。例如,要检查 1,000,000 和 2,000,000(含)之间的重复项:
int pigeons = 0;
int pigeonholes = 2000000 - 1000000 + 1; // include both fenceposts
for (each number N in file) {
if ( N >= 1000000 && N <= 2000000 ) {
pigeons++
}
}
if (pigeons > pigeonholes) {
// one of the duplicates is between 1,000,000 and 2,000,000
// try again with a narrower range
}
选择要检查的范围有多大与要读取 16GB 数据的次数取决于您:)
就一般算法类别而言,这是一个组合(关于计数的数学)问题。
【讨论】:
对整数进行排序并循环遍历它们以查看连续整数是否重复。如果您想在内存中执行此操作,则需要 16GB 内存,这在当今的机器上是可能的。如果这不可能,您可以使用合并排序对数字进行排序,并将中间数组存储到磁盘。
我的第一次实现尝试是使用来自 unix 的 sort 和 uniq 命令。
【讨论】:
如果你的意思是 32 位正整数, 我认为这个问题不需要一些特殊的算法 或技巧来解决。只需简单的观察即可得出预期的解决方案。
我的观察是这样的,顺序文件将只包含 32 位整数(从 0 到 2 ^ 31 - 1)。假设你把所有的 在该文件中,您将得到 2 ^ 31 行。你可以看到 如果你再次输入这些正整数,你将得到 2 ^ 31 * 2 行 并且小于 4,300,000,000。
因此,答案是从 0 到 2 ^ 31 - 1 的整个正整数。
【讨论】:
创建一个长度为 2^32 位(初始化为零)的位数组,大小约为 512MB,适合任何现代机器的 RAM。
开始读取文件,int by int,检查与int的值相同索引的位,如果该位被设置,则发现重复,如果为零,则设置为1并继续下一个int来自文件。
诀窍是找到合适的数据结构和算法。在这种情况下,所有内容都可以通过合适的数据结构装入 RAM,并且可以使用简单高效的算法。
如果数字是 int64,您需要找到合适的排序策略或进行多次传递,具体取决于您有多少可用的额外存储空间。
【讨论】: