Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何以尽可能快的时间对接近排序的数组进行排序?(爪哇)_Java_Performance_Algorithm_Sorting_Smoothsort - Fatal编程技术网

Java 如何以尽可能快的时间对接近排序的数组进行排序?(爪哇)

Java 如何以尽可能快的时间对接近排序的数组进行排序?(爪哇),java,performance,algorithm,sorting,smoothsort,Java,Performance,Algorithm,Sorting,Smoothsort,我有一个值数组,几乎是,但不完全排序,有几个值被替换(比如,100000中有50个)。如何最有效地对其进行排序?(性能在这里绝对至关重要,应该比O(N)快很多) 我知道smoothsort,但找不到Java实现。有人知道它是否已经实施了吗?或者我可以用什么来代替smoothsort?正如Botz3000所指出的,你不能比O(N)更快地执行这样的操作。任何算法最基本的元素都是在数组中查找那些无序的条目。这需要O(N),甚至在你弄清楚如何处理它们之前 如果“无序”元素的数量确实比元素总数少几个数量级

我有一个值数组,几乎是,但不完全排序,有几个值被替换(比如,100000中有50个)。如何最有效地对其进行排序?(性能在这里绝对至关重要,应该比O(N)快很多)


我知道smoothsort,但找不到Java实现。有人知道它是否已经实施了吗?或者我可以用什么来代替smoothsort?正如Botz3000所指出的,你不能比O(N)更快地执行这样的操作。任何算法最基本的元素都是在数组中查找那些无序的条目。这需要O(N),甚至在你弄清楚如何处理它们之前

