找到一个不是40亿个给定值的整数

时间:2011-08-22 21:11:48

标签: algorithm

这是一个面试问题:

  

给定一个包含40亿个整数的输入文件,提供一个算法来生成一个未包含在文件中的整数。假设您有1 GB内存。如果您只有10 MB的内存,请跟进您的操作。

我的分析:

文件大小为4×10 9 ×4字节= 16 GB。

我们可以进行外部排序,因此我们可以了解整数的范围。我的问题是在排序的大整数集中检测缺失整数的最佳方法是什么?

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

假设我们正在谈论32位整数。有2 ^ 32 = 4 * 10 9 不同的整数。

案例1:我们有1 GB = 1 * 10 9 * 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);
        }
    }
}

案例2:10 MB内存= 10 * 10 6 * 8位= 8000万位

Solution: For all possible 16-bit prefixes, there are 2^16 number of
integers = 65536, we need 2^16 * 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.

step1: Build the counter of each bucket through the first pass through the file.
step2: Scan the buckets, find the first one who has less than 65536 hit.
step3: Build new buckets whose high 16-bit prefixes are we found in step2
through second pass of the file
step4: Scan the buckets built in step3, find the first bucket which doesnt
have a hit.

The code is very similar to above one.

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


对那些迟到的人的澄清:问题,并不是说文件中没有包含一个完整的整数 - 至少不是大多数人解释它的方式。但是,评论主题中的许多评论都是关于任务变体的。不幸的是,向评论主题引入的评论后来被其作者删除了,所以现在看来​​孤立的回复只是误解了一切。这很令人困惑。遗憾。

38 个答案:

答案 0 :(得分:521)

假设“整数”表示32位:拥有10 MB的空间足以让您计算输入文件中具有任何给定16位前缀的数字的数量,一次通过输入文件的所有可能的16位前缀。至少有一个桶的击中次数不会超过2 ^ 16次。做第二遍以找到已经使用该桶中可能的数字。

如果它意味着超过32位,但仍然是有限大小:如上所述,忽略恰好落在(有符号或无符号;您选择的)32位范围之外的所有输入数字

如果“整数”表示数学整数:读取输入一次并跟踪您所见过的最长数字的最大数长度。完成后,输出最大加一一个随机数,其中还有一个数字。 (文件中的一个数字可能是一个超过10 MB的bignum来准确表示,但如果输入是一个文件,那么你至少可以代表适合的任何东西的长度它)。

答案 1 :(得分:194)

统计通知算法使用比确定性方法更少的传递来解决此问题。

如果允许非常大的整数,那么可以生成一个在O(1)时间内可能唯一的数字。像[{3}}这样的伪随机128位整数只会与集合中现有的40亿个整数中的一个冲突,而每640亿亿个案例中只有不到一个。

如果整数限制为32位,则可以使用远小于10 MB的单次传递生成一个可能唯一的数字。伪随机32位整数将与40亿现有整数中的一个冲突的几率约为93%(4e9 / 2 ^ 32)。 1000个伪随机整数将全部碰撞的几率小于12,000亿亿(1个碰撞的概率^ 1000)。因此,如果一个程序维护一个包含1000个伪随机候选者的数据结构并迭代已知的整数,从候选者中消除匹配,那么几乎可以肯定找到至少一个不在该文件中的整数。

答案 2 :(得分:141)

有关此问题的详细讨论已在Jon Bentley“第1栏中讨论过。破解牡蛎”编程珍珠 Addison-Wesley pp.3-10

Bentley讨论了几种方法,包括外部排序,使用多个外部文件进行合并排序等。但Bentley建议的最佳方法是使用bit fields的单程算法,他幽默地称之为“Wonder Sort”:) 遇到这个问题,40亿个数字可以代表:

4 billion bits = (4000000000 / 8) bytes = about 0.466 GB

