【问题标题】:Why is the the elapsed time for Quick Sort is so irregular in this case instead it should be strictly increasing?为什么在这种情况下快速排序的经过时间如此不规则,而应该严格增加?
【发布时间】:2018-03-03 04:08:06
【问题描述】:

快速排序算法的平均时间复杂度是 O(nlogn)。这意味着它应该随着 n 值的增加而严格增加。 但是为什么在这种情况下时间是增加 - 减少 - 增加。 为什么它随着n值的增加而减小

我正在使用 java 的函数生成随机输入

解决办法是什么?

public class Qsort {
static Scanner sc = new Scanner(System.in);
public static void main(String args[]) throws IOException {
File file = new File("Qtest.txt");
PrintWriter output = new PrintWriter(file);
int n,i;
System.out.println("Enter the times of iteration");
n = sc.nextInt();
for(int j=1000;j<=n*1000;j=j+1000)
{
    Random r = new Random();
    int array[];
    array = new int[j];
    for(i=0;i<j;i++)
    {
        array[i] = r.nextInt(2000);
    }
    long startTime = System.nanoTime();
    Qsort(array,0,j-1);
    long endTime = System.nanoTime();
    output.println(j + " " + (endTime-startTime));
    System.out.println("After sorting the time elapsed is " +(endTime-startTime));
}
output.close();
}

快速排序的排序功能

public static void Qsort(int A[],int start,int end)
{
    if(start>=end)  return;
    int p = partition(A,start,end);
    Qsort(A,start,p-1);
    Qsort(A,p+1,end);
}
public static int partition(int A[],int start,int end)
{
    int pivot = A[end];
    int p = start;
    for(int i =start;i<end;i++)
    {
        if(A[i]<=pivot)
        {
            int temp = A[i];
            A[i] = A[p];
            A[p] = temp;
            p++;
        }
    }
    int temp = A[p];
    A[p] = A[end];
    A[end] = temp;
    return p;
}
}

我得到的输出:

输入迭代次数 10

对于 1000 个输入:排序后经过的时间为 779133

对于 2000 个输入:排序后经过的时间是 8350639

对于 3000 个输入:排序后经过的时间为 607856

对于 4000 个输入:排序后经过的时间是 833593

对于 5000 个输入:排序后经过的时间是 1042426

对于 6000 个输入:排序后经过的时间是 1283195

对于 7000 个输入:排序后经过的时间为 1497488

对于 8000 个输入:排序后经过的时间是 1261182

对于 9000 个输入:排序后经过的时间为 1207128

对于 10000 个输入:排序后经过的时间为 1427456

【问题讨论】:

  • 你能解释一下吗
  • 如何在每次循环运行 j 的新值时清除缓存
  • 当您使用随机值时,所用的实际时间也更加随机。您也只对每种尺寸运行一种排序。这不会给你一个可靠的结果。每个尺寸运行一百万次。
  • 当我只为一个 j 值运行它时。这些是我每次得到的输出......在对5000经过的时间进行排序之后是2415390,在对10000经过的时间进行排序之后是3281184,在对15000经过的时间进行排序之后是2624570,为什么即使我运行它也会减少每次分开
  • 阅读:stackoverflow.com/questions/504103/… ...以及答案链接到的各种来源。

标签: java sorting quicksort


【解决方案1】:

您的问题包含两件事:衡量算法复杂性和微基准测试。

算法时间复杂度适用于操作数。您正在测量的是快速排序所花费的时间 - 这使其成为微基准测试。

微基准测试很难。在第一次运行时,您很可能会看到 JVM 预热时间。在第二次下降时,它可能是指令缓存或 JIT 编译。我建议以在运行之前对 10000 个元素进行排序的方式重新运行测试!其他因素也可能会影响结果,例如您的机器中的 CPU 利用率、阵列中的随机性(有时交换发生有时它不会)——这通常需要微基准测试多次运行每个“实验”并得出结论以统计方式,使用标准误差、百分位数等,而不是从单个实验中得出。

底线:预热确实有助于消除一些 JVM 特定的噪音,并且操作数是时间复杂度的更精确度量。