如果“无序”元素的数量确实比元素总数少几个数量级,则可以使用以下算法(假设为链表):

  • 查找所有无序项,并将其从原始列表中提取到单独的列表,O(N)
  • 结果是两个列表:一个排序列表和一个简短的提取列表
  • 对于每个提取的元素,将它们插入到排序列表中。这将是O(log(N)),总数是O(Xlog(N)),其中X是提取元素的数量。如果X相对于N非常小,那么最终的总数是O(N)
    实际上,Wikipedia包含一个smoothsort的Java实现。你可以在这里找到它:

    .

    鸡尾酒类
    如果你想要一个简单且易于实现的算法,你可以做一个简单的实验。它在几乎分类的输入上运行得相当好。

    简单地说,实现良好的冒泡排序肯定是这里最简单的算法。最坏情况为O(n*m),m为位移数。m部分很大程度上取决于位移的模式,通常总的复杂度为O(n)。

    关于不可能达到O(n)的说法你是对的,但是假设有一台多核机器(我有),我们可以通过使用并行排序算法来作弊。

    实现我们在学校所说的Shell排序。这就是冒泡排序子数组。阶跃为k的子数组是一个具有0、k、2k、3k标记的元素数组


    如果您选择k=3i+1,并执行多个冒泡排序,从较高的i-s到0,则几乎排序的数组上的时间将更小。

    [p>[Sun]JDK7已经(或将有)一个(来自Python的)实现。这是一种合并排序,它利用了数组中已经存在的顺序。

    Smoothsort或Timsort是非常好的算法,使用起来非常明智


    我想补充一点,你可能没有意识到的是,谦逊的人是有适应能力的。事实上,对于几乎排序的列表,正如您所看到的,我的理解(我不能用引用来备份)是它比更复杂的算法更快。问题是,如果输入没有几乎分类,它会迅速退化为O(n^2)。尽管如此,正确地实现它还是非常简单的,因此,如果您确信您的输入总是几乎被排序的,那么这将是一个不错的选择。

    有很多很好的算法

    Smoothsort是我个人的最爱。。。如果你想知道为什么它这么好的话,我真的把所有的数学都算出来了

    对于已经排序的数据,一个相当好的算法是natural mergesort,它是mergesort的自底向上版本,其工作原理是将输入视为一系列已排序的子范围,然后在范围上进行多次传递,合并相邻的已排序范围。如果数据已经排序(因为它可以检测到只有一个排序范围),那么它将在O(n)时间内运行,在最坏的情况下是O(n lg n)。如果数据是“块排序”的,则该算法非常有效;也就是说,它由许多相邻放置的排序块组成


    直接插入排序确实适用于大部分已排序的数据,但在许多输入上可能会严重退化。一些非常好的排序(如introsort)实际上使用插入排序的这个属性来对输入执行“清理步骤”。

    这是Smoothsort的原始Java实现,以前可以通过

    //通过保持这些常量,我们可以避免烦人的工作
    //跟踪Dijkstra的b和c。而不是
    //b和c,我将在这个数组中保留一个索引。
    静态final int LP[]={1,1,3,5,9,15,25,41,67,109,
    177, 287, 465, 753, 1219, 1973, 3193, 5167, 8361, 13529, 21891,
    35421, 57313, 92735, 150049, 242785, 392835, 635621, 1028457,
    1664079, 2692537, 4356617, 7049155, 11405773, 18454929, 29860703,
    48315633, 78176337, 126491971, 204668309, 331160281, 535828591,
    866988873//下一个数字大于31位。
    };
    
    publicstatic您不能比O(N)更快地对其排序,因为这是您需要确定数组是否已排序的时间。可能还有关于数组的其他信息。假设被替换的成员可能都在末尾,那么您可以对它们进行排序(O(m log m)),然后就好像它们被插入一样(O(m log n)到O(m log n)来查找插入位置)。这可能需要不同的硬件,但网络排序可以按O((log n)^2)进行排序。参考链接实际上并不难证明。当x/n->0时,可以通过计算xlog(n)的极限来证明这一点。如何在步骤3中推导O(logn)?当使用链表时,二进制搜索可能很难…@Dirk:即使你可以进行二进制搜索,你也可以在O(XN)时间内插入所有元素,对于小x,这会产生O(N)。@Rax:是的,你可以找到它们,但可能所谓的“无序”元素的数量也恰好是O(N)。即使当您查看阵列时,阵列的数量较少。考虑[1,5,10,2,3,4,5,6,7,8,9,10,11]。暴力算法将按顺序调用元素2到10。这在后面的步骤中没有任何意义。正如在这个问题中澄清的那样:,第一步需要O(N logn)时间,掩盖了算法的其他部分,因此它不会在O中运行(
    // by keeping these constants, we can avoid the tiresome business
    // of keeping track of Dijkstra's b and c. Instead of keeping
    // b and c, I will keep an index into this array.
    
    static final int LP[] = { 1, 1, 3, 5, 9, 15, 25, 41, 67, 109,
        177, 287, 465, 753, 1219, 1973, 3193, 5167, 8361, 13529, 21891,
        35421, 57313, 92735, 150049, 242785, 392835, 635621, 1028457,
        1664079, 2692537, 4356617, 7049155, 11405773, 18454929, 29860703,
        48315633, 78176337, 126491971, 204668309, 331160281, 535828591,
        866988873 // the next number is > 31 bits.
    };
    
    public static <C extends Comparable<? super C>> void sort(C[] m,
        int lo, int hi) {
      int head = lo; // the offset of the first element of the prefix into m
    
      // These variables need a little explaining. If our string of heaps
      // is of length 38, then the heaps will be of size 25+9+3+1, which are
      // Leonardo numbers 6, 4, 2, 1. 
      // Turning this into a binary number, we get b01010110 = 0x56. We represent
      // this number as a pair of numbers by right-shifting all the zeros and 
      // storing the mantissa and exponent as "p" and "pshift".
      // This is handy, because the exponent is the index into L[] giving the
      // size of the rightmost heap, and because we can instantly find out if
      // the rightmost two heaps are consecutive Leonardo numbers by checking
      // (p&3)==3
    
      int p = 1; // the bitmap of the current standard concatenation >> pshift
      int pshift = 1;
    
      while (head < hi) {
        if ((p & 3) == 3) {
          // Add 1 by merging the first two blocks into a larger one.
          // The next Leonardo number is one bigger.
          sift(m, pshift, head);
          p >>>= 2;
          pshift += 2;
        } else {
          // adding a new block of length 1
          if (LP[pshift - 1] >= hi - head) {
            // this block is its final size.
            trinkle(m, p, pshift, head, false);
          } else {
            // this block will get merged. Just make it trusty.
            sift(m, pshift, head);
          }
    
          if (pshift == 1) {
            // LP[1] is being used, so we add use LP[0]
            p <<= 1;
            pshift--;
          } else {
            // shift out to position 1, add LP[1]
            p <<= (pshift - 1);
            pshift = 1;
          }
        }
        p |= 1;
        head++;
      }
    
      trinkle(m, p, pshift, head, false);
    
      while (pshift != 1 || p != 1) {
        if (pshift <= 1) {
          // block of length 1. No fiddling needed
          int trail = Integer.numberOfTrailingZeros(p & ~1);
          p >>>= trail;
          pshift += trail;
        } else {
          p <<= 2;
          p ^= 7;
          pshift -= 2;
    
          // This block gets broken into three bits. The rightmost
          // bit is a block of length 1. The left hand part is split into
          // two, a block of length LP[pshift+1] and one of LP[pshift].
          // Both these two are appropriately heapified, but the root
          // nodes are not necessarily in order. We therefore semitrinkle
          // both of them
    
          trinkle(m, p >>> 1, pshift + 1, head - LP[pshift] - 1, true);
          trinkle(m, p, pshift, head - 1, true);
        }
    
        head--;
      }
    }
    
    private static <C extends Comparable<? super C>> void sift(C[] m, int pshift,
        int head) {
      // we do not use Floyd's improvements to the heapsort sift, because we
      // are not doing what heapsort does - always moving nodes from near
      // the bottom of the tree to the root.
    
      C val = m[head];
    
      while (pshift > 1) {
        int rt = head - 1;
        int lf = head - 1 - LP[pshift - 2];
    
        if (val.compareTo(m[lf]) >= 0 && val.compareTo(m[rt]) >= 0)
          break;
        if (m[lf].compareTo(m[rt]) >= 0) {
          m[head] = m[lf];
          head = lf;
          pshift -= 1;
        } else {
          m[head] = m[rt];
          head = rt;
          pshift -= 2;
        }
      }
    
      m[head] = val;
    }
    
    private static <C extends Comparable<? super C>> void trinkle(C[] m, int p,
        int pshift, int head, boolean isTrusty) {
    
      C val = m[head];
    
      while (p != 1) {
        int stepson = head - LP[pshift];
    
        if (m[stepson].compareTo(val) <= 0)
          break; // current node is greater than head. Sift.
    
        // no need to check this if we know the current node is trusty,
        // because we just checked the head (which is val, in the first
        // iteration)
        if (!isTrusty && pshift > 1) {
          int rt = head - 1;
          int lf = head - 1 - LP[pshift - 2];
          if (m[rt].compareTo(m[stepson]) >= 0
              || m[lf].compareTo(m[stepson]) >= 0)
            break;
        }
    
        m[head] = m[stepson];
    
        head = stepson;
        int trail = Integer.numberOfTrailingZeros(p & ~1);
        p >>>= trail;
        pshift += trail;
        isTrusty = false;
      }
    
      if (!isTrusty) {
        m[head] = val;
        sift(m, pshift, head);
      }
    }