编程珍珠:在 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 &lt; (1/2)^m &lt; 1 对任何正数m(由于0 &lt; 1/2 &lt; 1),我们可以说(1-r^m) &lt; 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 亿个整数的文件中查找缺失的整数的主要内容,如果未能解决你的问题,请参考以下文章

从40亿个整数中找到不存在的一个

漫画:如何判断一个数是否在40亿个整数中?

漫画:如何判断一个数是否在40亿个整数中?

编程珠玑第二章问题A

6.3 40亿个非负整数中找到没出现的数

如何判断一个数是否在40亿个整数中?