实现bitset的代码很简单:(取自solutions page

#define BITSPERWORD 32
#define SHIFT 5
#define MASK 0x1F
#define N 10000000
int a[1 + N/BITSPERWORD];

void set(int i) {        a[i>>SHIFT] |=  (1<<(i & MASK)); }
void clr(int i) {        a[i>>SHIFT] &= ~(1<<(i & MASK)); }
int  test(int i){ return a[i>>SHIFT] &   (1<<(i & MASK)); }

Bentley的算法对文件进行单次传递,set在数组中的相应位,然后使用上面的test宏检查此数组,以找到缺少的数字。

如果可用内存小于0.466 GB,Bentley建议使用k-pass算法,该算法根据可用内存将输入划分为范围。举一个非常简单的例子,如果只有1个字节(即处理8个数字的存储器)可用且范围从0到31,我们将其分为0到7,8-15,16-22的范围等等。并在32/8 = 4次传递中处理此范围。

HTH。

答案 3 :(得分:117)

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

答案 4 :(得分:56)

对于1 GB RAM变体,您可以使用位向量。您需要分配40亿位== 500 MB字节数组。对于从输入读取的每个数字,将相应位设置为“1”。完成后,迭代这些位,找到第一个仍为“0”的位。它的索引就是答案。

答案 5 :(得分:45)

如果它们是32位整数(可能选择约40亿个数字接近2 ^ 32),那么40亿个数字的列表将占用最多93%的可能整数(4 * 10 ^ 9 /(2 ^ 32))。因此,如果你创建一个2 ^ 32位的位数组,每个位初始化为零(这将占用2 ^ 29字节~500 MB的RAM;记住一个字节= 2 ^ 3位= 8位),读取你的整数列表并为每个int设置相应的位数组元素,从0到1;然后读取你的位数组并返回仍为0的第一位。

如果RAM较少(~10 MB),则需要稍微修改此解决方案。对于介于0和83886079之间的所有数字,10 MB~83886080位仍足以为位数组做。因此,您可以读取整数列表;并且只在位数组中记录0到83886079之间的#s。如果数字是随机分布的;以极大的概率(大约10^-2592069相差100%)你会发现缺少int)。事实上,如果你只选择数字1到2048(只有256字节的RAM),你仍然会发现一个缺失的数字(99.9999999999999999999999999999999999999999999995%)的压倒性百分比。

但是,让我们说而不是大约有40亿个数字;你有2 ^ 32 - 1个数字和不到10 MB的RAM;因此,任何小范围的整数只有很小的可能性不包含该数字。

如果您确保列表中的每个int都是唯一的,您可以对数字求和并将一个#减去全部总和(1/2)(2 ^ 32) (2 ^ 32 - 1)= 9223372034707292160找到缺少的int。但是,如果int发生两次,则此方法将失败。

但是,你可以随时分而治之。一种天真的方法是读取数组并计算前半部分(0到2 ^ 31-1)和后半部分(2 ^ 31,2 ^ 32)的数字数量。然后选择数字较少的范围并重复将该范围分成两半。 (假如在(2 ^ 31,2 ^ 32)中有两个较少的数字,那么你的下一个搜索将计算范围内的数字(2 ^ 31,3 * 2 ^ 30-1),(3 * 2 ^ 30, 2 ^ 32)。继续重复,直到找到一个数字为零的范围并且你有答案。应该通过数组取O(lg N)~32个读数。

这种方法效率低下。我们在每一步中只使用两个整数(或大约8字节的RAM和一个4字节(32位)整数)。一种更好的方法是划分为sqrt(2 ^ 32)= 2 ^ 16 = 65536个bin,每个bin在bin中有65536个数字。每个bin需要4个字节来存储其计数,因此您需要2 ^ 18个字节= 256 kB。所以bin 0是(0到65535 = 2 ^ 16-1),bin 1是(2 ^ 16 = 65536到2 * 2 ^ 16-1 = 131071),bin 2是(2 * 2 ^ 16 = 131072到3) * 2 ^ 16-1 = 196607)。在python中你会有类似的东西:

import numpy as np
nums_in_bin = np.zeros(65536, dtype=np.uint32)
for N in four_billion_int_array:
    nums_in_bin[N // 65536] += 1
for bin_num, bin_count in enumerate(nums_in_bin):
    if bin_count < 65536:
        break # we have found an incomplete bin with missing ints (bin_num)

通读~40亿整数列表;并计算每个2 ^ 16箱中有多少总量,并找到一个不包含全部65536个数字的incomplete_bin。然后再读一遍40亿的整数列表;但这次只注意整数在那个范围内;当你找到它们时翻转一下。

del nums_in_bin # allow gc to free old 256kB array
from bitarray import bitarray
my_bit_array = bitarray(65536) # 32 kB
my_bit_array.setall(0)
for N in four_billion_int_array:
    if N // 65536 == bin_num:
        my_bit_array[N % 65536] = 1
for i, bit in enumerate(my_bit_array):
    if not bit:
        print bin_num*65536 + i
        break

答案 6 :(得分:37)

为什么要这么复杂?你要求文件中没有的整数吗?

根据指定的规则,您需要存储的唯一内容是您到目前为止在文件中遇到的最大整数。读完整个文件后,返回一个大于1的数字。

没有碰到maxint或任何东西的风险,因为根据规则,对整数的大小或算法返回的数字没有限制。

答案 7 :(得分:31)

这可以使用二进制搜索的变体在很小的空间内解决。

  1. 从允许的数字范围开始,04294967295

  2. 计算中点。

  3. 循环浏览文件,计算有多少数字相等,小于或高于中点值。

  4. 如果没有数字相等,那么你就完成了。中点数就是答案。

  5. 否则,请选择编号最少的范围,并使用此新范围从步骤2开始重复。

  6. 这将需要通过文件进行多达32次线性扫描,但它只会使用几个字节的内存来存储范围和计数。

    这与Henning's solution基本相同,只是它使用两个箱而不是16k。

答案 8 :(得分:27)

编辑好的,这并没有考虑过,因为它假设文件中的整数遵循一些静态分布。显然他们不需要,但即便如此,也应该尝试这个:


有大约43亿个32位整数。我们不知道它们是如何在文件中分布的,但最坏的情况是具有最高香农熵的那个:相等的分布。在这种情况下,文件中不出现任何一个整数的概率是

((2³²-1)/2³²)⁴⁰⁰⁰⁰⁰⁰⁰⁰⁰≈.4

香农熵越低,这个概率在平均值上越高,但即使在这种最坏的情况下,我们也有90%的机会在随机整数进行5次猜测后找到一个非发生的数字。只需使用伪随机生成器创建此类数字,将它们存储在列表中。然后在int之后读取int并将其与所有猜测进行比较。如果匹配,请删除此列表条目。经过所有文件后,你可能会有不止一个猜测。使用其中任何一个。在极少数情况下(甚至在最坏情况下为10%)无法猜测的事件中,获得一组新的随机整数,这次可能更多(10-> 99%)。

内存消耗:几十个字节,复杂度:O(n),开销:可以选择,因为大部分时间都花在不可避免的硬盘访问上,而不是比较整数。

<小时/> 实际的最坏情况,当我们假设静态分布时,每个整数都出现最大值。曾经,因为那时候 1 - 4000000000 /2³²≈6% 所有整数都不会出现在文件中。所以你需要一些更多的猜测,但这仍然不会花费大量内存。

答案 9 :(得分:24)

如果您在[0,2 x - 1]范围内缺少一个整数,那么只需将它们全部放在一起。例如:

>>> 0 ^ 1 ^ 3
2
>>> 0 ^ 1 ^ 2 ^ 3 ^ 4 ^ 6 ^ 7
5

(我知道这不会回答完全这个问题,但对于一个非常相似的问题,这是一个很好的答案。)

答案 10 :(得分:17)

根据原始问题中的当前措辞,最简单的解决方案是:

找到文件中的最大值,然后向其中添加1。

答案 11 :(得分:16)

他们可能正在寻找你是否听说过概率Bloom Filter,它可以非常有效地确定一个值是否不是一个大集的一部分,(但只能很有可能确定它是一个成员集合。)

答案 12 :(得分:14)

使用BitSet。 40个整数(假设最多2 ^ 32个整数)以每字节8个打包到BitSet中为2 ^ 32/2 ^ 3 = 2 ^ 29 =约0.5 Gb。

要添加更多细节 - 每次读取数字时,请在BitSet中设置相应的位。然后,对BitSet进行一次传递,找到第一个不存在的数字。事实上,你可以通过反复选择一个随机数并测试它是否存在来做到这一点。

实际上BitSet.nextClearBit(0)会告诉你第一个非设置位。

查看BitSet API,它似乎只支持0..MAX_INT,因此您可能需要2个BitSet - 一个用于+'ve数字,一个用于-'ve数字 - 但内存要求不会改变。< / p>

答案 13 :(得分:12)

如果没有大小限制,最快的方法是获取文件的长度,并生成文件的长度+ 1个随机数字(或“11111 ...”)。优点:您甚至不需要读取文件,并且可以将内存使用率降至最低。缺点:您将打印数十亿位数。

但是,如果唯一的因素是最小化内存使用,而其他任何事情都不重要,那么这将是最佳解决方案。它甚至可能会让你“滥用规则”奖。

答案 14 :(得分:10)

如果我们假设数字的范围总是2 ^ n(偶数幂为2),则排他性或将起作用(如另一张海报所示)。至于原因,我们来证明一下:

理论

给定任何基于0的整数范围,其中2^n个元素缺少一个元素,您可以通过简单地将已知值相加来找到缺少的元素,从而产生缺失的数字。

证据

让我们看一下n = 2.对于n = 2,我们可以表示4个唯一的整数:0,1,2,3。它们的位模式为:

  • 0 - 00
  • 1 - 01
  • 2 - 10
  • 3 - 11

现在,如果我们看一下,每个位都设置了两次。因此,由于它被设置为偶数次,并且数字的异或将产生0.如果缺少单个数字,则排除 - 或将产生一个数字,当与缺少的数字进行排他时将导致因此,缺失的数字和由此产生的排他性数字完全相同。如果我们删除2,则生成的xor将为10(或2)。

现在,让我们看看n + 1。让我们调用nx中设置每个位的次数以及n+1 y中每位设置的次数。 y的值将等于y = x * 2,因为x位设置为0 n+1个元素,x元素n+1由于2x始终是偶数,n+1将始终将每个位设置为偶数次。

因此,由于n=2有效且n+1有效,因此xor方法适用于n>=2的所有值。

基于0的范围的算法

这很简单。它使用2 * n位的内存,因此对于任何范围&lt; = 32,2个32位整数将起作用(忽略文件描述符消耗的任何内存)。它只对文件进行一次传递。

long supplied = 0;
long result = 0;
while (supplied = read_int_from_file()) {
    result = result ^ supplied;
}
return result;

基于任意的范围的算法

该算法适用于任何起始编号到任何结束编号的范围,只要总范围等于2 ^ n ...这基本上重新定义范围,使其最小值为0.但它确实需要2次传递文件(第一次获取最小值,第二次获取计算缺失的int)。

long supplied = 0;
long result = 0;
long offset = INT_MAX;
while (supplied = read_int_from_file()) {
    if (supplied < offset) {
        offset = supplied;
    }
}
reset_file_pointer();
while (supplied = read_int_from_file()) {
    result = result ^ (supplied - offset);
}
return result + offset;

任意范围

我们可以将这种修改后的方法应用于一组任意范围,因为所有范围将至少跨越2 ^ n的幂。仅当存在一个缺失位时才有效。它需要2次传递未分类的文件,但每次都会找到一个缺失的数字:

long supplied = 0;
long result = 0;
long offset = INT_MAX;
long n = 0;
double temp;
while (supplied = read_int_from_file()) {
    if (supplied < offset) {
        offset = supplied;
    }
}
reset_file_pointer();
while (supplied = read_int_from_file()) {
    n++;
    result = result ^ (supplied - offset);
}
// We need to increment n one value so that we take care of the missing 
// int value
n++
while (n == 1 || 0 != (n & (n - 1))) {
    result = result ^ (n++);
}
return result + offset;

基本上,将范围重新设置为0左右。然后,它计算要附加的未排序值的数量,因为它计算异或。然后,它将未分类值的数量加1,以处理缺失值(计算缺失值)。然后,保持x值n值,每次递增1,直到n为2的幂。然后结果重新回到原始基数。完成。

这是我在PHP中测试的算法(使用数组而不是文件,但概念相同):

function find($array) {
    $offset = min($array);
    $n = 0;
    $result = 0;
    foreach ($array as $value) {
        $result = $result ^ ($value - $offset);
        $n++;
    }
    $n++; // This takes care of the missing value
    while ($n == 1 || 0 != ($n & ($n - 1))) {
        $result = $result ^ ($n++);
    }
    return $result + $offset;
}

在一个包含任何值范围的数组中(我测试包括底片),其中一个在该范围内缺失,每次都找到正确的值。

另一种方法

既然我们可以使用外部排序,为什么不检查差距呢?如果我们假设在运行此算法之前对文件进行了排序:

long supplied = 0;
long last = read_int_from_file();
while (supplied = read_int_from_file()) {
    if (supplied != last + 1) {
        return last + 1;
    }
    last = supplied;
}
// The range is contiguous, so what do we do here?  Let's return last + 1:
return last + 1;

答案 15 :(得分:9)

检查输入文件的大小,然后输出任何太大而无法用大小的文件表示。这看起来像是一个便宜的技巧,但它是面试问题的创造性解决方案,它巧妙地回避了记忆问题,技术上是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 bitcount - 1 ,这将始终大于 2 bitcount 。从技术上讲,你必须击败的数字是 2 bitcount - (4 * 10 9 - 1),因为你知道有(40亿) - 1)文件中的其他整数,即使有完美的压缩,它们每个至少占用一位。

答案 16 :(得分:8)

欺骗问题,除非引用不当。只需读取文件一次即可获得最大整数n,然后返回n+1

当然,如果n+1导致整数溢出,您需要备份计划。

答案 17 :(得分:8)

  • 最简单的方法是找到文件中的最小数字,然后返回1。这使用O(1)存储,并且对于n个数字的文件使用O(n)时间。但是,如果数字范围有限,它将失败,这可能使min-1不是一个数字。

  • 已经提到了使用位图的简单直接的方法。该方法使用O(n)时间和存储。

  • 还提到了具有2 ^ 16个计数桶的2遍方法。它读取2 * n个整数,因此使用O(n)时间和O(1)存储,但它不能处理超过2 ^ 16个数字的数据集。然而,通过运行4次传递而不是2次传递,它很容易扩展到(例如)2 ^ 60 64位整数,并且通过使用尽可能多的存储器并且相应地增加传递次数,可以轻松地适应使用微存储器。哪种情况下运行时间不再是O(n),而是O(n * log n)。

  • 目前由rfrankel提及的所有数字的异或,并由ircmaxell详细解答了stackoverflow#35185中提出的问题,正如ltn100指出的那样。它使用O(1)存储和O(n)运行时。如果目前我们假设32位整数,则XOR产生不同数字的概率为7%。理由:给出~4G不同的数字异或,并且约。 300M不在文件中,每个位位置的设置位数有相同的奇数或偶数。因此,2 ^ 32个数字具有与XOR结果相同的可能性,其中93%已经存在于文件中。请注意,如果文件中的数字并非全部不同,则XOR方法的成功概率会上升。

答案 18 :(得分:7)

出于某种原因,我一看到这个问题就想到了对角化。我假设任意大整数。

阅读第一个号码。用零位填充它,直到你有40亿位。如果第一个(高位)位为0,则输出1;否则输出0.(你真的不需要左键盘:如果数字中没有足够的位,你只输出一个。)对第二个数字做同样的事情,除了使用它的第二个数字。以这种方式继续浏览文件。您将一次输出一个40位的数字,该数字与文件中的任何数字都不相同。证明:它与第n个数字相同,然后他们会同意第n位,但它们不是通过构造。

答案 19 :(得分:6)

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

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

假设每个整数是32位,如果完成位标记,它们将方便地放入1 GB的RAM中。

答案 20 :(得分:6)

  

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

来自Carbonetc的Reddit。

答案 21 :(得分:6)

为了完整起见,这是另一个非常简单的解决方案,很可能需要很长时间才能运行,但使用的内存非常少。

让所有可能的整数都是从int_minint_max的范围,并且 bool isNotInFile(integer)一个函数,如果文件不包含某个整数则返回true,否则返回false(通过将该特定整数与文件中的每个整数进行比较)

for (integer i = int_min; i <= int_max; ++i)
{
    if (isNotInFile(i)) {
        return i;
    }
}

答案 22 :(得分:5)

对于10 MB内存约束:

  1. 将数字转换为二进制表示。
  2. 创建一个二叉树,其中left = 0且right = 1.
  3. 使用二进制表示法在树中插入每个数字。
  4. 如果已插入数字,则已经创建了叶子。
  5. 完成后,只需使用之前未创建的路径创建请求的号码。

    40亿个数字= 2 ^ 32,意味着10 MB可能还不够。

    修改

    可以进行优化,如果已创建两个叶子并且具有公共父级,则可以删除它们并将父标记为不是解决方案。这会削减分支并减少对内存的需求。

    编辑II

    也没有必要完全构建树。如果数字相似,您只需要构建深分支。如果我们也削减了分支,那么这个解决方案可能会有效。

答案 23 :(得分:5)

我将回答1 GB版本:

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

整数是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;
    }
}

