为什么首先对较小的子阵列进行排序会导致更快的快速排序?

时间:2014-08-22 23:56:10

标签: java optimization quicksort tail-recursion

我一直在尝试使用quicksort并学习如何测试程序的速度。有一点对我没有意义。我尝试实现了一个在Java中使用尾递归的快速排序算法。它通常运行得更快。但后来我才知道Java不支持尾调用优化。为什么它会跑得更快呢?

这是代码。

import java.util.Arrays;
import java.util.Random;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;

public class QuicksortCompare
{
    private final static Random rand = new Random();
    private static final int MIN_LENGTH = 10;
    private static final int MAX_LENGTH = 1000;
    private static final int MIN_VAL = -1000;
    private static final int MAX_VAL = 1000;

    public static void main(String[] args)
    {
        /*long[] avgTimes = {0, 0};
        System.out.println("press enter to start");
        Scanner in = new Scanner(System.in);
        in.nextLine();*/
        for(;;)
        {
            int[] arr1 = generateRandomArray(MIN_LENGTH, MAX_LENGTH, MIN_VAL, MAX_VAL);
            int[] arr2 = Arrays.copyOf(arr1, arr1.length);
            long startTime = System.nanoTime();
            quicksortNormal(arr1, 0, arr1.length-1);
            long endTime = System.nanoTime();
            long duration = endTime-startTime;
            System.out.print("normal: "+duration+" ns\t");
            startTime = System.nanoTime();
            quickSortTailRecurse(arr2, 0, arr2.length-1);
            endTime = System.nanoTime();
            duration = endTime-startTime;
            System.out.println("special: "+duration+" ns\tlength: "+arr1.length);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                Logger.getLogger(QuicksortCompare.class.getName()).log(Level.SEVERE, null, ex);
            }
        }    }


    public static int[] generateRandomArray(int minLength, int maxLength, int minVal, int maxVal)
    {
        int[] arr = new int[minLength+rand.nextInt((maxLength-minLength)+1)];
        populateArray(arr, minVal, maxVal);
        return arr;
    }

    public static void populateArray(int arr[], int min, int max)
    {
        for(int i = 0; i < arr.length; i++)
            arr[i] = min+rand.nextInt((max-min)+1);
    }

    private static void quickSortTailRecurse(int[] arr, int lo, int hi){
        if(lo >= hi) return;

        int p = partition(arr, lo, hi);

        if((p - lo ) <= (hi-p)){
          quickSortTailRecurse(arr, lo, p);
          quickSortTailRecurse(arr, p+1, hi);
        }else {
          quickSortTailRecurse(arr, p+1, hi);
          quickSortTailRecurse(arr, lo, p);
        }
      }

    public static void quicksortNormal(int[] a, int p, int r)
    {
        if(p<r)
        {
            int q = partition(a,p,r);
            quicksortNormal(a,p,q);
            quicksortNormal(a,q+1,r);
        }
    }

    public static void quicksortSmallSide_old(int[] a, int p, int r)
    {
        while(p<r)
        {
            int q = partition(a,p,r);
            if(q-p < r-q)
            {
                quicksortSmallSide(a,p,q);//may supposed to be q-1
                p = q+1;
            }
            else
            {
                quicksortSmallSide(a,q+1,r);
                r = q-1;
            }
        }
    } 

    private static int partition(int[] a, int p, int r) {

        int x = a[p];
        int i = p-1 ;
        int j = r+1 ;

        while (true) {
            i++;
            while ( i< r && a[i] < x)
                i++;
            j--;
            while (j>p && a[j] > x)
                j--;

            if (i < j)
                swap(a, i, j);
            else
                return j;
        }
    }

    private static void swap(int[] a, int i, int j) {
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }

}

以下是探查者:

screenshot

这是一些输出:

run:
normal: 42000 ns    special: 39000 ns   length: 35
normal: 1240000 ns  special: 1202000 ns length: 829
normal: 336000 ns   special: 37000 ns   length: 63
normal: 358000 ns   special: 179000 ns  length: 839
normal: 102000 ns   special: 62000 ns   length: 322
normal: 72000 ns    special: 61000 ns   length: 393
normal: 11000 ns    special: 10000 ns   length: 75
normal: 26000 ns    special: 27000 ns   length: 210
normal: 134000 ns   special: 58000 ns   length: 337
normal: 91000 ns    special: 94000 ns   length: 393
normal: 66000 ns    special: 70000 ns   length: 551
normal: 107000 ns   special: 115000 ns  length: 805
normal: 54000 ns    special: 57000 ns   length: 386
normal: 21000 ns    special: 24000 ns   length: 197
normal: 29000 ns    special: 37000 ns   length: 250
normal: 117000 ns   special: 122000 ns  length: 932
normal: 199000 ns   special: 205000 ns  length: 963
normal: 31000 ns    special: 147000 ns  length: 148
normal: 16000 ns    special: 16000 ns   length: 136
normal: 193000 ns   special: 191000 ns  length: 959
normal: 107000 ns   special: 199000 ns  length: 634
objc[712]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/bin/java and /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/jre/lib/libinstrument.dylib. One of the two will be used. Which one is undefined.
Profiler Agent: Waiting for connection on port 5140 (Protocol version: 15)
Profiler Agent: Established connection with the tool
Profiler Agent: Local accelerated session
normal: 674000 ns   special: 864000 ns  length: 450
normal: 24424000 ns special: 1798000 ns length: 186
normal: 3561000 ns  special: 2434000 ns length: 678
normal: 2112000 ns  special: 2148000 ns length: 908
normal: 1595000 ns  special: 1582000 ns length: 739
normal: 2179000 ns  special: 2248000 ns length: 936
normal: 1025000 ns  special: 997000 ns  length: 447
normal: 1185000 ns  special: 1161000 ns length: 574
normal: 1507000 ns  special: 2678000 ns length: 741
...
normal: 1554000 ns  special: 1534000 ns length: 656
normal: 366000 ns   special: 318000 ns  length: 152
normal: 138000 ns   special: 132000 ns  length: 67
normal: 1146000 ns  special: 1095000 ns length: 478
BUILD STOPPED (total time: 5 minutes 33 seconds)

1 个答案:

答案 0 :(得分:4)

Java不保证尾部调用优化。

JVM实现可能仍然会这样做,但Java代码不能依赖它来获得正确性。