【问题标题】:Making MergeSort More Efficient让 MergeSort 更高效
【发布时间】:2018-02-08 04:54:57
【问题描述】:

我正在研究一个hackerrank问题:https://www.hackerrank.com/challenges/big-sorting

并用 Python 编写了 MergeSort 的实现。该算法运行良好,但在一些较大的输入测试中出现超时错误。由于我不是 Python 专家,谁能建议我如何让我的代码更高效?

unsorted = map(int, unsorted) # Unsorted is provided as an input, an array of strings


def mergeSort(list):
    s = len(list)

    if s == 1:
        return list

    if s == 2:
        if list[0] < list[1]:
            return list
        return [list[1], list[0]]

    listA = mergeSort(list[:s / 2])
    listB = mergeSort(list[s / 2:])

    r = []

    while len(listA) > 0 or len(listB) > 0:
        if len(listA) == 0:
            r = r + listB
            return r

        if len(listB) == 0:
            r = r + listA
            return r

        if listA[0] < listB[0]:
            r.append(listA.pop(0))
        else:
            r.append(listB.pop(0))


list = mergeSort(unsorted)
for n in list:
    print n

【问题讨论】:

  • 如果合并排序完全是错误的算法,我不会感到惊讶。我没有看过挑战,但可能是关于 O(1) 排序,如 radixsort 或 bucketsort。你试过内置的sorted吗?如果这也超时,您将无法用纯 Python mergesort 击败它。
  • @MSeifert - 挑战是对长度为 10^6 个字符的字符串进行排序,因此基数排序不是最佳选择。

标签: python sorting mergesort


【解决方案1】:

针对 1 到 10000 之间的 100000 个随机数列表运行您的脚本会给我这个分析:

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    3.687    3.687 <string>:1(<module>)
 131071/1    1.457    0.000    3.687    3.687 \Test\untitled4.py:8(mergeSort)
  1502009    1.903    0.000    1.903    0.000 {method 'pop' of 'list' objects}
  4833703    0.217    0.000    0.217    0.000 {len}
  1502009    0.110    0.000    0.110    0.000 {method 'append' of 'list' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

从中可以看出,大部分时间都花在了pop()len() 以及函数调用上。例如,pop(0) 可以通过使用较低的指针来消除。 关于python中mergesort算法的类似优化有很多问题,所以请尝试应用类似问题下答案中描述的优化。

【讨论】:

  • 不出所料,pop(0) 是罪魁祸首!
【解决方案2】:
  1. 您通过切片不断复制子列表的事实(例如 list[:s / 2]) 比你使用的内存要多得多 实现了合并排序以运行“in-place”。
  2. 可能是归并排序太慢了,有一些算法会运行得更快,例如counting sortradix sort

【讨论】:

    【解决方案3】:

    其他人也做过类似的挑战:

    Recursive algorithm works without return statement? (Runs really fast)

    对于这个挑战,字符串没有前导零并且将被视为整数。较长的字符串大于较短的字符串。首先需要进行长度比较,并且只有当长度相等时才应该比较字符串。

    这可以通过一次性分配辅助数组 aux = [none]*n 来进一步改进,其中 n 是行数(main() 需要计算行数并将 n 作为参数传递到排序功能)。如果使用自顶向下合并排序,可以使用一对相互递归的函数来避免数据复制(在这种情况下,我假设“数据”实际上相当于一个指向字符串的指针数组,并且字符串是从未被排序所感动)。一个函数以原始数组中的排序子数组结束,另一个函数以辅助数组中的排序子数组结束。每个函数调用另一个函数两次,左半边一次,右半边一次,然后合并两半。在排序后的数据最终位于辅助数组且大小为 1 的特殊情况下,将单个元素从原始数组复制到辅助数组。

    自下而上的归并排序会更快一些,因为它会跳过所有用于生成 n-1 对索引的递归,并首先将包含 n 个元素的数组视为 n 个子数组,每个子数组包含 1 个元素,然后使用迭代来操作索引。它并没有快很多,因为大部分时间都花在了合并功能上,而且自上而下和自下而上的合并排序的合并功能是相同的。与优化的自顶向下合并排序类似,通过根据合并过程改变合并方向来避免复制。合并通道的数量是根据n提前确定的,如果是奇数通道,第一通道可以交换成对的元素,留下偶数的合并通道来做排序数据最终在原始数组中。

    看来这个挑战是打算用 C 来实现的,因为它提供了一个 C sn-p。 python 实现会慢很多。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-01
      • 2011-06-14
      • 2012-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-11
      相关资源
      最近更新 更多