答案 24 :(得分:4)

只要我们做出创造性的答案,这是另一个答案。

使用外部排序程序以数字方式对输入文件进行排序。这适用于您可能拥有的任何内存量(如果需要,它将使用文件存储)。 读取已排序的文件并输出缺少的第一个数字。

答案 25 :(得分:3)

比特消除

一种方法是消除位,但这实际上可能不会产生结果(很可能不会)。伪码:

long val = 0xFFFFFFFFFFFFFFFF; // (all bits set)
foreach long fileVal in file
{
    val = val & ~fileVal;
    if (val == 0) error;
}

比特计数

跟踪位数;并使用金额最小的位生成一个值。同样,这无法保证生成正确的值。

范围逻辑

跟踪列表有序范围(按开始排序)。范围由结构定义:

struct Range
{
  long Start, End; // Inclusive.
}
Range startRange = new Range { Start = 0x0, End = 0xFFFFFFFFFFFFFFFF };

浏览文件中的每个值,然后尝试将其从当前范围中删除。这种方法没有内存保证,但它应该做得很好。

答案 26 :(得分:3)

我认为这是一个已解决的问题(见上文),但有一个有趣的侧面案例要记住,因为它可能会被问到:

如果确实有4,294,967,295(2 ^ 32 - 1)个32位整数没有重复,因此只有一个缺失,有一个简单的解决方案。

