我的面试问题是这样的:

给定一个包含40亿个整数的输入文件,提供一种算法来生成一个文件中不包含的整数。假设您有1gb内存。如果你只有10mb的内存,你会怎么做。

我的分析:

文件大小为4×109×4 bytes = 16gb。

我们可以进行外部排序,从而知道整数的范围。

我的问题是,在已排序的大整数集中检测缺失整数的最佳方法是什么?

我的理解(看完所有答案后):

假设我们讨论的是32位整数,有232 = 4*109个不同的整数。

情况1:我们有1gb = 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);
        }
    }
}

情形二:10mb内存= 10 * 106 * 8bits = 8000万bits

Solution: For all possible 16-bit prefixes, there are 216 number of integers = 65536, we need 216 * 4 * 8 = 2 million bits. We need build 65536 buckets. For each bucket, we need 4 bytes holding all possibilities because the worst case is all the 4 billion integers belong to the same bucket. Build the counter of each bucket through the first pass through the file. Scan the buckets, find the first one who has less than 65536 hit. Build new buckets whose high 16-bit prefixes are we found in step2 through second pass of the file Scan the buckets built in step3, find the first bucket which doesnt have a hit. The code is very similar to above one.

结论: 我们通过增加文件传递来减少内存。


为那些迟到的人澄清一下:这个问题,正如所问的,并不是说有一个整数没有包含在文件中——至少大多数人不是这么理解的。不过,评论线程中的许多评论都是关于任务的变化。不幸的是,把它引入评论线程的评论后来被它的作者删除了,所以现在看起来它的孤儿回复只是误解了一切。这很让人困惑,抱歉。


当前回答

也许我完全没有理解这个问题的重点,但是您想从一个已排序的整数文件中找到一个丢失的整数吗?

喔…真的吗?让我们想想这样的文件会是什么样子:

1 2 3 4 5 6…第一个丢失的号码……等。

这个问题的解决办法似乎微不足道。

其他回答

我将回答1gb版本:

这个问题没有足够的信息,所以我将先说明一些假设:

整数为32位,取值范围为-2,147,483,648 ~ 2,147,483,647。

伪代码:

var bitArray = new bit[4294967296];  // 0.5 GB, initialized to all 0s.

foreach (var number in file) {
    bitArray[number + 2147483648] = 1;   // Shift all numbers so they start at 0.
}

for (var i = 0; i < 4294967296; i++) {
    if (bitArray[i] == 0) {
        return i - 2147483648;
    }
}

检查输入文件的大小,然后输出任何过大而无法用该大小的文件表示的数字。这似乎是一个廉价的技巧,但它是一个创造性的解决面试问题的方法,它巧妙地避开了记忆问题,从技术上讲,它是O(n)。

void maxNum(ulong filesize)
{
    ulong bitcount = filesize * 8; //number of bits in file

    for (ulong i = 0; i < bitcount; i++)
    {
        Console.Write(9);
    }
}

应该打印10位计数- 1,这将永远大于2位计数。从技术上讲,你必须打败的数字是2 bitcount -(4 * 109 - 1),因为你知道文件中还有(40亿- 1)个其他整数,即使使用完美的压缩,它们也会占用至少1位。

我想出了下面的算法。

我的想法是:遍历整个整数文件一次,对每个位位置数0和1。0和1的数量必须是2^(numOfBits)/2,因此,如果数量比预期的少,我们可以使用我们的结果数。

例如,假设整数是32位,那么我们需要

int[] ones = new int[32];
int[] zeroes = new int[32];

对于每个数字,我们必须迭代32位,并增加0或1的值:

for(int i = 0; i < 32; i++){
   ones[i] += (val>>i&0x1); 
   zeroes[i] += (val>>i&0x1)==1?0:1;
}

最后,在文件处理后:

int res = 0;
for(int i = 0; i < 32; i++){
   if(ones[i] < (long)1<<31)res|=1<<i;
}
return res;

注意:在某些语言中(如Java) 1<<31是负数,因此,(长)1<<31是正确的方法

由于问题没有指定我们必须找到文件中不存在的最小数字,我们可以生成一个比输入文件本身更长的数字。:)

您可以使用位标志来标记一个整数是否存在。

遍历整个文件后,扫描每个位以确定数字是否存在。

假设每个整数是32位,如果进行了位标记,它们将方便地放入1gb RAM中。