我的面试问题是这样的:

给定一个包含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.

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


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


当前回答

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

其他回答

老问题了,但我想知道“非功能性”需求。在我看来,应该给出一个线索——如果这个问题是在其他地方问的,而不是在一本书里,然后继续讨论所有的可能性的利弊。通常情况下,这似乎是在工作面试中问的,让我困惑的是,在不知道软要求的情况下,不可能给出一个明确的答案,即。“查找缺失的数字一定非常快,因为它一秒钟要使用x次。”

我想这样的问题或许可以给出一个合理的答案。

我将所有数字归并排序到一个新文件中,每个int使用4个字节。当然,一开始做起来会很慢。但是它可以用很小的内存量来完成(你不需要把所有内存都保存在RAM中) 使用二进制搜索检查数字是否存在于预排序文件中。因为每个值仍然是4个字节,这没有问题

缺点:

文件大小 第一次排序很慢——但只需要一次

优点:

查找起来非常快

这又是一个非常适合写书的问题。但我认为,当要解决的问题还不完全清楚时,在寻求单一的最佳解决方案时,这是一个奇怪的问题。

统计信息算法解决这个问题的次数比确定性方法少。

如果允许使用非常大的整数,则可以生成一个在O(1)时间内可能唯一的数字。像GUID这样的伪随机128位整数只会与集合中现有的40亿个整数中的一个发生碰撞,这种情况的概率不到640亿亿亿分之一。

If integers are limited to 32 bits then one can generate a number that is likely to be unique in a single pass using much less than 10 MB. The odds that a pseudo-random 32-bit integer will collide with one of the 4 billion existing integers is about 93% (4e9 / 2^32). The odds that 1000 pseudo-random integers will all collide is less than one in 12,000 billion billion billion (odds-of-one-collision ^ 1000). So if a program maintains a data structure containing 1000 pseudo-random candidates and iterates through the known integers, eliminating matches from the candidates, it is all but certain to find at least one integer that is not in the file.

根据原题中目前的措辞,最简单的解决方法是:

找到文件中的最大值,然后加上1。

从文件中删除空白和非数字字符,并追加1。您的文件现在包含原始文件中没有列出的单个数字。

来自Reddit,作者:Carbonetc。

你不需要对它们排序,只需要重复划分它们的子集。

The first step is like the first pass of a quicksort. Pick one of the integers, x, and using it make a pass through the array to put all the values less than x to its left and values more than x to its right. Find which side of x has the greatest number of available slots (integers not in the list). This is easily computable by comparing the value of x with its position. Then repeat the partition on the sub-list on that side of x. Then repeat the partition on the sub-sub list with the greatest number of available integers, etc. Total number of compares to get down to an empty range should be about 4 billion, give or take.