在零处开始运行总计,并且对于文件中的每个整数,添加具有32位溢出的整数(实际上,runningTotal =(runningTotal + nextInteger)%4294967296)。完成后,将4294967296/2添加到运行总计中,再次使用32位溢出。从4294967296中减去它,结果是缺少的整数。

“只有一个缺失的整数”问题只能通过一次运行来解决,并且只有64位专用于数据的RAM(32用于运行总计,32用于读取下一个整数)。

推论:如果我们不关心整数结果必须具有多少位,则更通用的规范非常容易匹配。我们只生成一个足够大的整数,它不能包含在我们给出的文件中。同样,这占用了绝对最小的RAM。请参阅伪代码。

# Grab the file size
fseek(fp, 0L, SEEK_END);
sz = ftell(fp);
# Print a '2' for every bit of the file.
for (c=0; c<sz; c++) {
  for (b=0; b<4; b++) {
    print "2";
  }
}

答案 27 :(得分:3)

2 128 * 10 18 + 1(即(2 8 16 * 10 18 + 1) - 今天不能成为一个普遍的答案吗?这表示16 EB文件中无法保存的数字,这是当前任何文件系统中的最大文件大小。

答案 28 :(得分:3)

正如Ryan基本上说的那样,对文件进行排序,然后检查整数,当跳过一个值时,你就可以了:)

