【问题标题】:Sorting 2^30 number of 32 bit integers. Best solutions对 2^30 个 32 位整数进行排序。最佳解决方案
【发布时间】:2012-04-27 18:14:42
【问题描述】:

我有一个包含 2^30 个无符号 32 位整数值的文件,我需要对它们进行排序,所以我想用最快的算法来完成它。需要使用所有可用的处理器,并且使用的内存不超过 256MB。

我现在的想法: 最大 int 值(对于 32 位整数)Sm= 2^32,最低 = 0。可用内存为 M=2^28。

  • 划分输出文件

    Sm*(sizeof int)/M = 2^32*2^5/2^28 = 2^9 份; 每个零件尺寸 2^32/2^9 = 2^23。

首先,编写一个简单的阅读器,从输入文件中读取 int 值,检查它位于什么范围内,并将该范围内的整数放入 tempfile。之后我将有 2^9 个文件:

1 file= Integers from 0:2^23
2 file = 2^23:2^24
3 file = 2^24:(2^24+2^23),
and etc...
  • 使用 qsort 或金字塔排序等标准算法进行排序(对此算法有什么建议吗?)

我可以在这里使用 Python.multiprocessing 之类的东西来进行并行排序,但它需要在每个进程开始之前安全地计算可用内存

您如何看待这种方法?可能存在更干净、更简单的解决方案吗?

【问题讨论】:

  • 我可以建议您最好的做法是尝试一下,看看它是如何工作的?
  • “需要使用所有可用的处理器”。你怎么会那么说?当然,您只想要最快的解决方案。为什么要关心使用了多少个处理器?
  • @AndrewAylett 我可以建议,反复试验并不是解决算法问题的最有效方法
  • @David 是的,使用所有流程并做出更好的解决方案可能是个坏主意
  • 相关优秀书籍:amazon.com/Programming-Pearls-2nd-Edition-Bentley/dp/0201657880BTW 整数是唯一的吗?

标签: python algorithm sorting computer-science


【解决方案1】:

1)。将整数分成几部分

a. [0, 2^20 - 1], [2^20, 2^21 - 1]....

2)。对于每个部分,您可以计算每个整数的计数(类似于基数排序),每个部分的时间复杂度是部分的长度。而空间复杂度也是零件的长度。

// for each part
int start = 0;      // the starting point of the part
int end = 2^20 - 1; // the ending point of the part
int *hash = new int[end - start + 1];
for (int i = start; i <= end; ++i) {
    // read a integer val
    ++hash[val];
}
for (int i = start; i <= end; ++i) {
    if (hash[i] > 0) {
        for (int j = 0; j < hash[i]; ++j) {
            // print i
        }
     }
}

3)。因为你有 256MB = 256 * 2^20 = 64 * 2^20(int),所以你可以并行处理 64 个部分。如果需要,您可以将 2^20 设置为其他值。

4)。无论如何,这个算法的总时间复杂度应该是O(n) + O(2 ^ 32),n表示整数的个数。当 n 非常大,接近 2^32 时,该算法效果很好。此外,该算法可以并行处理。

5)。该算法不需要合并过程,因为部分已排序。

6)。上面提到的堆解决方案似乎不是并行处理的。

【讨论】:

  • 可以吗?即每个下一个部分比上一个大 2 倍。 len(0:2^20) = len(2^20:2^21) = 2*len(2^21:2^22) = 等等...
  • 嗨,Rustem,每个部分的长度相同,但部分之间没有交集。我觉得你可以试试这个方法,代码应该很简单,没有复杂的排序算法。
【解决方案2】:

基数排序常号称是O(n),但实际上是O(nlogn),因为它需要的时间与最大数的位数*数的个数成正比,而且位数是走的倾向于 log(n)。

我建议使用 3 级复合排序:

  1. insertionsort 用于长度约为 32 或 64 的小型子列表 - 基准测试以找到最好的 - timsort 会为您介绍这一点。
  2. 大型子列表的timsort 或merge sort 达到您的最大物理内存量。 timsort 被 Python 和 Java 用于它们的排序方法,并且速度非常快,但它是一种比 mergesort 更复杂的算法,如果你使用 C 之类的东西很重要 - 所以如果你只需要一些工作得很好并且是简单,使用归并排序。
  3. 在 large 中,使用 mergesort 来合并包含已排序大子列表的已排序文件。

Python 的多处理模块允许您将标量类型(如整数)的数组存储在共享内存中。 '只是要记住的事情。

绝对让每个核心对一个大的子列表进行排序 - 这对于具有多个核心的系统会有很大帮助。有时对 #3 使用 minheap 会很好,有时你最好只使用一个数组(对于较少数量的大型子列表)。

【讨论】:

    【解决方案3】:

    “无符号32位整数值”是这里的重点。您可以使用radix sort 对其进行排序。维基页面提供完整的example in Python

    由于您没有足够的内存一次将所有内容排序,您必须将工作划分为适合内存的部分,对每个部分进行排序,将结果保存到磁盘,然后以类似于 merge pass 的方式合并结果merge sort。合并不需要将整个内容加载到内存中,您所要做的就是从部分读取,同时写入最终结果。

    【讨论】:

    • 它需要将所有列表成员加载到内存中 - 256 MB 是不可能的
    • @Rustem:不,合并不需要将整个内容加载到内存中,因为部分内容已经排序。
    【解决方案4】:

    考虑使用 MergeSort。可以在此处找到简短说明:http://en.wikipedia.org/wiki/Merge_sort

    合并排序非常适合并行实现和内存限制。

    【讨论】:

      【解决方案5】:
      1. 一次读取适合您内存的任何内容(我们称之为块),对其进行排序并将其写回磁盘(即对 256 MB 的块进行排序)
      2. 同时打开所有块,从每个块中读取前 n 个条目并构建一个 heap(其中 n 是您填充 256 MBytes 的值)
      3. 从堆中获取最小元素(注意它来自哪个块),将其写入目标文件
      4. 从同一输入块中读取下一个元素并将其添加到堆中并重复上一步,直到处理完所有数据

      256 MBytes 是 2^28 字节或 2^26(四字节)整数,因此您只需要对 2^4 = 16 个块进行排序。

      【讨论】:

      • 正如有人指出的那样:这将文件系统用作一种虚拟内存,根据问题的性质,这可能会违反 256mb 的限制。 (如果是现实世界的问题,可能没问题,但如果是家庭作业问题,则可能不允许。)
      • 我上面描述的可以重用存储数字的原始文件(当然,除非出于某种原因也被禁止)
      猜你喜欢
      • 2017-03-28
      • 2011-11-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-26
      • 1970-01-01
      • 2020-04-12
      相关资源
      最近更新 更多