Java 快速排序未排序的列表运行速度比已排序的列表快

Java 快速排序未排序的列表运行速度比已排序的列表快,java,algorithm,quicksort,timing,nanotime,Java,Algorithm,Quicksort,Timing,Nanotime,对于第一个元素轴的快速排序,我得到了奇怪的计时结果。我的代码首先使用未排序、非随机的整数列表运行快速排序。然后,它对之前排序的列表进行排序。在我选择的透视图中,我认为排序列表的快速排序比未排序列表运行得最差。我已经用以下结果验证了这一点: Quicksort 1st run (unsorted, non-randomized) # of calls to exch(): 14 # of calls to sort(): 17 # of calls to partition():8 # of ca

对于第一个元素轴的快速排序,我得到了奇怪的计时结果。我的代码首先使用未排序、非随机的整数列表运行快速排序。然后,它对之前排序的列表进行排序。在我选择的透视图中,我认为排序列表的快速排序比未排序列表运行得最差。我已经用以下结果验证了这一点:

Quicksort 1st run (unsorted, non-randomized)
# of calls to exch(): 14
# of calls to sort(): 17
# of calls to partition():8
# of calls to less(): 47
Sorted?: true
Time to sort input: 0.004212 secs

Quicksort 2nd run (sorted)
# of calls to exch(): 25
# of calls to sort(): 40
# of calls to partition(): 19
# of calls to less(): 138
Sorted?: true
Time to sort input: 0.000535 secs
请注意,时间安排没有意义。第二次快速排序应比第一次快速排序慢

Timing code:
start();
Quick.sort(a);
stop();

private static void start(){
   start = System.nanoTime();       
}

private static void stop(){
   elapsed = (System.nanoTime() - start)/1E9;
}   
快速排序算法是正确的。所以我实现计时器的方式肯定有问题

完整代码:

public class Quick {

static int exchCount; //DEBUG
static int sortCount; //DEBUG
static int partitionCount; //DEBUG
static int compareCount; //DEBUG

public static void sort(Comparable[]a){
    //StdRandom.shuffle(a);
    exchCount=0; //DEBUG
    sortCount=0; //DEBUG
    partitionCount=0; //DEBUG
    compareCount=0; //DEBUG
    sort(a, 0, a.length-1);
    System.out.printf("# of calls to exch(): %d%n", exchCount); //DEBUG
    System.out.printf("# of calls to sort(): %d%n", sortCount); // DEBUG
    System.out.printf("# of calls to partition(): %d%n", partitionCount); // DEBUG
    System.out.printf("# of calls to less(): %d%n", compareCount); // DEBUG
    return;

}

private static void sort(Comparable a[], int lo, int hi){
    sortCount++; // DEBUG

    if (hi<=lo) return; // base case    
    int p = partition(a, lo, hi); // select partition
    sort(a, lo, p-1); // recursively sort left side of partition
    sort(a, p+1, hi); // recursively sort right side of partition

    return; 
}


private static int partition(Comparable[]a, int lo, int hi){    
    partitionCount++; //DEBUG
    int i = lo, j = hi+1; // set pointers i (left) & j (right)

    Comparable p = a[lo]; // select a partition point we'll use lo as default

    while (true){

        // continue walking right if values are < p
        // captures any value < p
        while (less(a[++i], p)) if(i==hi)break;

        // continue walking left if values are > p
        while (less(p, a[--j]));

        if(i>=j) break; // has i crossed j?

        exch(a, i, j);

    }

    exch(a, lo, j); 
    return j;
}


private static boolean less(Comparable a, Comparable b){
    compareCount++; //DEBUG
    return a.compareTo(b)<0;

}

private static void exch(Comparable[] a, int i, int j){
    exchCount++; //DEBUG
    Comparable tmp = a[i];
    a[i] = a[j];
    a[j] = tmp; 

}

}