downvoters的

编辑:OP提到文件可以排序,因此这是一种有效的方法。

答案 29 :(得分:2)

如果您不假设32位约束,则只返回一个随机生成的64位数字(如果您是悲观主义者,则返回128位)。碰撞的机会是1 in 2^64/(4*10^9) = 4611686018.4(大约十亿分之一十亿)。你大部分时间都是对的!

(开玩笑......好吧。)

答案 30 :(得分:1)

  
    

给定一个包含40亿个整数的输入文件,提供一种算法     生成一个未包含在文件中的整数。假设你     有1 GiB内存。如果你有的话,跟进你会做什么     10 MiB的记忆。

  
     

文件大小为4 * 109 * 4字节= 16 GiB

如果是32位无符号整数

0 <= Number < 2^32
0 <= Number < 4,294,967,296

我提出的解决方案:没有错误检查的C ++

#include <vector>
#include <fstream>
#include <iostream>
using namespace std;

int main ()
{
    const long SIZE = 1L << 32;

    std::vector<bool> checker(SIZE, false);

    std::ifstream infile("file.txt");  // TODO: error checking

    unsigned int num = 0;

    while (infile >> num)
    {
        checker[num] = true ;
    }

    infile.close();

    // print missing numbers

    for (long i = 0; i < SIZE; i++)
    {
        if (!checker[i])
            cout << i << endl ;
    }

    return 0;
}