下面的代码是您的代码的一个版本,用于计算操作次数并进行预热:

public class QSort {
    private static int numOps = 0;
    static Scanner sc = new Scanner(System.in);

    public static void main(String args[]) throws IOException {
        File file = new File("Qtest.txt");
        PrintWriter output = new PrintWriter(file);
        int n, i;
        System.out.println("warming up...");
        Qsort(randomInts(1000000), 0, 100000-1);
        System.out.println("Enter the times of iteration");
        n = sc.nextInt();
        for (int j = 1000; j <= n * 1000; j = j + 1000) {
            int[] array = randomInts(j);
            long startTime = System.nanoTime();
            numOps = 0;
            Qsort(array, 0, j - 1);
            long endTime = System.nanoTime();
            output.println(j + " " + (endTime - startTime) + " " + numOps);
            System.out.println("After sorting the time elapsed is " + (endTime - startTime) + " numOps: " + numOps);
        }
        output.close();
    }

    private static int[] randomInts(int j) {
        int i;Random r = new Random();
        int array[];
        array = new int[j];
        for (i = 0; i < j; i++) {
            array[i] = r.nextInt(2000);
        }
        return array;
    }

    public static void Qsort(int A[], int start, int end) {
        if (start >= end) return;
        int p = partition(A, start, end);
        Qsort(A, start, p - 1);
        Qsort(A, p + 1, end);
    }

    public static int partition(int A[], int start, int end) {
        int pivot = A[end];
        int p = start;
        for (int i = start; i < end; i++) {
            if (A[i] <= pivot) {
                int temp = A[i];
                A[i] = A[p];
                A[p] = temp;
                p++;
                numOps++;
            }
        }
        int temp = A[p];
        A[p] = A[end];
        A[end] = temp;
        return p;
    }
}

输出:

warming up...
Enter the times of iteration
20
After sorting the time elapsed is 94206 numOps: 5191
After sorting the time elapsed is 150524 numOps: 12718
After sorting the time elapsed is 232478 numOps: 20359
After sorting the time elapsed is 314819 numOps: 31098
After sorting the time elapsed is 475933 numOps: 38483
After sorting the time elapsed is 500866 numOps: 55114
After sorting the time elapsed is 614642 numOps: 57251
After sorting the time elapsed is 693324 numOps: 68683
After sorting the time elapsed is 738800 numOps: 83332
After sorting the time elapsed is 798644 numOps: 83057
After sorting the time elapsed is 899891 numOps: 99975
After sorting the time elapsed is 987163 numOps: 113854
After sorting the time elapsed is 1059323 numOps: 124735
After sorting the time elapsed is 1103815 numOps: 143278
After sorting the time elapsed is 1192974 numOps: 164740
After sorting the time elapsed is 1276277 numOps: 166781
After sorting the time elapsed is 1344138 numOps: 180460
After sorting the time elapsed is 1439943 numOps: 204095
After sorting the time elapsed is 1593336 numOps: 209483
After sorting the time elapsed is 1644561 numOps: 225523

【讨论】:

  • 如果是指令缓存和 JIT 的情况,那为什么即使我运行一次 foreach 值 j 即 rin 分别为 5000,10000,15000,20000 。时间仍然不规则.
  • 好吧,如果你只运行一次,单独运行它会变得更糟——每一次运行现在都“处于热身阶段”,使得挂钟时间更难以预测。我在答案中添加了更多内容 - 建议测量实际操作次数而不是墙上时间,这总是有点混乱。
  • “让挂钟时间更难预测”所以现在如果每次指令对处理器来说都是新鲜的,那么每次新的输入时间都应该增加
  • 测量值不一定会以统一的方式增加,因为很难说“热身”阶段何时结束。它可能在您的迭代中间,或者在迭代结束时。这是导致已用时间(挂钟时间)的测量难以预测的最大因素之一。
  • Himanshu,如果你觉得这很满意,你介意接受我的回答吗?或者您还有其他问题吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-15
  • 2014-09-13
  • 1970-01-01
  • 2011-09-14
  • 2012-02-17
  • 1970-01-01
相关资源
最近更新 更多