【问题标题】:How does Timsort perform on data in descending order?Timsort 如何按降序处理数据?
【发布时间】:2013-12-17 04:21:48
【问题描述】:

来自:

http://svn.python.org/projects/python/trunk/Objects/listsort.txt

和:

http://en.wikipedia.org/wiki/Timsort

我明白了,Timsort 在a0 > a1 > a2 > ... 时有一些优化,但是下一个数组呢:

10000,10000,9999,9999,9998,9998,....,9,9,8,8,7,7,6,6,5,5,4,4,3,3,2,2,1,1,0,0

这种阵列的时间效率是多少?

(整数用于简化示例,需要稳定排序) 我做了一些测量,似乎这样的数组对于 Timsort 来说不是“好”的情况。

其实就是 JDK 中的 TimSort http://cr.openjdk.java.net/~martin/webrevs/openjdk7/timsort/raw_files/new/src/share/classes/java/util/TimSort.java 有一个方法“countRunAndMakeAscending”

@SuppressWarnings("unchecked")
private static int countRunAndMakeAscending(Object[] a, int lo, int hi) {
    assert lo < hi;
    int runHi = lo + 1;
    if (runHi == hi)
        return 1;

    // Find end of run, and reverse range if descending
    if (((Comparable) a[runHi++]).compareTo(a[lo]) < 0) { // Descending
        while(runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) < 0)
            runHi++;
        reverseRange(a, lo, runHi);
    } else {                              // Ascending
        while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) >= 0)
            runHi++;
    }

    return runHi - lo;
}

为什么不以另一种方式实现它:

private static int countRunAndMakeAscending(Object[] a, int lo, int hi) {
    int runHi = lo;
    int lastEqual = lo;
    int ascending = 0;
    while (++runHi < hi) {
      int c = ((Comparable) a[runHi+1]).compareTo(a[runHi]);
      if (ascending == 0) {
        if (c != 0) {
          if (c > 0) {
            ascending = 1;
          } else {
            ascending = -1;
            reverseRange(a, lastEqual, runHi);
            lastEqual = runHi;
          }
        }
      } else if (ascending == 1) {
        if (c < 0) {
          return runHi - lo;
        }
      } else {
        if (c > 0) {
          reverseRange(a, lastEqual, runHi);
          reverseRange(a, lo, runHi);
          return runHi - lo;
        } else if (c < 0) {
          reverseRange(a, lastEqual, runHi);
          lastEqual = runHi;
        }
      }
    }
    if (ascending == -1) {
      reverseRange(a, lastEqual, runHi);
      reverseRange(a, lo, runHi);
    }
    return runHi - lo;
}

所以它可以在非升序下正常工作?

【问题讨论】:

    标签: sorting mergesort timsort


    【解决方案1】:

    是的。

    基本上,它决定“升序”实际上意味着“不降序”,而不失一般性 - 如果您有例如 [5,5,4 3] 它只会将其分解为 [5,5](升序),然后在下一次调用时为 [4,3](降序)。

    至于为什么,我想这是为了简单起见:只需尝试在您的代码和原始代码中计算 reverseRange() 的调用次数,您就会明白(我通过注意到我花了多长时间才知道)与另一个版本相比,了解一个版本:)

    编辑:错错错了!正如 Oscar Smith 指出的那样,原因是使 timsort 成为一种稳定的排序算法。 如果有人知道如何转移不应有的赏金......

    【讨论】:

    • 我很确定你给出的理由实际上是错误的。实际原因是为了确保排序是稳定的(相等的元素按照给定的顺序返回。如果你想按第一个元素对元组进行排序,这很有用。如果你有[(1,2),(2,0),(1,3)] ,如果排序结果是[(1,3),(1,2),(2,0)]那就不好了
    • 当我有大量空闲时间(哈哈)时,我计划做的一件事是看看通过实施一个不稳定的 timsort 可以获得多大的性能提升。在某些情况下,我预计会有几个 %(在一些非常糟糕的情况下,O(n) 与 O(nlog(n))
    猜你喜欢
    • 2017-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-23
    • 2019-04-19
    • 1970-01-01
    • 1970-01-01
    • 2017-12-21
    相关资源
    最近更新 更多