复杂性

Space ~ 2^32 bits = 2^29 Bytes = 2^19 KB = 2^9 MB = 1/2 GB

Time ~ Single Pass

Completeness ~ Yes

答案 31 :(得分:1)

通过在某些树形结构中存储未访问整数的范围,可以在读取现有的整数之后加快查找缺失的整数。

您首先要存储[0..4294967295],每次读取整数时,都会拼接它所在的范围,当它变为空时删除范围。最后,您将获得范围中缺少的确切整数集。因此,如果您将5视为第一个整数,那么您将[0..4]和[6..4294967295]。

这比标记位慢很多,所以它只是10MB大小写的解决方案,前提是你可以将较低级别的树存储在文件中。

存储这样一棵树的一种方法是B树,其中范围的起点为关键,范围的结尾为值。最糟糕的情况是当你得到所有奇数或偶数整数时,这意味着为树存储2 ^ 31个值或数十GB ......哎哟。最好的情况是一个排序文件,你只需要为整棵树使用几个整数。

所以不是真正的答案,但我想我会提到这种做法。我想我没有通过面试; - )

答案 32 :(得分:1)

您不需要对它们进行排序,只需重复分区它们的子集。

第一步就像快速排序的第一步。选择其中一个整数x,并使用它来遍历数组,将所有小于x的值放在其左侧,将值大于x的值放在右侧。找到x的哪一侧有可用的最大插槽数(整数不在列表中)。通过比较x的值与其位置,可以很容易地计算出来。然后在x的那一侧重复子列表上的分区。然后用最大数量的可用整数等重复子子列表上的分区。要达到空范围的比较总数应该是大约40亿,给予或接受。

