【问题标题】:using 10 MB of memory for four billion integers (about finding the optimized block size) [duplicate]为 40 亿个整数使用 10 MB 内存(关于找到优化的块大小)[重复]
【发布时间】:2016-01-08 02:20:48
【问题描述】:

问题是,给定一个包含 40 亿个整数的输入文件,提供一个算法来生成一个不包含在文件中的整数,假设只有 10 MB 内存。

搜索了一些解决方案,其中一个是将整数存储到位向量块中(每个块代表40亿范围中的特定整数范围,块中的每个位代表一个整数),并使用另一个计数器每个块,计算每个块中的整数个数。因此,如果整数的数量小于整数的块容量,则扫描块的位向量以查找丢失的整数。

我对这个解决方案的困惑是,当块计数器数组占用与位向量相同的内存时,提到了最佳的最小占用空间。我很困惑为什么在这种情况下它是最佳的最小足迹?

这里是我提到的计算细节,

Let N = 2^32.
counters (bytes): blocks * 4
bit vector (bytes): (N / blocks) / 8
blocks * 4 = (N / blocks) / 8
blocks^2 = N / 32
blocks = sqrt(N/2)/4

提前致谢, 林

【问题讨论】:

标签: algorithm bit-manipulation bit


【解决方案1】:

为什么它是最小的内存占用:

在您提出的解决方案中,有两个阶段:

  1. 计算每个块中的整数个数

    这使用4*(#blocks) 字节的内存。

  2. 使用位向量,每个位表示块中的一个整数。

    这使用(blocksize/8)字节的内存,即(N/blocks)/8

如您所述,将 2 设置为相等会导致 blocks = sqrt(N/32)

这是最优的,因为所需的内存是每个阶段所需内存的最大值(必须同时执行)。在第 1 阶段之后,您可以忘记计数器,除了在哪个块中搜索第 2 阶段。

优化

如果您的计数器在达到容量时饱和,则每个计数器实际上不需要 4 个字节,而是 3 个字节。当计数器超过块中的整数数时,计数器达到容量。

在这种情况下,阶段 1 使用内存的3*blocks,阶段 2 使用 (N/blocks)/8。因此,最优值为blocks = sqrt(N/24)。如果N 为 40 亿,则块数约为 12910,块大小为每块 309838 个整数。这适合 3 个字节。

注意事项和具有良好平均案例性能的替代方案

您提出的算法仅在所有输入整数都是不同的情况下才有效。如果整数不是不同的,我建议您简单地使用随机的整数候选集方法。在随机候选整数集方法中,您可以随机选择 1000 个候选整数,并检查是否在输入文件中找不到任何候选整数。如果你失败了,你可以尝试找到另一组随机的候选整数。虽然这在最坏情况下的性能很差,但在大多数输入的平均情况下它会更快。例如,如果输入整数覆盖了 99% 的可能整数,那么平均而言,在 1000 个候选整数中,将找不到其中的 10 个。您可以伪随机选择候选整数,这样您就不会重复候选整数,并且还可以保证在固定次数的尝试中,您将测试所有可能的整数。

如果每次都检查sqrt(N) 候选整数,那么最坏情况下的性能可以与N*sqrt(N) 一样好,因为您可能必须扫描所有N 个整数sqrt(N) 次。

如果您使用此替代方案,您可以避免最坏的情况时间,如果它不适用于第一组候选整数,则切换到您建议的解决方案。这可能会提供更好的平均情况性能(这是一种常见的排序策略,首先使用快速排序,然后再切换到堆排序,例如,如果出现最坏情况输入时)。

【讨论】:

  • 感谢 ronalchn,在我原来的解决方案中,当使用 blocks = sqrt(N/32) 时,这意味着一个数字块的大小 + 整个计数器的大小可以放入内存?我有点迷失“将 2 设置为相等的结果”的逻辑含义。
  • 嗨 ronalchn,如果你能对我的问题发表评论,那就太好了。 :)
  • 这不是真的,一个数字块或整个计数器在任何时候都适合内存。但是,您只需要一次存储其中任何一个。在第一阶段之后,您知道要查看哪个块,因此您可以忘记其余的计数器,并将内存重新用于您的数字块。
  • 感谢 ronalchn,在第一阶段之后,为什么一个整数块保存整个内存是最佳的?最适合什么?我也可以在第二阶段加载两个或更多更小的块(具有不同的块大小)?
  • 您将只有足够的空间来跟踪第 2 阶段的 1 个区块。第 1 阶段会告诉您在哪些区块中可以找到答案。由于您只需要 1 个唯一整数,因此您只需检查未填充的块之一。你没有空间来存储多个块,你也不需要。
【解决方案2】:
# assumes all integers are positive and fit into an int
# very slow, but definitely uses less than 10MB RAM
int generate_unique_integer(file input-file)
{
  int largest=0
  while (not eof(input-file))
  {
    i=read(integer)
    if (i>largest) largest=i
  }
  return i++; #larger than the largest integer in the input file
}

【讨论】:

  • 什么时候最大的是 INT_MAX?
  • 感谢 user5429970,但您的回答与我的问题有什么关系?我的问题与获取最大值无关。 :)
  • @LinMa 通过在文件中找到最大整数m,并返回下一个更大的整数m+1,这确实提供了问题的答案,因为m+1 绝对不是 文件中的整数之一。此外,除了代码中使用的少数标​​量外,这种方法不需要额外的存储空间。正如其他人指出的那样,如果m==INT_MAX,这种方法会失败,因为在这种情况下m+1 是不可表示的,而且我看不到解决该问题的简单方法。
  • 同样感谢@njuffa。如果您对我原来的问题有任何想法,感谢分享。 :)
  • 林马:我不明白你为什么觉得这个回复与你的问题无关。这绝对是一种识别唯一整数的方法。
猜你喜欢
  • 2015-03-12
  • 2014-05-07
  • 2014-05-11
  • 2011-07-15
  • 1970-01-01
  • 1970-01-01
  • 2011-11-01
  • 2020-07-01
相关资源
最近更新 更多