public class SortClient {

static double start=0, elapsed=0;
public static void main(String[] args) {

    for(int i=0; i<10; i++){

        //Comparable[] a={"K","R","A","T","E","L","E","P","U","I"
        //  ,"M","Q","C","X","O","S"};
        //Comparable[] a = DataReader.readInt("http://algs4.cs.princeton.edu/14analysis/8Kints.txt");
        Comparable[] a={8,4,45,23,13,1,65,44,9,8,3,33,21};

        start();
        Quick.sort(a);
        stop();
        System.out.printf("Quicksort#1%n");
        System.out.printf("Sorted?: %b%n",isSorted(a));
        System.out.printf("Time to sort input: %f secs%n%n%n",elapsed);

        start();
        Quick.sort(a);
        stop();
        System.out.printf("Quicksort#2%n");
        System.out.printf("Sorted?: %b%n",isSorted(a));
        System.out.printf("Time to sort input: %f secs%n%n%n",elapsed);

    }
}

private static void start(){

    start = System.nanoTime();

}

private static void stop(){

    elapsed = (System.nanoTime() - start)/1E9;


}

private static boolean isSorted(Comparable[]a){

    for(int i=0; i<a.length-2; i++)
       if(a[i].compareTo(a[i+1])>0)
           return false;
    return true;

}

}
公共类快速{
静态int exchCount;//调试
静态int-sortCount;//调试
静态int partitionCount;//调试
静态int compareCount;//调试
公共静态无效排序(可比[]a){
//施特兰多姆·沙夫勒(a);
exchCount=0;//调试
sortCount=0;//调试
partitionCount=0;//调试
compareCount=0;//调试
排序(a,0,a.length-1);
System.out.printf(“#对exch()的调用次数:%d%n”,exchCount);//调试
System.out.printf(“#对sort()的调用次数:%d%n”,sortCount);//调试
System.out.printf(“#对partition()的调用次数:%d%n”,partitionCount);//调试
System.out.printf(“#对less()的调用次数:%d%n”,compareCount);//调试
回来
}
专用静态无效排序(可比较的a[],int lo,int hi){
sortCount++;//调试
如果(hi“我们都知道快速排序在排序列表中比在未排序列表中运行得最差”:这是一个大胆的说法

快速排序的效率取决于正确选择支点:好的支点是这样的:它们以平衡的方式分割要排序的区域,因此过程保持二分法,这会导致
O(N.Lg(N))
行为。相反,导致最大不平衡的糟糕选择会导致二次
O(N²)
退化,因为过程保持增量


天真的轴心选择策略,如“第一个元素”,确实会导致排序序列出现最坏的情况。但更明智的实现,如“三个中位数(第一、中间和最后)”将以最佳方式执行。

正如@AbhisekBansal指出的,快速排序按预期在大型数组上运行,即超过1000项。在对4K整数列表运行快速排序后,在所有迭代中按预期执行快速排序(即T*未排序* 输出(前4次迭代):


根据您上面的输出,第二次运行速度更快。您执行这项操作的次数是多少?您必须执行数千次才能让jvm“预热”。@BrettOkken我尝试执行了10、100、1000、5000次,您在某些方面是对的,而不是全部,排序的(第二次运行)确实比未排序的(第一次运行)慢正如所料,jvm“预热”是什么意思?这是Java/解释语言特有的问题吗?在多次执行之后,编译器可以优化执行行为。你真的需要几万次或几十万次执行。@OliCharlesworth感谢你指出这一点。我已经更正了我的措辞。Yves,你是对的。我应该更仔细地选择我的措辞。从我所看到的,你是我们在小阵列上做实验(最多十分之几个元素)。出于许多原因,你应该在更大的阵列上工作。我按照阿披舍克·班萨尔的建议,用1k和4k元素运行了更大的阵列。预排序的阵列的性能比未排序的阵列差。
Quicksort#1(unsorted)
calls to exch(): 11466
calls to sort(): 5329
calls to partition(): 2664
calls to less(): 60092
Sorted?: true
Time to sort input: 0.006784 secs

Quicksort#2(presorted)
calls to exch(): 3999
calls to sort(): 7999
calls to partition(): 3999
calls to less(): 8005998
Sorted?: true
Time to sort input: 0.079226 secs

Quicksort#1(unsorted)
calls to exch(): 11466
calls to sort(): 5329
calls to partition(): 2664
calls to less(): 60092
Sorted?: true
Time to sort input: 0.001649 secs

Quicksort#2(presorted)
calls to exch(): 3999
calls to sort(): 7999
calls to partition(): 3999
calls to less(): 8005998
Sorted?: true
Time to sort input: 0.079353 secs

Quicksort#1(unsorted)
calls to exch(): 11466
calls to sort(): 5329
calls to partition(): 2664
calls to less(): 60092
Sorted?: true
Time to sort input: 0.001680 secs

Quicksort#2(presorted)
calls to exch(): 3999
calls to sort(): 7999
calls to partition(): 3999
calls to less(): 8005998
Sorted?: true
Time to sort input: 0.079331 secs

Quicksort#1(unsorted)
calls to exch(): 11466
calls to sort(): 5329
calls to partition(): 2664
calls to less(): 60092
Sorted?: true
Time to sort input: 0.001625 secs

Quicksort#2(presorted)
calls to exch(): 3999
calls to sort(): 7999
calls to partition(): 3999
calls to less(): 8005998
Sorted?: true
Time to sort input: 0.079593 secs