答案 33 :(得分:1)

也许我完全忽略了这个问题的重点,但是你想找到整数排序文件中缺少的整数?

呃......真的吗?让我们考虑一下这样一个文件的样子:

1 2 3 4 5 6 ...首先缺少号码......等等。

这个问题的解决方案似乎微不足道。

答案 34 :(得分:0)

我提出了以下算法。

我的想法:遍历整个整数文件,每个位位置计数0和1。 0s和1s的数量必须是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&lt;&lt; 31是负数,因此,(长)1 <&lt; 31是正确的方法

答案 35 :(得分:0)

我可能会仔细阅读,但问题是“生成一个未包含在文件中的整数”。我只是对列表进行排序,并在最大条目中添加1。 Bam,一个未包含在文件中的整数。

答案 36 :(得分:0)

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

我认为这样的问题可能会给出一个合理的答案。

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

缺点:

  • 文件大小
  • 慢速排序 - 但只需要一次

的优点:

  • 非常快速地查找

再一次,一本书的一个非常好的问题。但是我认为在要求一个最佳解决方案时,当要解决的问题尚不完全清楚时,这是一个奇怪的问题。

答案 37 :(得分:-1)

当然,并且说经验有限(刚刚开始在Uni学习java)你可以运行trhough一组(桶)的int,如果没有找到数量处理桶。这将释放空间并对每个数据单元进行检查。如果找到了您要查找的内容,请将其添加到计数变量中。需要很长时间但是,如果您为每个部分创建了多个变量并运行每个变量的检查计数并确保它们同时退出/处置,则变量存储不应该增加?并将加快检查过程。只是一个想法。