[英]Test case for Insertion Sort, MergeSort and Quick Sort
我已經實現了(在Java中)Insertion Sort,MergeSort,ModifiedMergeSort和Quick Sort:
ModifiedMergeSort有一個變量用於元素的“綁定”。 當要排序的元素小於或等於“bound”時,請使用Insertion Sort對它們進行排序。
為什么版本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默認情況下會以解釋模式運行您的代碼。 這意味着在您的方法中找到的每個字節碼指令將在現場解釋,然后運行。
這種方法的優點是,它允許您的應用程序比在每次運行啟動時編譯為本機代碼的Java程序具有更快的啟動時間。
不利的一面是,雖然沒有啟動性能,但是你會得到一個性能較慢的程序。
為了改善這兩個問題,JVM團隊進行了權衡。 最初您的程序將被解釋,但JVM將收集有關哪些方法(或方法的一部分)被密集使用的信息,並將僅編譯那些似乎被大量使用的方法。 在編譯時,你會得到一個小的性能命中,但隨后代碼將比以前更快。
在進行測量時,您必須考慮這一事實。
標准方法是“預熱JVM”,即稍微運行算法以確保JVM完成它需要執行的所有編譯。 讓JVM變暖的代碼與您想要進行基准測試的代碼相同非常重要,否則在您對代碼進行基准測試時可能會進行一些編譯。
測量時間時,應使用System.nanoTime()
而不是System.currentTimeMillis
。 我不會詳細介紹,可以在這里訪問。
你也應該小心。 下面的代碼塊起初可能看起來相同,但大多數時候會產生不同的結果:
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所做的那樣)。 Big O表示法大多數時候只對大Os有意義。 Big O將告訴您算法如何處理難以忍受的輸入值大小,而不是它如何處理小數字。 規范示例例如是QuickSort,對於大數組而言,它將是王者,但對於大小為4或5的數組而言,這通常比選擇或插入排序更慢。 但是,您的輸入大小似乎足夠大。
正如Chang先前所說,Jakub完成的數學練習是完全錯誤的,不應該被考慮在內。
自己計算復雜性。 我假設10000個樣本用於以下計算:
插入排序: O(n ^ 2),10 000 * 10 000 = 100 000 000。
合並排序: O(nlogn),10 000 * log10 000 = 140 000。
合並插入(15): 15介於9(大小為20的數組)和10(大小為10的數組)2 ^ 10插入排序(大小為10),然后2 ^ 10 * 10 000合並:1 024 * 10 * 10(插入)+ 1 024 * 10 000(合並)= 10 342 400
合並插入(30): 30介於8(大小40的數組)和9(大小20的數組)2 ^ 9插入排序(大小20),然后2 ^ 9 * 10 000合並:512 * 20 * 20 (插入)+ 512 * 10 000(合並)= 5 324 800
與插入(45)合並: 45介於7(大小為80的數組)和8(大小為40的數組)2 ^ 8插入排序(大小為40),然后2 ^ 8 * 10 000合並:256 * 40 * 40 (插入)+ * 10 000(合並)= 2 969 600
Quicksort:雖然最壞情況的快速排序需要O(n ^ 2),但最壞的情況必須是特制的以達到這個極限。 大多數情況下,使用radomly生成的算法,平均為O(nlogn):10 000 * log10 000 = 140 000。
測量排序算法性能會變得非常痛苦,因為您需要在盡可能廣泛的輸入數據上有效地進行測量。
您在插入排序上看到的數字可能會因輸入數字而大幅偏差。 如果您在數組中僅使用0和1,並且數組是隨機生成的,那么實際上您可以更容易地解決算法問題。 對於給定的情況,平均一半的數組已經排序,並且您不需要將0和1相互比較。 問題是減少到向左傳輸所有0,平均只需要(log(n / 2))!+ n時間。 對於10 000,實際時間是5 000!+10 000 = 133 888。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.