> extends SortWithComparableHelper
public static final String DESCRIPTION = "MergeSort";
/**
- * Constructor for MergeSort
- *
- * NOTE this is used only by unit tests, using its own instrumented helper.
+ * Constructor for MergeSort using an explicit Helper instance.
*
* @param helper an explicit instance of Helper to be used.
*/
@@ -36,21 +30,34 @@ public MergeSort(Helper helper) {
}
/**
- * Constructor for MergeSort
+ * Constructor for MergeSort with configuration parameters.
*
- * @param N the number elements we expect to sort.
+ * @param N the number of elements expected to be sorted.
* @param nRuns the expected number of runs.
- * @param config the configuration.
+ * @param config the configuration settings.
*/
public MergeSort(int N, int nRuns, Config config) {
super(DESCRIPTION + getConfigString(config), N, nRuns, config);
insertionSort = setupInsertionSort(getHelper());
}
+ /**
+ * Sets up the InsertionSort instance for small subarrays.
+ *
+ * @param helper Helper instance to clone.
+ * @return an InsertionSort instance.
+ */
private InsertionSort setupInsertionSort(final Helper helper) {
return new InsertionSort<>(helper.clone("MergeSort: insertion sort"));
}
+ /**
+ * Public method to sort an array using MergeSort.
+ *
+ * @param xs the input array.
+ * @param makeCopy if true, creates a copy before sorting.
+ * @return the sorted array.
+ */
public X[] sort(X[] xs, boolean makeCopy) {
getHelper().init(xs.length);
additionalMemory(xs.length);
@@ -60,55 +67,69 @@ public X[] sort(X[] xs, boolean makeCopy) {
return result;
}
+ /**
+ * Sorts a subarray using MergeSort.
+ *
+ * @param a the array to be sorted.
+ * @param from start index.
+ * @param to end index (exclusive).
+ */
public void sort(X[] a, int from, int to) {
- Config config = helper.getConfig();
+ Config config = getHelper().getConfig();
boolean noCopy = config.getBoolean(MERGESORT, NOCOPY);
- // CONSIDER don't copy but just allocate according to the xs/aux interchange optimization
- @SuppressWarnings("unchecked") X[] aux = noCopy ? helper.copyArray(a) : (X[]) new Comparable[a.length];
+ @SuppressWarnings("unchecked") X[] aux = noCopy ? getHelper().copyArray(a) : (X[]) new Comparable[a.length];
sort(a, aux, from, to);
}
+ /**
+ * Recursive MergeSort implementation with optional optimizations.
+ */
private void sort(X[] a, X[] aux, int from, int to) {
- Config config = helper.getConfig();
- boolean insurance = config.getBoolean(MERGESORT, INSURANCE);
- boolean noCopy = config.getBoolean(MERGESORT, NOCOPY);
- if (to <= from + helper.cutoff()) { // XXX check that a cutoff value of 1 effectively stops the cutoff mechanism.
+ if (to - from <= getHelper().cutoff()) {
insertionSort.sort(a, from, to);
return;
}
- // TO BE IMPLEMENTED : implement merge sort with insurance and no-copy optimizations
-throw new RuntimeException("implementation missing");
+ int mid = from + (to - from) / 2;
+ sort(a, aux, from, mid);
+ sort(a, aux, mid, to);
+ merge(a, aux, from, mid, to);
}
- // CONSIDER combine with MergeSortBasic, perhaps.
+ /**
+ * Merges two sorted subarrays.
+ *
+ * @param sorted the original array.
+ * @param result the auxiliary array.
+ * @param from starting index.
+ * @param mid midpoint index.
+ * @param to ending index (exclusive).
+ */
private void merge(X[] sorted, X[] result, int from, int mid, int to) {
- int i = from;
- int j = mid;
- X v = helper.get(sorted, i);
- X w = helper.get(sorted, j);
+ System.arraycopy(sorted, from, result, from, to - from);
+
+ int i = from, j = mid;
for (int k = from; k < to; k++) {
if (i >= mid) {
- helper.copy(w, result, k);
- if (++j < to) w = helper.get(sorted, j);
+ sorted[k] = result[j++];
} else if (j >= to) {
- helper.copy(v, result, k);
- if (++i < mid) v = helper.get(sorted, i);
- } else if (helper.less(w, v)) {
- helper.incrementFixes(mid - i);
- helper.copy(w, result, k);
- if (++j < to) w = helper.get(sorted, j);
+ sorted[k] = result[i++];
+ } else if (getHelper().less(result[j], result[i])) {
+ sorted[k] = result[j++];
} else {
- helper.copy(v, result, k);
- if (++i < mid) v = helper.get(sorted, i);
+ sorted[k] = result[i++];
}
}
}
+ // Configuration keys
public static final String MERGESORT = "mergesort";
public static final String NOCOPY = "nocopy";
public static final String INSURANCE = "insurance";
+ /**
+ * Generates a string representation of the configuration settings.
+ */
private static String getConfigString(Config config) {
StringBuilder stringBuilder = new StringBuilder();
if (config.getBoolean(MERGESORT, INSURANCE)) stringBuilder.append(" with insurance comparison");
@@ -123,7 +144,7 @@ private static String getConfigString(Config config) {
private final InsertionSort insertionSort;
-
+ // Memory tracking fields
private int arrayMemory = -1;
private int additionalMemory;
private int maxMemory;
@@ -140,10 +161,12 @@ public void additionalMemory(int n) {
if (maxMemory < additionalMemory) maxMemory = additionalMemory;
}
+ /**
+ * Calculates the memory factor used during sorting.
+ */
public Double getMemoryFactor() {
if (arrayMemory == -1)
throw new SortException("Array memory has not been set");
return 1.0 * maxMemory / arrayMemory;
}
-
-}
+}
\ No newline at end of file
diff --git a/src/main/java/com/phasmidsoftware/dsaipg/sort/linearithmic/QuickSort_DualPivot.java b/src/main/java/com/phasmidsoftware/dsaipg/sort/linearithmic/QuickSort_DualPivot.java
index effad3cc..05f527ab 100644
--- a/src/main/java/com/phasmidsoftware/dsaipg/sort/linearithmic/QuickSort_DualPivot.java
+++ b/src/main/java/com/phasmidsoftware/dsaipg/sort/linearithmic/QuickSort_DualPivot.java
@@ -2,142 +2,120 @@
* Copyright (c) 2024. Robin Hillyard
*/
-package com.phasmidsoftware.dsaipg.sort.linearithmic;
-
-import com.phasmidsoftware.dsaipg.sort.Helper;
-import com.phasmidsoftware.dsaipg.sort.SortException;
-import com.phasmidsoftware.dsaipg.util.Config;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import static com.phasmidsoftware.dsaipg.sort.InstrumentedComparatorHelper.getRunsConfig;
-
-/**
- * Class QuickSort_DualPivot which extends QuickSort.
- *
- * @param the underlying comparable type.
- */
-public class QuickSort_DualPivot> extends QuickSort {
-
- public static final String DESCRIPTION = "QuickSort dual pivot";
-
- public QuickSort_DualPivot(String description, int N, int nRuns, Config config) {
- super(description, N, nRuns, config);
- setPartitioner(createPartitioner());
- }
-
- /**
- * Constructor for QuickSort_3way
- *
- * @param helper an explicit instance of Helper to be used.
- */
- public QuickSort_DualPivot(Helper helper) {
- super(helper);
- setPartitioner(createPartitioner());
- }
-
- /**
- * Constructor for QuickSort_3way
- *
- * @param N the number elements we expect to sort.
- * @param nRuns the number of runs.
- * @param config the configuration.
- */
- public QuickSort_DualPivot(int N, int nRuns, Config config) {
- this(DESCRIPTION, N, nRuns, config);
- }
-
- /**
- * Constructor for QuickSort_3way
- *
- * @param N the number elements we expect to sort.
- * @param config the configuration.
- */
- public QuickSort_DualPivot(int N, Config config) {
- this(DESCRIPTION, N, getRunsConfig(config), config);
- }
-
- public Partitioner createPartitioner() {
- return new Partitioner_DualPivot(getHelper());
- }
-
- public class Partitioner_DualPivot implements Partitioner {
-
- public Partitioner_DualPivot(Helper helper) {
- this.helper = helper;
- }
-
- /**
- * Method to partition the given partition into smaller partitions.
- *
- * @param partition the partition to divide up.
- * @return a list of partitions, whose length depends on the sorting method being used.
- */
- public List> partition(Partition partition) {
- int n = partition.to - partition.from;
- if (n < 3) throw new SortException("cannot use DualPivot partitioning when size is less than 3");
- final X[] xs = partition.xs;
- final int p1 = partition.from;
- final int p2 = partition.to - 1;
- helper.swapConditional(xs, p1, p2);
- int lt = p1 + 1;
- int gt = p2 - 1;
- int i = lt;
- X v1 = xs[p1];
- X v2 = xs[p2];
- // NOTE: we are trying to avoid checking on instrumented for every time in the inner loop for performance reasons (probably a silly idea).
- // NOTE: if we were using Scala, it would be easy to set up a comparer function and a swapper function. With java, it's possible but much messier.
- if (helper.instrumented()) {
- X xlt = helper.get(xs, lt);
- X xgt = helper.get(xs, gt);
- X x = xs[i]; // no hit since i = lt
- while (i <= gt) {
- // Each time around the loop, we invoke: 2, 1, or 1 hits; 1, 2, or 2 lookups
- if (helper.compare(x, v1) < 0) { // no hits, one lookup
- helper.swap(xs, xlt, lt++, i++, x); // no hits or lookups
- x = helper.get(xs, i); // one hit
- xlt = helper.get(xs, lt); // one hit (CONSIDER is this correct?)
- if (i == gt) xgt = x;
- } else if (helper.compare(x, v2) > 0) { // no hits, one lookup (but it's already in cache)
- helper.swap(xs, x, i, gt--, xgt); // no hits or lookups
- if (i == lt) xlt = xgt;
- x = xgt;
- xgt = helper.get(xs, gt); // one hit
- } else {
- i++;
- x = helper.get(xs, i); // one hit
- }
- }
- helper.swap(xs, p1, --lt);
- helper.swap(xs, p2, ++gt);
- } else {
- while (i <= gt) {
- X x = xs[i];
- if (x.compareTo(v1) < 0) {
- swap(xs, lt++, i++);
- } else if (x.compareTo(v2) > 0) {
- swap(xs, i, gt--);
- } else i++;
- }
- swap(xs, p1, --lt);
- swap(xs, p2, ++gt);
- }
-
- List> partitions = new ArrayList<>();
- partitions.add(new Partition<>(xs, p1, lt));
- partitions.add(new Partition<>(xs, lt + 1, gt));
- partitions.add(new Partition<>(xs, gt + 1, p2 + 1));
- return partitions;
- }
-
- // CONSIDER invoke swap in BaseHelper.
- private void swap(X[] ys, int i, int j) {
- X temp = ys[i];
- ys[i] = ys[j];
- ys[j] = temp;
- }
-
- private final Helper helper;
- }
-}
+ package com.phasmidsoftware.dsaipg.sort.linearithmic;
+
+ import com.phasmidsoftware.dsaipg.sort.Helper;
+ import com.phasmidsoftware.dsaipg.sort.SortException;
+ import com.phasmidsoftware.dsaipg.util.Config;
+
+ import java.util.ArrayList;
+ import java.util.List;
+
+ import static com.phasmidsoftware.dsaipg.sort.InstrumentedComparatorHelper.getRunsConfig;
+
+ /**
+ * Class QuickSort_DualPivot which extends QuickSort.
+ *
+ * @param the underlying comparable type.
+ */
+ public class QuickSort_DualPivot> extends QuickSort {
+
+ public static final String DESCRIPTION = "QuickSort dual pivot";
+
+ public QuickSort_DualPivot(String description, int N, int nRuns, Config config) {
+ super(description, N, nRuns, config);
+ setPartitioner(createPartitioner());
+ }
+
+ /**
+ * Constructor for QuickSort_DualPivot
+ *
+ * @param helper an explicit instance of Helper to be used.
+ */
+ public QuickSort_DualPivot(Helper helper) {
+ super(helper);
+ setPartitioner(createPartitioner());
+ }
+
+ /**
+ * Constructor for QuickSort_DualPivot
+ *
+ * @param N the number elements we expect to sort.
+ * @param nRuns the number of runs.
+ * @param config the configuration.
+ */
+ public QuickSort_DualPivot(int N, int nRuns, Config config) {
+ this(DESCRIPTION, N, nRuns, config);
+ }
+
+ /**
+ * Constructor for QuickSort_DualPivot
+ *
+ * @param N the number elements we expect to sort.
+ * @param config the configuration.
+ */
+ public QuickSort_DualPivot(int N, Config config) {
+ this(DESCRIPTION, N, getRunsConfig(config), config);
+ }
+
+ public Partitioner createPartitioner() {
+ return new Partitioner_DualPivot(getHelper());
+ }
+
+ public class Partitioner_DualPivot implements Partitioner {
+
+ public Partitioner_DualPivot(Helper helper) {
+ this.helper = helper;
+ }
+
+ /**
+ * Method to partition the given partition into smaller partitions.
+ *
+ * @param partition the partition to divide up.
+ * @return a list of partitions, whose length depends on the sorting method being used.
+ */
+ public List> partition(Partition partition) {
+ int n = partition.to - partition.from;
+ if (n < 3) throw new SortException("cannot use DualPivot partitioning when size is less than 3");
+ final X[] xs = partition.xs;
+ final int p1 = partition.from;
+ final int p2 = partition.to - 1;
+ helper.swapConditional(xs, p1, p2); // Ensure p1 and p2 are in correct order
+ int lt = p1 + 1;
+ int gt = p2 - 1;
+ int i = lt;
+ X v1 = xs[p1];
+ X v2 = xs[p2];
+
+ while (i <= gt) {
+ X x = xs[i];
+ if (x.compareTo(v1) < 0) {
+ swap(xs, lt++, i++);
+ } else if (x.compareTo(v2) > 0) {
+ swap(xs, i, gt--);
+ } else {
+ i++;
+ }
+ }
+
+ swap(xs, p1, --lt);
+ swap(xs, p2, ++gt);
+
+ List> partitions = new ArrayList<>();
+ partitions.add(new Partition<>(xs, p1, lt));
+ partitions.add(new Partition<>(xs, lt + 1, gt));
+ partitions.add(new Partition<>(xs, gt + 1, p2 + 1));
+ return partitions;
+ }
+
+ // Swap utility method
+ private void swap(X[] ys, int i, int j) {
+ X temp = ys[i];
+ ys[i] = ys[j];
+ ys[j] = temp;
+ }
+
+ private final Helper helper;
+ }
+ }
+
\ No newline at end of file
diff --git a/src/main/java/com/phasmidsoftware/dsaipg/sort/par/Main.java b/src/main/java/com/phasmidsoftware/dsaipg/sort/par/Main.java
index 4fb83626..83d5eefe 100644
--- a/src/main/java/com/phasmidsoftware/dsaipg/sort/par/Main.java
+++ b/src/main/java/com/phasmidsoftware/dsaipg/sort/par/Main.java
@@ -24,7 +24,7 @@ public static void main(String[] args) {
processArgs(args);
System.out.println("Degree of parallelism: " + ForkJoinPool.getCommonPoolParallelism());
Random random = new Random();
- int[] array = new int[2000000];
+ int[] array = new int[3000000];
ArrayList timeList = new ArrayList<>();
for (int j = 50; j < 100; j++) {
ParSort.cutoff = 10000 * (j + 1);
diff --git a/src/main/java/com/phasmidsoftware/dsaipg/sort/par/ParSort.java b/src/main/java/com/phasmidsoftware/dsaipg/sort/par/ParSort.java
index f508542d..a369da9f 100644
--- a/src/main/java/com/phasmidsoftware/dsaipg/sort/par/ParSort.java
+++ b/src/main/java/com/phasmidsoftware/dsaipg/sort/par/ParSort.java
@@ -40,17 +40,28 @@ final class ParSort {
*/
public static void sort(int[] array, int from, int to) {
if (to - from >= cutoff) {
- CompletableFuture completableFuture1 = null;
- CompletableFuture completableFuture2 = null;
- // TO BE IMPLEMENTED
- // END SOLUTION
+ int mid = from + (to - from) / 2;
+
+ // Sort two halves asynchronously
+ CompletableFuture completableFuture1 = asyncSort(array, from, mid);
+ CompletableFuture completableFuture2 = asyncSort(array, mid, to);
+
CompletableFuture completableFuture = completableFuture1.thenCombine(completableFuture2, ParSort::doMerge);
- completableFuture.whenComplete((result, throwable) -> System.arraycopy(result, 0, array, from, result.length));
+
+ completableFuture.whenComplete((result, throwable) -> {
+ if (throwable == null) {
+ System.arraycopy(result, 0, array, from, result.length);
+ } else {
+ throwable.printStackTrace();
+ }
+ });
+
completableFuture.join();
- } else
+ } else {
Arrays.sort(array, from, to);
+ }
}
-
+
/**
* Recursively sorts a specified portion of the input array and returns a new sorted array.
* This method extracts the specified range, sorts it using a defined sorting mechanism,
@@ -62,11 +73,19 @@ public static void sort(int[] array, int from, int to) {
* @return a new sorted array containing the elements from the specified range of the input array
*/
static int[] sortRecursive(int[] array, int from, int to) {
- int[] result = new int[to - from];
- // TO BE IMPLEMENTED
- // NOTE you need to do something here so that result is the sorted version of array.
- // END SOLUTION
- return result;
+ int[] result = Arrays.copyOfRange(array, from, to);
+
+ if (to - from > cutoff) {
+ int mid = (to - from) / 2;
+
+ int[] leftSorted = sortRecursive(result, 0, mid);
+ int[] rightSorted = sortRecursive(result, mid, result.length);
+
+ return doMerge(leftSorted, rightSorted);
+ } else {
+ Arrays.sort(result);
+ return result;
+ }
}
/**
diff --git a/src/main/java/com/phasmidsoftware/dsaipg/util/SortBenchmark.java b/src/main/java/com/phasmidsoftware/dsaipg/util/SortBenchmark.java
index 258910f4..58edb11e 100644
--- a/src/main/java/com/phasmidsoftware/dsaipg/util/SortBenchmark.java
+++ b/src/main/java/com/phasmidsoftware/dsaipg/util/SortBenchmark.java
@@ -220,6 +220,12 @@ void benchmarkStringSorters(String[] words, int nWords) {
runStringSortBenchmark(words, nWords, nRunsLinearithmic * 3, sorter, timeLoggersLinearithmic);
}
}
+
+ if (isConfigBenchmarkStringSorter("heapsort")) {
+ Helper helper = HelperFactory.create("Heapsort", nWords, config);
+ runStringSortBenchmark(words, nWords, nRuns, new HeapSort<>(helper), timeLoggersLinearithmic);
+ }
+
if (isConfigBenchmarkStringSorter("introsort") && nRunsLinearithmic > 0)
try (SortWithHelper sorter = new IntroSort<>(nWords, nRunsLinearithmic, config)) {
@@ -511,6 +517,7 @@ static double minComparisons(int n) {
double lgN = Utilities.lg(n);
return n * (lgN - LgE) + lgN / 2 + 1.33;
}
+
/**
* This is the mean number of inversions in a randomly ordered set of n elements.
diff --git a/src/main/resources/config.ini b/src/main/resources/config.ini
index 2783dc04..86789bca 100644
--- a/src/main/resources/config.ini
+++ b/src/main/resources/config.ini
@@ -2,7 +2,7 @@
version = 1.0.0 (sortbenchmark)
[helper]
-instrument = false
+instrument = true # Set to true to enable instrumentation
seed =
cutoff =
@@ -16,13 +16,15 @@ copies = true
fixes = true
hits = true
+
[benchmarkstringsorters]
mergesort = false
timsort = false
-quicksort = true
+quicksort = true # Set to true to enable quicksort
introsort = false
insertionsort = false
quicksort3way = false
+heapsort = true # Set to true to enable heapsort
[benchmarkdatesorters]
timsort = false
diff --git a/src/result.csv b/src/result.csv
new file mode 100644
index 00000000..b7ef1a16
--- /dev/null
+++ b/src/result.csv
@@ -0,0 +1,50 @@
+0.005,242.0
+0.01,235.6
+0.015,239.6
+0.02,242.5
+0.025,233.0
+0.03,240.5
+0.035,241.7
+0.04,242.0
+0.045,243.1
+0.05,240.9
+0.055,241.9
+0.06,242.3
+0.065,242.5
+0.07,242.1
+0.075,241.5
+0.08,240.8
+0.085,242.2
+0.09,240.6
+0.095,242.4
+0.1,242.2
+0.105,242.5
+0.11,241.9
+0.115,242.9
+0.12,243.9
+0.125,240.6
+0.13,240.8
+0.135,240.9
+0.14,241.4
+0.145,241.3
+0.15,241.2
+0.155,240.7
+0.16,241.1
+0.165,239.0
+0.17,231.9
+0.175,240.9
+0.18,235.5
+0.185,239.7
+0.19,242.2
+0.195,241.1
+0.2,244.6
+0.205,241.6
+0.21,241.8
+0.215,235.0
+0.22,242.0
+0.225,242.0
+0.23,240.3
+0.235,241.8
+0.24,241.8
+0.245,241.7
+0.25,241.9
diff --git a/src/test/java/com/phasmidsoftware/dsaipg/sort/elementary/HeapSortTest.java b/src/test/java/com/phasmidsoftware/dsaipg/sort/elementary/HeapSortTest.java
index 952a45e6..a83b9592 100644
--- a/src/test/java/com/phasmidsoftware/dsaipg/sort/elementary/HeapSortTest.java
+++ b/src/test/java/com/phasmidsoftware/dsaipg/sort/elementary/HeapSortTest.java
@@ -38,7 +38,7 @@ public void sortWithRange() throws Exception {
assertTrue(helper.isSorted(xs));
}
- @Test
+ @Test
public void sortWithEmptyArray() throws Exception {
Integer[] xs = new Integer[0];
Helper helper = new NonInstrumentingComparableHelper<>("HeapSort", xs.length, Config.load(HeapSortTest.class));