From f8e2a5d5f5e09c545bb21b181755538b854943e3 Mon Sep 17 00:00:00 2001 From: gaurav-northeastern Date: Fri, 31 Jan 2025 17:03:59 -0500 Subject: [PATCH 1/3] Pushing 1st assignment changes --- .vscode/launch.json | 296 ++++++++++++++++++ .vscode/settings.json | 3 + pom.xml | 32 +- .../dsaipg/misc/randomwalk/RandomWalk.java | 12 +- 4 files changed, 324 insertions(+), 19 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..ecbcd567 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,296 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "type": "java", + "name": "Current File", + "request": "launch", + "mainClass": "${file}" + }, + { + "type": "java", + "name": "MySet", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.adt.bqs.MySet", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "ShuntingYard", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.adt.bqs.ShuntingYard", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "PriorityQueue", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.adt.pq.PriorityQueue", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "BSTBenchmark", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.adt.symbolTable.tree.BSTBenchmark", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "ThreeSumBenchmark", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.adt.threesum.ThreeSumBenchmark", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "TwoSumBenchmark", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.adt.threesum.TwoSumBenchmark", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "HuffmanCoding", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.compression.HuffmanCoding", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "CoinChange", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.graphs.dynamicProgramming.coins.CoinChange", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "Tunnels_Boruvka", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.graphs.tunnels.Tunnels_Boruvka", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "Tunnels_Kruskal", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.graphs.tunnels.Tunnels_Kruskal", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "Tunnels_Prim", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.graphs.tunnels.Tunnels_Prim", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "BinarySearch", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.misc.BinarySearch", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "CallByValue", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.misc.CallByValue", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "Counter", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.misc.Counter", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "Mystery", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.misc.Mystery", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "NewtonApproximation", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.misc.NewtonApproximation", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "RecursionAndIteration", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.misc.RecursionAndIteration", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "CodeLength", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.misc.codelength.CodeLength", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "CouplingNone", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.misc.coupling.CouplingNone", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "CouplingTight", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.misc.coupling.CouplingTight", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "Newton", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.misc.functions.Newton", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "RandomWalk", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.misc.randomwalk.RandomWalk", + "projectName": "INFO6205", + "args": "500 10" + }, + { + "type": "java", + "name": "RunLength", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.misc.runLengthEncoding.RunLength", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "Game", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.projects.life.base.Game", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "MCTS", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.projects.mcts.tictactoe.MCTS", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "TicTacToe", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.projects.mcts.tictactoe.TicTacToe", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "SelectBenchmark", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.select.SelectBenchmark", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "BubbleSort", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.sort.elementary.BubbleSort", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "ShellSort", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.sort.elementary.ShellSort", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "Solution", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.sort.elementary.Solution", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "MergeSortDecisionTree", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.sort.linearithmic.MergeSortDecisionTree", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "Main", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.sort.par.Main", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "FastInverseSquareRoot", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.util.FastInverseSquareRoot", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "Iteration", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.util.Iteration", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "Matrix", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.util.Matrix", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "OperationsBenchmark", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.util.OperationsBenchmark", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "PQBenchmark", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.util.PQBenchmark", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "SortBenchmark", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.util.SortBenchmark", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "SymbolTableBenchmark", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.util.SymbolTableBenchmark", + "projectName": "INFO6205" + }, + { + "type": "java", + "name": "VLA2", + "request": "launch", + "mainClass": "com.phasmidsoftware.dsaipg.util.VLA2", + "projectName": "INFO6205" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..385f27a2 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "interactive" +} diff --git a/pom.xml b/pom.xml index c2576b09..571aa452 100644 --- a/pom.xml +++ b/pom.xml @@ -1,9 +1,9 @@ +~ Copyright (c) 2018, 2019, 2020. Phasmid Software +- +- NOTE: this POM file is designed to include both unit and integration tests. +- The mechanism has not been entirely tested. +--> 4.0.0 @@ -15,16 +15,17 @@ org.apache.maven.plugins maven-compiler-plugin + 3.8.1 - 17 - 18 + 11 + 11 - 1.17 - 1.18 + 17 + 17 UTF-8 UTF-8 @@ -45,7 +46,7 @@ number_2.13 1.0.12 - + org.apache.logging.log4j log4j-api 2.19.0 @@ -98,10 +99,11 @@ compile + edu.neu.coe.mgen + INFO6205 + 1 + + --> + \ No newline at end of file diff --git a/src/main/java/com/phasmidsoftware/dsaipg/misc/randomwalk/RandomWalk.java b/src/main/java/com/phasmidsoftware/dsaipg/misc/randomwalk/RandomWalk.java index 72781604..ced89403 100644 --- a/src/main/java/com/phasmidsoftware/dsaipg/misc/randomwalk/RandomWalk.java +++ b/src/main/java/com/phasmidsoftware/dsaipg/misc/randomwalk/RandomWalk.java @@ -21,7 +21,7 @@ public class RandomWalk { */ public double distance() { // TO BE IMPLEMENTED - return 0.0; + return Math.sqrt(x * x + y * y); // END SOLUTION } @@ -33,7 +33,8 @@ public double distance() { */ private void move(int dx, int dy) { // TO BE IMPLEMENTED do move - throw new RuntimeException("Not implemented"); + x += dx; + y += dy; // END SOLUTION } @@ -43,8 +44,11 @@ private void move(int dx, int dy) { * @param m the number of steps the drunkard takes */ private void randomWalk(int m) { - // TO BE IMPLEMENTED -throw new RuntimeException("implementation missing"); + + for (int i = 0; i < m; i++) { + randomMove(); + } + } /** From 6e7498f82ec315267a686699a6ef0958d7179849 Mon Sep 17 00:00:00 2001 From: gaurav-northeastern Date: Tue, 18 Mar 2025 00:13:10 -0400 Subject: [PATCH 2/3] parSort done --- .../phasmidsoftware/dsaipg/sort/par/Main.java | 2 +- .../dsaipg/sort/par/ParSort.java | 43 +++++++++++----- src/result.csv | 50 +++++++++++++++++++ 3 files changed, 82 insertions(+), 13 deletions(-) create mode 100644 src/result.csv 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/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 From a4f09a9156c04c5ce6374f1452cda79474c59e4c Mon Sep 17 00:00:00 2001 From: gaurav-northeastern Date: Sat, 29 Mar 2025 20:46:54 -0400 Subject: [PATCH 3/3] sorting algo implemented --- .vscode/settings.json | 4 +- .../dsaipg/sort/elementary/HeapSort.java | 30 +- .../elementary/InsertionSortComparator.java | 49 ++-- .../dsaipg/sort/linearithmic/MergeSort.java | 99 ++++--- .../linearithmic/QuickSort_DualPivot.java | 256 ++++++++---------- .../dsaipg/util/SortBenchmark.java | 7 + src/main/resources/config.ini | 6 +- .../dsaipg/sort/elementary/HeapSortTest.java | 2 +- 8 files changed, 237 insertions(+), 216 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 385f27a2..350cd127 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,5 @@ { - "java.configuration.updateBuildConfiguration": "interactive" + "java.configuration.updateBuildConfiguration": "interactive", + "java.compile.nullAnalysis.mode": "automatic", + "java.debug.settings.onBuildFailureProceed": true } diff --git a/src/main/java/com/phasmidsoftware/dsaipg/sort/elementary/HeapSort.java b/src/main/java/com/phasmidsoftware/dsaipg/sort/elementary/HeapSort.java index 02840921..a310f286 100644 --- a/src/main/java/com/phasmidsoftware/dsaipg/sort/elementary/HeapSort.java +++ b/src/main/java/com/phasmidsoftware/dsaipg/sort/elementary/HeapSort.java @@ -1,7 +1,3 @@ -/* - * Copyright (c) 2024. Robin Hillyard - */ - package com.phasmidsoftware.dsaipg.sort.elementary; import com.phasmidsoftware.dsaipg.sort.Helper; @@ -74,15 +70,14 @@ public HeapSort(int nWords, int nRuns, Config config) { public void sort(X[] array, int from, int to) { if (array == null || array.length <= 1) return; - // XXX construction phase + // Construction phase: Build a max heap buildMaxHeap(array); - // XXX sort-down phase + // Sort-down phase: Move the largest element to the end, and restore the heap Helper helper = getHelper(); - // TODO we over-count hits in the swap operation -- fix it. for (int i = array.length - 1; i >= 1; i--) { - helper.swap(array, 0, i); - heapify(array, i, 0); + helper.swap(array, 0, i); // Swap the root (max element) with the last element + heapify(array, i, 0); // Restore the heap property for the reduced heap } } @@ -96,8 +91,10 @@ public void sort(X[] array, int from, int to) { * should support comparison. */ private void buildMaxHeap(X[] array) { - int half = array.length / 2; - for (int i = half; i >= 0; i--) heapify(array, array.length, i); + int half = array.length / 2; // Start with the last parent node + for (int i = half; i >= 0; i--) { + heapify(array, array.length, i); // Ensure max-heap property for each subtree + } } /** @@ -113,19 +110,24 @@ private void buildMaxHeap(X[] array) { * subtree rooted at this index satisfies the max-heap property upon completion. */ private void heapify(X[] array, int heapSize, int index) { - // TODO we over-count hits in the swap operation -- fix it. Helper helper = getHelper(); final int left = index * 2 + 1; final int right = index * 2 + 2; int largest = index; + + // Check if left child exists and is larger than root if (left < heapSize && helper.compare(array, largest, left) < 0) largest = left; + + // Check if right child exists and is larger than root (or left child) if (right < heapSize && helper.compare(array, largest, right) < 0) largest = right; + + // If the largest is not the current index, swap and continue heapifying if (index != largest) { helper.swap(array, index, largest); - heapify(array, heapSize, largest); + heapify(array, heapSize, largest); // Recursively heapify the affected subtree } } public static final String DESCRIPTION = "Heap Sort"; -} \ No newline at end of file +} diff --git a/src/main/java/com/phasmidsoftware/dsaipg/sort/elementary/InsertionSortComparator.java b/src/main/java/com/phasmidsoftware/dsaipg/sort/elementary/InsertionSortComparator.java index ff5460a9..00ec8661 100644 --- a/src/main/java/com/phasmidsoftware/dsaipg/sort/elementary/InsertionSortComparator.java +++ b/src/main/java/com/phasmidsoftware/dsaipg/sort/elementary/InsertionSortComparator.java @@ -22,6 +22,9 @@ * @param the type of elements to be sorted, which can be compared using a provided comparator. */ public class InsertionSortComparator extends SortWithHelper { + + public static final String DESCRIPTION = "Insertion sort"; + /** * Constructor for InsertionSortComparator, which initializes the comparator with the provided helper. * @@ -45,38 +48,42 @@ protected InsertionSortComparator(String description, Comparator comparator, } /** - * Constructor for InsertionSort + * Constructor for InsertionSortComparator. * - * @param N the number elements we expect to sort. - * @param nRuns the number of runs to be expected (this is only significant when instrumenting). - * @param config the configuration. + * @param comparator the comparator to use for sorting elements. + * @param N the expected number of elements to be sorted. + * @param nRuns the number of runs expected (used for benchmarking and analysis). + * @param config the configuration settings. */ public InsertionSortComparator(Comparator comparator, int N, int nRuns, Config config) { this(DESCRIPTION, comparator, N, nRuns, config); } /** - * Sort the sub-array xs:from:to using insertion sort. + * Sort the sub-array xs[from:to] using insertion sort. * - * @param xs sort the array xs from "from" to "to". - * @param from the index of the first element to sort - * @param to the index of the first element not to sort + * @param xs the array to be sorted. + * @param from the starting index of the sorting range. + * @param to the index one past the last element to be sorted. */ public void sort(X[] xs, int from, int to) { final Helper helper = getHelper(); - - // TO BE IMPLEMENTED -throw new RuntimeException("implementation missing"); + for (int i = from + 1; i < to; i++) { + X key = xs[i]; + int j = i - 1; + while (j >= from && helper.less(key, xs[j])) { + xs[j + 1] = xs[j]; + j--; + } + xs[j + 1] = key; + } } - public static final String DESCRIPTION = "Insertion sort"; - /** - * Sorts the given array in-place using the provided insertion sort comparator. + * Sorts the given array in-place using the insertion sort algorithm. * * @param the generic type parameter that extends Comparable. - * @param ts the array of elements to be sorted, where elements must implement {@code Comparable}. - * The method modifies this array directly to produce the sorted order. + * @param ts the array of elements to be sorted. * @throws RuntimeException if an IOException occurs during the sorting process. */ public static > void sort(T[] ts) { @@ -99,19 +106,19 @@ public static Sort stringSorterCaseInsensitive(int n, Config config) { } /** - * This method is designed to count inversions in quadratic time, using insertion sort. + * This method is designed to count inversions in quadratic time using insertion sort. * - * @param ts an array of comparable T elements. - * @param the underlying type of the elements. + * @param ts an array of comparable T elements. + * @param comparator the comparator used for sorting. + * @param the underlying type of the elements. * @return the number of inversions in ts, which remains unchanged. */ public static long countInversions(T[] ts, Comparator comparator) { final Config config = Config_Benchmark.setupConfigFixes(); try (InsertionSortComparator sorter = new InsertionSortComparator<>(comparator, ts.length, getRunsConfig(config), config)) { Helper helper = sorter.getHelper(); - sorter.sort(ts, true); + sorter.sort(ts, 0, ts.length); return helper.getFixes(); } } - } \ No newline at end of file diff --git a/src/main/java/com/phasmidsoftware/dsaipg/sort/linearithmic/MergeSort.java b/src/main/java/com/phasmidsoftware/dsaipg/sort/linearithmic/MergeSort.java index 30f41af7..2a275e99 100644 --- a/src/main/java/com/phasmidsoftware/dsaipg/sort/linearithmic/MergeSort.java +++ b/src/main/java/com/phasmidsoftware/dsaipg/sort/linearithmic/MergeSort.java @@ -1,7 +1,3 @@ -/* - * Copyright (c) 2024. Robin Hillyard - */ - package com.phasmidsoftware.dsaipg.sort.linearithmic; import com.phasmidsoftware.dsaipg.sort.Helper; @@ -15,7 +11,7 @@ import static com.phasmidsoftware.dsaipg.util.Config_Benchmark.*; /** - * Class MergeSort. + * Class MergeSort implements the Merge Sort algorithm with optional optimizations. * * @param the underlying comparable type. */ @@ -24,9 +20,7 @@ public class MergeSort> 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/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/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));