Skip to content

Commit 8debb36

Browse files
SvenWoltmannSven Woltmann
authored and
Sven Woltmann
committed
Add Ultimate test with basic and efficient sort algorithms.
1 parent 5076a00 commit 8debb36

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+3143
-2
lines changed

.gitignore

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.classpath
2+
.idea
3+
.project
4+
.settings
5+
out
6+
target
7+
**/gitignore
8+
*.iml
9+
*.class
10+
*.bin
11+
src/eu/happycoders/cds/sandbox

README.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
# Ultimate Guide - Sorting in Java
2-
1+
# Sorting Algorithms - Ultimate Guide

pom.xml

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<groupId>eu.happycoders</groupId>
8+
<artifactId>sort</artifactId>
9+
<version>1.0-SNAPSHOT</version>
10+
11+
<properties>
12+
<maven.compiler.target>14</maven.compiler.target>
13+
<maven.compiler.source>14</maven.compiler.source>
14+
</properties>
15+
16+
<dependencies>
17+
<dependency>
18+
<scope>test</scope>
19+
<groupId>org.junit.jupiter</groupId>
20+
<artifactId>junit-jupiter-engine</artifactId>
21+
<version>5.6.2</version>
22+
</dependency>
23+
<dependency>
24+
<groupId>org.junit.jupiter</groupId>
25+
<artifactId>junit-jupiter-params</artifactId>
26+
<version>5.6.2</version>
27+
<scope>test</scope>
28+
</dependency>
29+
</dependencies>
30+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package eu.happycoders.sort;
2+
3+
import eu.happycoders.sort.method.*;
4+
import eu.happycoders.sort.utils.ArrayUtils;
5+
6+
import java.util.Locale;
7+
import java.util.function.Function;
8+
9+
/**
10+
* Measures the performance of all sorting algorithms for various input sizes.
11+
*
12+
* @author <a href="[email protected]">Sven Woltmann</a>
13+
*/
14+
public class CountOperations {
15+
16+
private static final int MIN_SORTING_SIZE = 1 << 3;
17+
private static final int MAX_SORTING_SIZE = 1 << 15;
18+
19+
// Stop when counting takes longer than 20 seconds
20+
private static final int MAX_COUNTING_TIME_SECS = 20;
21+
22+
public static void main(String[] args) {
23+
for (SortAlgorithm algorithm : UltimateTest.ALGORITHMS) {
24+
if (algorithm.supportsCounting()) {
25+
countOps(algorithm);
26+
}
27+
}
28+
}
29+
30+
private static void countOps(SortAlgorithm algorithm) {
31+
// Test with a random, a sorted, and a reversed (= sorted descending) array
32+
countOps(algorithm, "random", ArrayUtils::createRandomArray);
33+
34+
// Quicksort with right pivot element would go n x into recursion here...
35+
if (algorithm.isSuitableForSortedInput()) {
36+
countOps(algorithm, "ascending", ArrayUtils::createSortedArray);
37+
countOps(algorithm, "descending", ArrayUtils::createReversedArray);
38+
}
39+
}
40+
41+
private static void countOps(SortAlgorithm algorithm,
42+
String inputOrder,
43+
Function<Integer, int[]> arraySupplier) {
44+
System.out.printf("%n--- %s (order: %s) ---%n",
45+
algorithm.getName(), inputOrder);
46+
47+
// Sort until sorting takes more than MAX_SORTING_TIME_SECS
48+
// Upper limit used by insertion sort on already sorted data
49+
for (int size = MIN_SORTING_SIZE;
50+
size <= MAX_SORTING_SIZE && algorithm.isSuitableForInputSize(size);
51+
size <<= 1) {
52+
long time = System.currentTimeMillis();
53+
Counters counters = countOps(algorithm, arraySupplier.apply(size));
54+
time = System.currentTimeMillis() - time;
55+
56+
System.out.printf(Locale.US,
57+
"%s (order: %s): size = %,11d --> %s%n",
58+
algorithm.getName(),
59+
inputOrder,
60+
size,
61+
counters);
62+
63+
// Stop after specified time
64+
if (time > MAX_COUNTING_TIME_SECS * 1_000L) {
65+
break;
66+
}
67+
}
68+
}
69+
70+
private static Counters countOps(SortAlgorithm algorithm, int[] elements) {
71+
Counters counters = new Counters();
72+
algorithm.sort(elements, counters);
73+
return counters;
74+
}
75+
76+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
package eu.happycoders.sort;
2+
3+
import eu.happycoders.sort.method.DualPivotQuicksort;
4+
import eu.happycoders.sort.method.*;
5+
import eu.happycoders.sort.utils.*;
6+
7+
import java.util.*;
8+
import java.util.function.Function;
9+
10+
/**
11+
* Measures the performance of all sorting algorithms for various input sizes.
12+
*
13+
* @author <a href="[email protected]">Sven Woltmann</a>
14+
*/
15+
public class UltimateTest {
16+
17+
static final SortAlgorithm[] ALGORITHMS = {
18+
new InsertionSort(),
19+
new SelectionSort(),
20+
new BubbleSort(),
21+
new Quicksort(Quicksort.PivotStrategy.MIDDLE),
22+
new QuicksortImproved(48, Quicksort.PivotStrategy.MIDDLE),
23+
new DualPivotQuicksort(DualPivotQuicksort.PivotStrategy.MIDDLES),
24+
new DualPivotQuicksortImproved(48,
25+
DualPivotQuicksort.PivotStrategy.MIDDLES),
26+
new MergeSort(),
27+
new HeapSort(),
28+
new CountingSort(),
29+
new JavaArraysSort()
30+
};
31+
32+
private static final int WARM_UPS = 2;
33+
34+
private static final int MIN_SORTING_SIZE = 1 << 10;
35+
private static final int MAX_SORTING_SIZE = 1 << 29;
36+
37+
// Stop when sorting takes longer than 20 seconds
38+
private static final int MAX_SORTING_TIME_SECS = 20;
39+
40+
private static final boolean TEST_SORTED_INPUT = true;
41+
42+
private static final Map<String, Scorecard> scorecards = new HashMap<>();
43+
44+
public static void main(String[] args) {
45+
for (int i = 1; i <= WARM_UPS; i++) {
46+
System.out.printf("%n===== Warm up %d of %d =====%n", i, WARM_UPS);
47+
for (SortAlgorithm algorithm : ALGORITHMS) {
48+
test(algorithm, true);
49+
}
50+
}
51+
52+
for (int i = 1; ; i++) {
53+
System.out.printf("%n===== Iteration %d =====%n", i);
54+
for (SortAlgorithm algorithm : ALGORITHMS) {
55+
test(algorithm, false);
56+
}
57+
System.out.printf("%n===== Results for iteration %d =====%n", i);
58+
for (SortAlgorithm algorithm : ALGORITHMS) {
59+
printResults(i, algorithm, "random");
60+
if (TEST_SORTED_INPUT) {
61+
printResults(i, algorithm, "ascending");
62+
printResults(i, algorithm, "descending");
63+
}
64+
}
65+
}
66+
}
67+
68+
private static void test(SortAlgorithm algorithm, boolean warmingUp) {
69+
// Test with a random, a sorted, and a reversed (= sorted descending) array
70+
test(algorithm, "random", ArrayUtils::createRandomArray, warmingUp);
71+
72+
// Quicksort would go n x into recursion here...
73+
if (TEST_SORTED_INPUT && algorithm.isSuitableForSortedInput()) {
74+
test(algorithm, "ascending", ArrayUtils::createSortedArray, warmingUp);
75+
test(algorithm, "descending", ArrayUtils::createReversedArray,
76+
warmingUp);
77+
}
78+
}
79+
80+
private static void test(SortAlgorithm algorithm,
81+
String inputOrder,
82+
Function<Integer, int[]> arraySupplier,
83+
boolean warmingUp) {
84+
System.out.printf("%n--- %s (order: %s) ---%n",
85+
algorithm.getName(), inputOrder);
86+
87+
// Sort until sorting takes more than MAX_SORTING_TIME_SECS
88+
// Upper limit used by insertion sort on already sorted data
89+
for (int size = MIN_SORTING_SIZE;
90+
size <= MAX_SORTING_SIZE && algorithm.isSuitableForInputSize(size);
91+
size <<= 1) {
92+
long time = measureTime(algorithm, arraySupplier.apply(size));
93+
boolean newRecord = !warmingUp
94+
&& scorecard(algorithm, inputOrder, size, true).add(time);
95+
96+
System.out.printf(Locale.US,
97+
"%s (order: %s): size = %,11d --> time = %,10.3f ms %s%n",
98+
algorithm.getName(),
99+
inputOrder,
100+
size,
101+
time / 1_000_000.0,
102+
newRecord ? "<<< NEW RECORD :-)" : "");
103+
104+
// Stop after specified time
105+
if (time > MAX_SORTING_TIME_SECS * 1_000_000_000L) {
106+
break;
107+
}
108+
}
109+
}
110+
111+
private static long measureTime(SortAlgorithm algorithm, int[] elements) {
112+
System.gc();
113+
long time = System.nanoTime();
114+
algorithm.sort(elements);
115+
return System.nanoTime() - time;
116+
}
117+
118+
private static Scorecard scorecard(SortAlgorithm algorithm,
119+
String inputOrder, int size,
120+
boolean create) {
121+
String key = algorithm.getName() + "/" + inputOrder + "/" + size;
122+
return create
123+
? scorecards.computeIfAbsent(key, Scorecard::new)
124+
: scorecards.get(key);
125+
}
126+
127+
private static void printResults(int iteration, SortAlgorithm algorithm,
128+
String inputOrder) {
129+
System.out.printf("%n--- Results for iteration %d for: %s (order: %s) ---%n",
130+
iteration, algorithm.getName(), inputOrder);
131+
132+
int longestNameLength = 0;
133+
for (int size = MIN_SORTING_SIZE;
134+
size <= MAX_SORTING_SIZE && algorithm.isSuitableForInputSize(size);
135+
size <<= 1) {
136+
Scorecard scorecard = scorecard(algorithm, inputOrder, size, false);
137+
if (scorecard != null) {
138+
int nameLength = scorecard.getName().length();
139+
if (nameLength > longestNameLength) {
140+
longestNameLength = nameLength;
141+
}
142+
}
143+
}
144+
145+
for (int size = MIN_SORTING_SIZE;
146+
size <= MAX_SORTING_SIZE && algorithm.isSuitableForInputSize(size);
147+
size <<= 1) {
148+
Scorecard scorecard = scorecard(algorithm, inputOrder, size, false);
149+
if (scorecard != null) {
150+
scorecard.printResult(longestNameLength, "");
151+
}
152+
}
153+
}
154+
155+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package eu.happycoders.sort.comparisons;
2+
3+
import eu.happycoders.sort.method.DualPivotQuicksort;
4+
import eu.happycoders.sort.method.*;
5+
6+
import java.util.*;
7+
8+
/**
9+
* Compares Dual-Pivot Quicksort with the improved version for various
10+
* thresholds at which the algorithm switches from Quicksort to Insertion Sort.
11+
*
12+
* @author <a href="[email protected]">Sven Woltmann</a>
13+
*/
14+
public class CompareImprovedDualPivotQuickSort extends DirectComparison {
15+
16+
private static final int SIZE = 5_555_555; // ~500 ms for Quicksort
17+
18+
public static void main(String[] args) {
19+
List<SortAlgorithm> algorithms = new ArrayList<>();
20+
algorithms.add(new DualPivotQuicksort(DualPivotQuicksort.PivotStrategy.MIDDLES));
21+
for (int i = 2; i < 1 << 8; i <<= 1) {
22+
algorithms.add(new DualPivotQuicksortImproved(i,
23+
DualPivotQuicksort.PivotStrategy.MIDDLES));
24+
25+
// From 16 elements on, add i+i/2 (-> 16, 24, 32, 48, 64, 96, 128, ...)
26+
if (i >= 16) {
27+
int i2 = i + i / 2;
28+
algorithms.add(new DualPivotQuicksortImproved(i2,
29+
DualPivotQuicksort.PivotStrategy.MIDDLES));
30+
}
31+
}
32+
runTest(algorithms.toArray(SortAlgorithm[]::new), SIZE);
33+
}
34+
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package eu.happycoders.sort.comparisons;
2+
3+
import eu.happycoders.sort.method.*;
4+
import eu.happycoders.sort.method.Quicksort.PivotStrategy;
5+
6+
import java.util.*;
7+
8+
/**
9+
* Compares the regular Quicksort with the improved Quicksort for various
10+
* thresholds at which the algorithm switches from Quicksort to Insertion Sort.
11+
*
12+
* @author <a href="[email protected]">Sven Woltmann</a>
13+
*/
14+
public class CompareImprovedQuickSort extends DirectComparison {
15+
16+
private static final int SIZE = 5_555_555; // ~500 ms for Quicksort
17+
18+
public static void main(String[] args) {
19+
List<SortAlgorithm> algorithms = new ArrayList<>();
20+
algorithms.add(new Quicksort(PivotStrategy.MIDDLE));
21+
algorithms.add(new Quicksort(PivotStrategy.MEDIAN3));
22+
23+
for (int i = 2; i < 1 << 8; i <<= 1) {
24+
algorithms.add(new QuicksortImproved(i, PivotStrategy.MIDDLE));
25+
algorithms.add(new QuicksortImproved(i, PivotStrategy.MEDIAN3));
26+
27+
// From 16 elements on, add i+i/2 (-> 16, 24, 32, 48, 64, 96, 128, ...)
28+
if (i >= 16) {
29+
int i2 = i + i / 2;
30+
algorithms.add(new QuicksortImproved(i2, PivotStrategy.MIDDLE));
31+
algorithms.add(new QuicksortImproved(i2, PivotStrategy.MEDIAN3));
32+
}
33+
}
34+
35+
runTest(algorithms.toArray(SortAlgorithm[]::new), SIZE);
36+
}
37+
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package eu.happycoders.sort.comparisons;
2+
3+
import eu.happycoders.sort.method.*;
4+
5+
/**
6+
* Compares the three merge sort algorithms.
7+
*
8+
* @author <a href="[email protected]">Sven Woltmann</a>
9+
*/
10+
public class CompareMergeSorts extends DirectComparison {
11+
12+
private static final int SIZE = 5_555_555; // ~840 ms for Merge Sort
13+
14+
public static void main(String[] args) {
15+
SortAlgorithm[] algorithms = new SortAlgorithm[]{
16+
new MergeSort(),
17+
new MergeSort2(),
18+
new MergeSort3()
19+
};
20+
runTest(algorithms, SIZE);
21+
}
22+
23+
}

0 commit comments

Comments
 (0)