我已经(用 Java)实现了插入排序、合并排序、ModifiedMergeSort 和快速排序:
ModifiedMergeSort 有一个用于元素“绑定(bind)”的变量。当要排序的元素小于或等于“绑定(bind)”时,然后使用插入排序对它们进行排序。
为什么版本 1 比版本 3、4 和 5 更好?
版本 2 和 6 的结果是否真实?
这是我的结果(以毫秒为单位):
Version 1 - Insertion Sort: Run-Times over 50 test runs
Input Size Best-Case Worst-Case Average-Case
N = 10000 14 19 14.96
N = 20000 59 60 59.3
N = 40000 234 277 243.1
Version 2 - Merge Sort: Run-Times over 50 test runs
Input Size Best-Case Worst-Case Average-Case
N = 10000 1 15 1.78
N = 20000 3 8 3.4
N = 40000 6 9 6.7
Version 3 - Merge Sort and Insertion Sort on 15 elements: Run-Times over 50 test runs
Input Size Best-Case Worst-Case Average-Case
N = 10000 41 52 45.42
N = 20000 170 176 170.56
N = 40000 712 823 728.08
Version 4 - Merge Sort and Insertion Sort on 30 elements: Run-Times over 50 test runs
Input Size Best-Case Worst-Case Average-Case
N = 10000 27 33 28.04
N = 20000 113 119 114.36
N = 40000 436 497 444.2
Version 5 - Merge Sort and Insertion Sort on 45 elements: Run-Times over 50 test runs
Input Size Best-Case Worst-Case Average-Case
N = 10000 20 21 20.68
N = 20000 79 82 79.7
N = 40000 321 383 325.64
Version 6 - Quick Sort: Run-Times over 50 test runs
Input Size Best-Case Worst-Case Average-Case
N = 10000 1 9 1.18
N = 20000 2 3 2.06
N = 40000 4 5 4.26
这是我的代码:
package com.testing;
import com.sorting.InsertionSort;
import com.sorting.MergeSort;
import com.sorting.ModifiedMergeSort;
import com.sorting.RandomizedQuickSort;
/**
*
* @author mih1406
*/
public class Main {
private static final int R = 50; // # of tests
private static int N = 0; // Input size
private static int[] array; // Tests array
private static int[] temp; // Tests array
private static long InsertionSort_best = -1;
private static long InsertionSort_worst = -1;
private static double InsertionSort_average = -1.0;
private static long MergeSort_best = -1;
private static long MergeSort_worst = -1;
private static double MergeSort_average = -1.0;
private static long ModifiedMergeSort_V3_best = -1;
private static long ModifiedMergeSort_V3_worst = -1;
private static double ModifiedMergeSort_V3_average = -1.0;
private static long ModifiedMergeSort_V4_best = -1;
private static long ModifiedMergeSort_V4_worst = -1;
private static double ModifiedMergeSort_V4_average = -1.0;
private static long ModifiedMergeSort_V5_best = -1;
private static long ModifiedMergeSort_V5_worst = -1;
private static double ModifiedMergeSort_V5_average = -1.0;
private static long RandomizedQuickSort_best = -1;
private static long RandomizedQuickSort_worst = -1;
private static double RandomizedQuickSort_average = -1.0;
public static void main(String args[]) {
StringBuilder InsertionSort_text = new StringBuilder(
"Version 1 - Insertion Sort: Run-Times over 50 test runs\n");
StringBuilder MergeSort_text = new StringBuilder(
"Version 2 - Merge Sort: Run-Times over 50 test runs\n");
StringBuilder ModifiedMergeSort_V3_text = new StringBuilder(
"Version 3 - Merge Sort and Insertion Sort on 15 elements: Run-Times over 50 test runs\n");
StringBuilder ModifiedMergeSort_V4_text = new StringBuilder(
"Version 4 - Merge Sort and Insertion Sort on 30 elements: Run-Times over 50 test runs\n");
StringBuilder ModifiedMergeSort_V5_text = new StringBuilder(
"Version 5 - Merge Sort and Insertion Sort on 45 elements: Run-Times over 50 test runs\n");
StringBuilder RandomizedQuickSort_text = new StringBuilder(
"Version 6 - Quick Sort: Run-Times over 50 test runs\n");
InsertionSort_text.append("Input Size\t\t"
+ "Best-Case\t\tWorst-Case\t\tAverage-Case\n");
MergeSort_text.append("Input Size\t\t"
+ "Best-Case\t\tWorst-Case\t\tAverage-Case\n");
ModifiedMergeSort_V3_text.append("Input Size\t\t"
+ "Best-Case\t\tWorst-Case\t\tAverage-Case\n");
ModifiedMergeSort_V4_text.append("Input Size\t\t"
+ "Best-Case\t\tWorst-Case\t\tAverage-Case\n");
ModifiedMergeSort_V5_text.append("Input Size\t\t"
+ "Best-Case\t\tWorst-Case\t\tAverage-Case\n");
RandomizedQuickSort_text.append("Input Size\t\t"
+ "Best-Case\t\tWorst-Case\t\tAverage-Case\n");
// Case N = 10000
N = 10000;
fillArray();
testing_InsertionSort();
testing_MergeSort();
testing_ModifiedMergeSort(15);
testing_ModifiedMergeSort(30);
testing_ModifiedMergeSort(45);
testing_RandomizedQuickSort();
InsertionSort_text.append("N = " + N + "\t\t" + InsertionSort_best
+ "\t\t\t" + InsertionSort_worst + "\t\t\t"
+ InsertionSort_average + "\n");
MergeSort_text.append("N = " + N + "\t\t" + MergeSort_best
+ "\t\t\t" + MergeSort_worst + "\t\t\t"
+ MergeSort_average + "\n");
ModifiedMergeSort_V3_text.append("N = " + N + "\t\t" + ModifiedMergeSort_V3_best
+ "\t\t\t" + ModifiedMergeSort_V3_worst + "\t\t\t"
+ ModifiedMergeSort_V3_average + "\n");
ModifiedMergeSort_V4_text.append("N = " + N + "\t\t" + ModifiedMergeSort_V4_best
+ "\t\t\t" + ModifiedMergeSort_V4_worst + "\t\t\t"
+ ModifiedMergeSort_V4_average + "\n");
ModifiedMergeSort_V5_text.append("N = " + N + "\t\t" + ModifiedMergeSort_V5_best
+ "\t\t\t" + ModifiedMergeSort_V5_worst + "\t\t\t"
+ ModifiedMergeSort_V5_average + "\n");
RandomizedQuickSort_text.append("N = " + N + "\t\t" + RandomizedQuickSort_best
+ "\t\t\t" + RandomizedQuickSort_worst + "\t\t\t"
+ RandomizedQuickSort_average + "\n");
// Case N = 20000
N = 20000;
fillArray();
testing_InsertionSort();
testing_MergeSort();
testing_ModifiedMergeSort(15);
testing_ModifiedMergeSort(30);
testing_ModifiedMergeSort(45);
testing_RandomizedQuickSort();
InsertionSort_text.append("N = " + N + "\t\t" + InsertionSort_best
+ "\t\t\t" + InsertionSort_worst + "\t\t\t"
+ InsertionSort_average + "\n");
MergeSort_text.append("N = " + N + "\t\t" + MergeSort_best
+ "\t\t\t" + MergeSort_worst + "\t\t\t"
+ MergeSort_average + "\n");
ModifiedMergeSort_V3_text.append("N = " + N + "\t\t" + ModifiedMergeSort_V3_best
+ "\t\t\t" + ModifiedMergeSort_V3_worst + "\t\t\t"
+ ModifiedMergeSort_V3_average + "\n");
ModifiedMergeSort_V4_text.append("N = " + N + "\t\t" + ModifiedMergeSort_V4_best
+ "\t\t\t" + ModifiedMergeSort_V4_worst + "\t\t\t"
+ ModifiedMergeSort_V4_average + "\n");
ModifiedMergeSort_V5_text.append("N = " + N + "\t\t" + ModifiedMergeSort_V5_best
+ "\t\t\t" + ModifiedMergeSort_V5_worst + "\t\t\t"
+ ModifiedMergeSort_V5_average + "\n");
RandomizedQuickSort_text.append("N = " + N + "\t\t" + RandomizedQuickSort_best
+ "\t\t\t" + RandomizedQuickSort_worst + "\t\t\t"
+ RandomizedQuickSort_average + "\n");
// Case N = 40000
N = 40000;
fillArray();
testing_InsertionSort();
testing_MergeSort();
testing_ModifiedMergeSort(15);
testing_ModifiedMergeSort(30);
testing_ModifiedMergeSort(45);
testing_RandomizedQuickSort();
InsertionSort_text.append("N = " + N + "\t\t" + InsertionSort_best
+ "\t\t\t" + InsertionSort_worst + "\t\t\t"
+ InsertionSort_average + "\n");
MergeSort_text.append("N = " + N + "\t\t" + MergeSort_best
+ "\t\t\t" + MergeSort_worst + "\t\t\t"
+ MergeSort_average + "\n");
ModifiedMergeSort_V3_text.append("N = " + N + "\t\t" + ModifiedMergeSort_V3_best
+ "\t\t\t" + ModifiedMergeSort_V3_worst + "\t\t\t"
+ ModifiedMergeSort_V3_average + "\n");
ModifiedMergeSort_V4_text.append("N = " + N + "\t\t" + ModifiedMergeSort_V4_best
+ "\t\t\t" + ModifiedMergeSort_V4_worst + "\t\t\t"
+ ModifiedMergeSort_V4_average + "\n");
ModifiedMergeSort_V5_text.append("N = " + N + "\t\t" + ModifiedMergeSort_V5_best
+ "\t\t\t" + ModifiedMergeSort_V5_worst + "\t\t\t"
+ ModifiedMergeSort_V5_average + "\n");
RandomizedQuickSort_text.append("N = " + N + "\t\t" + RandomizedQuickSort_best
+ "\t\t\t" + RandomizedQuickSort_worst + "\t\t\t"
+ RandomizedQuickSort_average + "\n");
System.out.println(InsertionSort_text);
System.out.println(MergeSort_text);
System.out.println(ModifiedMergeSort_V3_text);
System.out.println(ModifiedMergeSort_V4_text);
System.out.println(ModifiedMergeSort_V5_text);
System.out.println(RandomizedQuickSort_text);
}
private static void fillArray() {
// (re)creating the array
array = new int[N];
// filling the array with random numbers
// using for-loop and the given random generator instruction
for(int i = 0; i < array.length; i++) {
array[i] = (int)(1 + Math.random() * (N-0+1));
}
}
private static void testing_InsertionSort() {
// run-times arrays
long [] run_times = new long[R];
// start/finish times
long start;
long finish;
for(int i = 0; i < R; i++) {
copyArray();
// recording start time
start = System.currentTimeMillis();
// testing the algorithm
InsertionSort.sort(temp);
// recording finish time
finish = System.currentTimeMillis();
run_times[i] = finish-start;
}
InsertionSort_best = findMin(run_times);
InsertionSort_worst = findMax(run_times);
InsertionSort_average = findAverage(run_times);
}
private static void testing_MergeSort() {
// run-times arrays
long [] run_times = new long[R];
// start/finish times
long start;
long finish;
for(int i = 0; i < R; i++) {
copyArray();
// recording start time
start = System.currentTimeMillis();
// testing the algorithm
MergeSort.sort(temp);
// recording finish time
finish = System.currentTimeMillis();
run_times[i] = finish-start;
}
MergeSort_best = findMin(run_times);
MergeSort_worst = findMax(run_times);
MergeSort_average = findAverage(run_times);
}
private static void testing_ModifiedMergeSort(int bound) {
// run-times arrays
long [] run_times = new long[R];
// setting the bound
ModifiedMergeSort.bound = bound;
// start/finish times
long start;
long finish;
for(int i = 0; i < R; i++) {
copyArray();
// recording start time
start = System.currentTimeMillis();
// testing the algorithm
ModifiedMergeSort.sort(temp);
// recording finish time
finish = System.currentTimeMillis();
run_times[i] = finish-start;
}
if(bound == 15) {
ModifiedMergeSort_V3_best = findMin(run_times);
ModifiedMergeSort_V3_worst = findMax(run_times);
ModifiedMergeSort_V3_average = findAverage(run_times);
}
if(bound == 30) {
ModifiedMergeSort_V4_best = findMin(run_times);
ModifiedMergeSort_V4_worst = findMax(run_times);
ModifiedMergeSort_V4_average = findAverage(run_times);
}
if(bound == 45) {
ModifiedMergeSort_V5_best = findMin(run_times);
ModifiedMergeSort_V5_worst = findMax(run_times);
ModifiedMergeSort_V5_average = findAverage(run_times);
}
}
private static void testing_RandomizedQuickSort() {
// run-times arrays
long [] run_times = new long[R];
// start/finish times
long start;
long finish;
for(int i = 0; i < R; i++) {
copyArray();
// recording start time
start = System.currentTimeMillis();
// testing the algorithm
RandomizedQuickSort.sort(temp);
// recording finish time
finish = System.currentTimeMillis();
run_times[i] = finish-start;
}
RandomizedQuickSort_best = findMin(run_times);
RandomizedQuickSort_worst = findMax(run_times);
RandomizedQuickSort_average = findAverage(run_times);
}
private static long findMax(long[] times) {
long max = times[0];
for(int i = 1; i < times.length; i++) {
if(max < times[i]) {
max = times[i];
}
}
return max;
}
private static long findMin(long[] times) {
long min = times[0];
for(int i = 1; i < times.length; i++) {
if(min > times[i]) {
min = times[i];
}
}
return min;
}
private static double findAverage(long[] times) {
long sum = 0;
double avg;
for(int i = 0; i < times.length; i++) {
sum += times[i];
}
avg = (double)sum/(double)times.length;
return avg;
}
private static void copyArray() {
temp = new int[N];
System.arraycopy(array, 0, temp, 0, array.length);
}
}
最佳答案
您目前采用的方法似乎存在一些系统性错误。我将说明您面临的一些最明显的实验问题,即使它们可能不是您实验结果的直接罪魁祸首。
JVM 编译
正如我之前在评论中所述,JVM 默认会在解释模式下运行您的代码。这意味着在您的方法中找到的每个字节码指令都将被当场解释,然后运行。
这种方法的优点是,与在每次运行启动时编译为 native 代码的 Java 程序相比,它允许您的应用程序启动时间更快。
缺点是虽然没有启动性能受到影响,但您将获得比其他情况下执行速度更慢的程序。
为了改善这两个问题,JVM 团队进行了权衡。最初您的程序将被解释,但 JVM 将收集有关哪些方法(或方法的一部分)被密集使用的信息,并且只会编译那些看起来被大量使用的方法。编译时,您的性能会受到一点影响,但代码会比以前快得多。
您在进行测量时必须考虑到这一事实。
标准方法是“预热 JVM”,也就是说,运行您的算法一段时间以确保 JVM 执行它需要执行的所有编译。让预热 JVM 的代码与您要进行基准测试的代码相同很重要,否则在对代码进行基准测试时可能会进行一些编译。
测量时间
测量时间时,您应该使用System.nanoTime()
而不是System.currentTimeMillis
。具体的就不说了,可以看here .
你也应该小心。以下代码块初看起来可能是等价的,但大多数时候会产生不同的结果:
totalDuration = 0;
for (i = 0; i < 1000; ++i)
startMeasure = now();
algorithm();
endMeasure = now();
duration = endMeasure - startMeasure;
totalDuration += duration;
}
//...
TRIALS_COUNT = 1000;
startMeasure = now();
for (i = 0; i < TRIALS_COUNT; ++i)
algorithm();
}
endMeasure = now();
duration = endMeasure - startMeasure / TRIALS_COUNT;
为什么?因为特别是对于更快的 algorithm()
实现,它们越快,就越难获得准确的结果。
大输入值
渐近符号有助于理解不同的算法如何针对较大的 n
值升级。将它们应用于较小的输入值通常是荒谬的,因为在这种情况下,通常您想要的是计算精确的操作数量及其相关成本(类似于 Jakub 所做的)。大多数时候,大 O 符号只对大 O 有意义。 Big O 将告诉您该算法如何处理难以忍受的输入值大小,而不是它如何处理小数字。典型的例子是快速排序,它对于大数组来说是王道,但对于大小为 4 或 5 的数组,它通常比选择或插入排序慢。不过,您的输入尺寸似乎足够大。
最后一点
并且正如 Chang 先前所说,Jakub 所做的数学练习是完全错误的,不应予以考虑。
关于java - 插入排序、合并排序和快速排序的测试用例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15317384/