【问题标题】:Sorting 1 trillion integers对 1 万亿个整数进行排序
【发布时间】:2011-09-03 14:38:27
【问题描述】:

给定硬盘上的 1 万亿个整数,找出其中最小的 100 万个。您一次最多可以在内存中容纳 100 万个整数。

一种方法是,从 1 万亿个整数中取出前 100 万个,然后对这 100 万个整数进行排序,然后将其存储回硬盘中。这样对每组100万个整数进行排序并存储在硬盘中。现在 100 万个整数的组被排序到 1 万亿个。现在比较所有排序组的第一个元素,它们中的最小值是 1 万亿中的最小值。将其存储为内存中的第一个元素。接下来,从最小元素所在的组中取出第二个元素,然后将其与所有其他组的第一个元素一起检查。以这种方式重复该过程,直到前100万个被排序并存储在内存中。

我还缺少更优化的方法吗?

【问题讨论】:

  • 对我来说听起来像是家庭作业......
  • 欧洲还是美国万亿?不过,无论哪种方式,都需要使用数百万个整数的大部分内存来一次对文件的三个部分进行内存映射,其余部分作为堆栈空间来运行 quickselect。
  • @Steve 我不知道有什么不同,仍然不知道是什么
  • @Chuck:美国万亿是 10^12。欧洲是 10^18,所以在第一步之后你会有 10^12 个排序的块。这是一个有点傻的问题:在大多数情况下,人们将美式英语用于任何与计算机相关的事情,所以通常会是前者。
  • 如果您对 3 组 3 (413)(928)(657)=>(134)(289)(567) 进行排序,然后在索引 (0..2) 处选择最小的,您得到 (134) 会错过 2。还是我理解错了。

标签: algorithm sorting


【解决方案1】:

Scala 中的解决方案,但不适用于 1 万亿个元素。使用指向文件而不是列表的指针,或多个小列表,可以通过以下方式完成:

def top (n: Int, li: List [Int]) : List[Int] = {

  def updateSofar (sofar: List [Int], el: Int) : List [Int] = {
    // println (el + " - " + sofar)
    if (el < sofar.head) 
      (el :: sofar.tail).sortWith (_ > _) 
    else sofar
  }

  /* better readable:
    val sofar = li.take (n).sortWith (_ > _)
    val rest = li.drop (n)
    (sofar /: rest) (updateSofar (_, _)) */    
  (li.take (n). sortWith (_ > _) /: li.drop (n)) (updateSofar (_, _)) 
}

取前一百万个元素。对它们进行排序。现在对于每个后续元素,将其与百万中最大的元素进行比较。如果它更小,请将其排序到列表中并删除旧的最大的。

【讨论】:

  • 这是我第一次看到拥有 28K 代表的用户投了反对票。
【解决方案2】:

您可以使用heap 在 O(n log m) 内高效地完成此操作。 (n = 所有数字,m = 您要查找的数字集的大小)。

一次浏览一万亿个数字。 对于每个新号码,请执行以下操作之一。

  1. 如果堆有
  2. 如果堆恰好有 100 万个节点,并且顶部节点大于新数,则从堆中弹出顶部节点,并插入具有新编号的节点。
  3. 如果 1 或 2 都不为真,则扔掉这个数字。

在您遍历所有万亿条目之后,生成的堆将具有 100 万个最小的数字。

从堆中插入和删除是 O(log m)。 单次通过堆是 n。 所以,算法是 n*log (m)

【讨论】:

  • 我不会称之为 O(n log n),因为 log 外面的 n 和 log 里面的 n 是不同的数字。
  • 你不必在每次插入后对堆进行排序吗?
  • @F.C.不。每次插入后,堆的顶部总是最大的元素。这是堆的基本属性。无论如何,您只是在检查顶部元素。
  • @sverre:我明白你的意思。将答案编辑为 O(n log m)。
  • @Scott Urban 你在这个答案上提供的编辑改变了意思(即使它只是为了改进它)。这不是编辑的目的。等到你有编写 cmets 的声望点,然后使用它。
【解决方案3】:

您可以使用 QuickSort 的变体在 O(n) 时间内更有效地完成此操作,其中“n”是磁盘上列表的大小。 (在本例中为万亿)

你所要做的就是:

  1. 通过在越来越小的部分中多次对磁盘驱动器进行分区来找到百万分之一的最小数字。这需要 O(n) 时间。

  2. 把它和分区整理出来的其他 999,999 个整数放在 RAM 中。你完成了。

最小的百万整数不会被排序,但它们会是最小的百万。

如果您想对最小的百万进行排序,则需要 O(m log m) 时间,在这种情况下,“m”是一百万。

没有空间成本,O(n) 时间,适用于非整数值。享受。 :)

【讨论】:

  • 最坏情况下效率为 O(n^2)
【解决方案4】:

整数有多大?如果它们只是 32 位值,我会简单地在磁盘上创建一个包含 40 亿个 64 位计数器的数组,并在输入中遇到 x 时,在位置 x 处增加计数器。一般来说,这种方法在空间上的成本非常高,但是当可能的元素值的范围远小于要排序的项目数时,成本相对较低,而且最好的是O(n) 在时间上。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-30
    • 2020-09-02
    • 2011-11-05
    • 1970-01-01
    • 2015-05-18
    • 2021-07-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多