编程珍珠:在 40 亿个整数的文件中查找缺失的整数
Posted
技术标签:
【中文标题】编程珍珠:在 40 亿个整数的文件中查找缺失的整数【英文标题】:Programming Pearls: Finding the missing integer in a file of 4 billion integers 【发布时间】:2013-10-24 06:32:45 【问题描述】:问题:输入是一个顺序文件。该文件最多包含 40 亿个整数。找到一个缺失的整数。
解决方案据我了解:
制作两个临时文件,一个以 0 开头,另一个以 1 开头
两个必须之一(4.3B 鸽子洞和 4B 鸽子)小于 2B。 选择文件并在第 2 位重复步骤 1 和 2,然后在第 3 位重复步骤,依此类推..
本次迭代的结束条件是什么?
另外,书中提到算法的效率为 O(n) 但是,
第一次迭代 => n 次探测操作 第二次迭代 => n/2 次探测操作 . . . n + n/2 + n/4 +... 1 => nlogn??
我错过了什么吗?
【问题讨论】:
如果你认为一个整数总是表示为 32 位整数,那么 32 被假定为常数,所以它是O(32n) = O(n)
。顺便说一句,有关于这个问题的讨论:***.com/questions/1642474/…
只有 1 个缺失的整数吗?我的意思是在 N..M 的范围内,只会丢失 1 个值?如果是这样,那么这确实是一个 O(n) 操作,很简单。 N..M 我的意思是,例如,在 10 到 20 的范围内(包括两者),只有 10 到 20 的值丢失,不是 2,不是 3,不是 0,只有 1。
n + n/2 + n/4 + ... + 1
所有 32 位整数的异或为零(也适用于任何其他位数)。因此,如果您有 (2^32)-1 个不同的整数,那么它们的异或就是缺少的那个。但是从这个问题中你是否有 40 亿或 (2^32)-1 个整数并不是很清楚。
@harold "4.3B pigeon-holes and 4B pigeons" 暗示我们没有 2^32 个整数。
【参考方案1】:
您将检查这两个文件并选择元素最少的一个。
您将重复该过程,直到您完成所有 32 位,最后您将拥有一个文件 0 元素。这是丢失的数字之一应该在的地方。因此,如果您一直在跟踪到目前为止已过滤的位,您就会知道该数字应该是多少。
请注意,这是为了查找 a(即“任何”)缺失的数字。如果给定一个包含 40 亿个(不是2^32
(4294967296))整数的(无序)顺序列表,其中一个缺失,您必须找到,这将不起作用,因为您可以在一开始就切断缺失的整数。
还有:
n + n/2 + n/4 + ... 1 <= 2n
不是n log n
。
是geometric sequence和a = n, r = 1/2
,可以用公式计算:
n (1-(1/2)^m)
-------------
1 - (1/2)
由于0 < (1/2)^m < 1
对任何正数m
(由于0 < 1/2 < 1
),我们可以说(1-r^m) < 1
,因此我们可以说最大值是:
n.1
-------
1 - 1/2
n
= ---
1/2
= 2n
【讨论】:
【参考方案2】:如果只有 1 个缺失值,则表示您具有以下条件:
-
文件包含从最低值
N
到最高值 M
的所有数字,但其中 1 个数字除外。
文件不必排序
这些值中只有 1 个缺失(只是确保)
那么解决方法就很简单了:
将文件中的所有数字相加或异或。 将您应该拥有的所有数字相加或异或。 缺少的数字要么是一个减去另一个(在 ADD 的情况下),要么是一个异或另一个。
这是一个您可以尝试的LINQPad 程序:
void Main()
var input = new[] 1, 2, 3, 4, 5, 6, 8, 9, 10 ;
var lowest = input[0];
var highest = input[0];
int xor = 0;
foreach (var value in input)
lowest = Math.Min(lowest, value);
highest = Math.Max(highest, value);
xor ^= value;
int requiredXor = 0;
for (int index = lowest; index <= highest; index++)
requiredXor ^= index;
var missing = xor ^ requiredXor;
missing.Dump();
基本上,它会:
-
对文件中的所有值进行异或运算(值 1)
同时查找最低和最高数字
XOR 所有值从最低到最高(值 2)
对两个值(值 1 和值 2)进行异或运算以找到缺失值
此方法不会检测缺失值是最小值 - 1 还是最大值 + 1,例如,如果文件应该包含 1..10,但缺失 10 或 1,则上述方法找不到。
这个解决方案是 O(2n)(我们将数字循环两次),转换为 O(n)。
这是一个更完整的示例,显示了 ADD 和 XOR 解决方案(同样在 LINQPad 中):
void Main()
var input = new[] 1, 2, 3, 4, 5, 6, 8, 9, 10 ;
MissingXOR(input).Dump("xor");
MissingADD(input).Dump("add");
public static int MissingXOR(int[] input)
var lowest = input[0];
var highest = input[0];
int xor = 0;
foreach (var value in input)
lowest = Math.Min(lowest, value);
highest = Math.Max(highest, value);
xor ^= value;
int requiredXor = 0;
for (int index = lowest; index <= highest; index++)
requiredXor ^= index;
return xor ^ requiredXor;
public static int MissingADD(int[] input)
var lowest = input[0];
var highest = input[0];
int sum = 0;
foreach (var value in input)
lowest = Math.Min(lowest, value);
highest = Math.Max(highest, value);
sum += value;
var sumToHighest = (highest * (highest + 1)) / 2;
var sumToJustBelowLowest = (lowest * (lowest - 1)) / 2;
int requiredSum = sumToHighest - sumToJustBelowLowest;
return requiredSum - sum;
【讨论】:
以上是关于编程珍珠:在 40 亿个整数的文件中查找缺失的整数的主要内容,如果未能解决你的问题,请参考以下文章