/* Program: AnalysisOfSortingAlgorithms * By: John P. Spurgeon * Created: 21 April 2018 * Updated: 23 April 2018 * URL: http://scribbledecobble.blogspot.com/2018/04/analysis-of-sorting-algorithms.html * * Some (multi)sets to try to sort. * * 0. Simple Sets: a b c d e f g h ... * * 1. A Multiset: c a b d d a b d a d * See: TAOCP, vol. 3, sec. 5.1.2. * * 2. A Set: 503 087 512 061 908 170 897 275 653 426 154 509 612 677 765 703 * See: TAOCP, vol. 3 (2nd ed.), p. 76. */ // With a little help from my friends... import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.List; import java.util.LinkedList; import java.util.ArrayList; import java.util.Comparator; import java.util.Collections; /* abstracted adj. * * 1. Drawn off; separate, apart from 1660. * 2. Withdrawn from the contemplation of present objects; absent in mind 1643. * *3. Separated from the concrete, ideal, abstruse (replaced by ABSTRACT a. 4) - 1823. * * The Evil one a. stood From his own evil MILT. * * * disinterestedness. * * Source: The Sorter Oxford English Dictionary on Historical Principles, * Third Edition, Revised with Addenda (1947). */ interface ComparisonCounter { int getComparisonCount(); } interface ExchangeCounter { int getExchangeCount(); } interface ExecutionTimer { long getElapsedTime(); }
/* Counting Comparator */ final class CountingComparator<E> implements Comparator<E>, ComparisonCounter { private Comparator<E> cmp; private int comparisonCount = 0; public CountingComparator(Comparator<E> cmp) { this.cmp = cmp; } // lhs: left hand side. // rhs: right hand side. public final int compare(E lhs, E rhs) { comparisonCount++; return cmp.compare(lhs, rhs); } public final int getComparisonCount() { return comparisonCount; } } /* All Sorts of Sorters */ final class Sorters { public static final String PARTIALLY_ABSTRACTED_SORTER = "Partially Abstracted Sorter", COUNTING_SORTER = "Counting Sorter", EXCHANGE_SORTER = "Exchange Sorter", COUNTING_EXCHANGE_SORTER = "Counting Exchange Sorter", BAD_BUBBLE_SORTER = "Bad Bubble Sorter", BETTER_BUBBLE_SORTER = "Better Bubble Sorter", BEST_BUBBLE_SORTER = "Best Bubble Sorter", INSERTION_SORTER = "Insertion Sorter", MERGE_SORTER = "Merge Sorter", QUICK_SORTER = "Quick Sorter", COLLECTIONS_SORTER = "Collections Sorter"; } // Generic, Possibly-Partially-Disinterested (in performance) Sorters interface Sorter extends ComparisonCounter, ExchangeCounter, ExecutionTimer { <E> void sort(List<E> sequence, Comparator<E> cmp); <E extends Comparable<E>> void sort(List<E> sequence); <E extends Comparable<E>> void sortReverse(List<E> sequence); // Does it actually count? boolean countsComparisons(); boolean countsExchanges(); }
// Certainly partially disinterested... abstract class PartiallyAbstractedSorter implements Sorter { private long elapsedTime; //******************************************// // ALL YOU HAVE TO DO IS SUPPLY THE METHOD! // //******************************************// protected abstract <E> void sortMethod(List<E> sequence, Comparator<E> cmp); // This Sorter is not entirely abstracted: it pays close attention to time. protected final <E> void timeSortMethod(List<E> sequence, Comparator<E> cmp) { long start = System.nanoTime(); this.sortMethod(sequence, cmp); elapsedTime = System.nanoTime() - start; } public <E> void sort(List<E> sequence, Comparator<E> cmp) { timeSortMethod(sequence, cmp); } public final <E extends Comparable<E>> void sort(List<E> sequence) { Comparator<E> cmp = (E lhs, E rhs) -> lhs.compareTo(rhs); sort(sequence, cmp); } public final <E extends Comparable<E>> void sortReverse(List<E> sequence) { Comparator<E> cmp = (E lhs, E rhs) -> rhs.compareTo(lhs); sort(sequence, cmp); } public boolean countsComparisons() { return false; } public boolean countsExchanges() { return false; } public int getComparisonCount() { throw new UnsupportedOperationException(); } public int getExchangeCount() { throw new UnsupportedOperationException(); } public String toString() { return Sorters.PARTIALLY_ABSTRACTED_SORTER; } public long getElapsedTime() { return elapsedTime; } }
abstract class CountingSorter extends PartiallyAbstractedSorter { private int comparisonCount = 0; public final <E> void sort(List<E> sequence, Comparator<E> cmp) { CountingComparator<E> cc = new CountingComparator<E>(cmp); timeSortMethod(sequence, cc); comparisonCount = cc.getComparisonCount(); } public final boolean countsComparisons() { return true; } public final int getComparisonCount() { return comparisonCount; } public String toString() { return Sorters.COUNTING_SORTER; } } abstract class ExchangeSorter extends PartiallyAbstractedSorter { protected <E> void swap(List<E> sequence, int i, int j) { E tmp = sequence.get(i); sequence.set(i, sequence.get(j)); sequence.set(j, tmp); } public String toString() { return Sorters.EXCHANGE_SORTER; } }
abstract class CountingExchangeSorter extends ExchangeSorter { private int comparisonCount = 0, exchangeCount = 0; protected final <E> void swap(List<E> sequence, int i, int j) { super.swap(sequence, i, j); exchangeCount++; } public final <E> void sort(List<E> sequence, Comparator<E> cmp) { exchangeCount = 0; CountingComparator<E> cc = new CountingComparator<E>(cmp); timeSortMethod(sequence, cc); comparisonCount = cc.getComparisonCount(); } public final boolean countsComparisons() { return true; } public final boolean countsExchanges() { return true; } public final int getComparisonCount() { return comparisonCount; } public final int getExchangeCount() { return exchangeCount; } public String toString() { return Sorters.COUNTING_EXCHANGE_SORTER; } }
// Bubble Sort class BadBubbleSorter extends CountingExchangeSorter { // Exercise 1a: Fix the problem with the bubble sort implementation below. protected <E> void sortMethod(List<E> sequence, Comparator<E> cmp) { /* * This method sorts. But it sorts in the wrong direction! */ final int n = sequence.size(); final int lastIndex = n - 1; for (int count = 0; count < n; count++) { for (int i = 0; i < lastIndex; i++) { int j = i + 1; E lhs = sequence.get(i); E rhs = sequence.get(j); if (cmp.compare(lhs, rhs) < 0) swap(sequence, i, j); } } } public String toString() { return Sorters.BAD_BUBBLE_SORTER; } } class BetterBubbleSorter extends BadBubbleSorter { // Exercise 1b: Implement bubble sort again. Make it better this time. // Specifically: On each pass, do one fewer comparison than before. protected <E> void sortMethod(List<E> sequence, Comparator<E> cmp) { } public String toString() { return Sorters.BETTER_BUBBLE_SORTER; } }
// Best: not Bestest, MoreBestest, or MostBestest. (But it's not final.) class BestBubbleSorter extends BetterBubbleSorter { // Exercise 1c: Implement bubble sort one more time. Make it even better. // Specifically: Stop as soon as you know you're done. protected <E> void sortMethod(List<E> sequence, Comparator<E> cmp) { } public final String toString() { return Sorters.BEST_BUBBLE_SORTER; } } // Insertion Sort final class InsertionSorter extends CountingExchangeSorter { // Sort an unordered sequence... protected <E> void sortMethod(List<E> sequence, Comparator<E> cmp) { // Exercise 2a: Implement insertion sort. } // Insert into a sorted multiset... public <E> void insertIntoMultiset(E element, List<E> sortedSeq, Comparator<E> cmp) { // Exercise 2b: Write code that inserts an element into a multiset (duplicates allowed). // Assume that the list passed to the method is in sorted order. } public final <E extends Comparable<E>> void insertIntoMultiset(E element, List<E> sortedSeq) { Comparator<E> cmp = (E lhs, E rhs) -> lhs.compareTo(rhs); insertIntoMultiset(element, sortedSeq, cmp); } public final <E extends Comparable<E>> void insertIntoMultisetReverse(E element, List<E> sortedSeq) { Comparator<E> cmp = (E lhs, E rhs) -> rhs.compareTo(lhs); insertIntoMultiset(element, sortedSeq, cmp); }
// Insert into a sorted set... public <E> boolean insertIntoSet(E element, List<E> sortedSeq, Comparator<E> cmp) { // Exercise 2c: Write code that inserts an element into a set (duplicates not allowed). // Assume that the list passed to the method is in sorted order. return false; // Modify this line if necessary. } public final <E extends Comparable<E>> boolean insertIntoSet(E element, List<E> sortedSeq) { Comparator<E> cmp = (E lhs, E rhs) -> lhs.compareTo(rhs); insertIntoSet(element, sortedSeq, cmp); return false; } public final <E extends Comparable<E>> boolean insertIntoSetReverse(E element, List<E> sortedSeq) { Comparator<E> cmp = (E lhs, E rhs) -> rhs.compareTo(lhs); insertIntoSet(element, sortedSeq, cmp); return false; } public String toString() { return Sorters.INSERTION_SORTER; } } // Quick Sort (What about Quicker? Quickest? MostQuickest?) class QuickSorter extends CountingExchangeSorter { protected <E> void sortMethod(List<E> sequence, Comparator<E> cmp) { // Exercise 3: Implement quick sort. } public String toString() { return Sorters.QUICK_SORTER; } }
// Merge Sort final class MergeSorter extends CountingSorter { protected <E> void sortMethod(List<E> sequence, Comparator<E> cmp) { // Exercise 4: Implement merge sort. } public String toString() { return Sorters.MERGE_SORTER; } } // Using Java's Collections.sort method to sort. final class CollectionsSorter extends CountingSorter { protected <E> void sortMethod(List<E> sequence, Comparator<E> cmp) { Collections.sort(sequence, cmp); } public String toString() { return Sorters.COLLECTIONS_SORTER; } }
/* * n. permuter 1. One who permutes. */ final class Permuter { public static <E> List<List<E>> getAllPermutations(Set<E> set) { ArrayList<E> list = new ArrayList<E>(set.size()); for (E element : set) list.add(element); return getAllPermutations(list, 0); } public static <E> List<List<E>> getAllPermutations(List<E> list) { return getAllPermutations(list, 0); } private static <E> List<List<E>> getAllPermutations(List<E> list, int headIndex) { final int permutationLength = list.size() - headIndex; List<List<E>> permutations = new ArrayList<List<E>>(); if (permutationLength > 0) { E theHeadElement = list.get(headIndex); List<List<E>> permutationsOfTail = getAllPermutations(list, headIndex + 1); for (List<E> permutationOfTail : permutationsOfTail) { final int lastIndex = permutationLength - 1; for (int putHeadAtIndex = 0; putHeadAtIndex <= lastIndex; putHeadAtIndex++) { List<E> newPermutation = new ArrayList<E>(permutationLength); for (int i = 0; i < lastIndex; i++) { E nextTailElement = permutationOfTail.get(i); if (putHeadAtIndex == i) { newPermutation.add(theHeadElement); } newPermutation.add(nextTailElement); } if (putHeadAtIndex == lastIndex) { newPermutation.add(theHeadElement); } permutations.add(newPermutation); } } } else { // There is one way to arrange no elements. permutations.add(new ArrayList<E>()); } return permutations; } }
/* A Statistics Utility */ final class Statistics { public static Double getMin(List<Double> values) { final int n = values.size(); if (n == 0) return null; Double smallest = values.get(0); for (int i = 1; i < n; i++) { Double value = values.get(i); if (value < smallest) smallest = value; } return smallest; } public static Double getMax(List<Double> values) { final int n = values.size(); if (n == 0) return null; Double largest = values.get(0); for (int i = 1; i < n; i++) { Double value = values.get(i); if (value > largest) largest = value; } return largest; } public static Double getSum(List<Double> values) { Double sum = 0.0; for (Double value : values) sum += value; return sum; } public static Double getAvg(List<Double> values) { return getSum(values) / values.size(); } public static Double getStdDev(List<Double> values, Double avg) { final List<Double> diffsSquared = new ArrayList<Double>(values.size()); for (Double value : values ) { Double diff = avg - value; Double diffSquared = diff * diff; diffsSquared.add(diffSquared); } return Math.sqrt(getAvg(diffsSquared)); } public static Double getStdDev(List<Double> values) { return getStdDev(values, getAvg(values)); } }
/* Count, Min, Max, Avg, SD (standard deviation) * * Count is not one of the Gang of Four statistics, but it's good to know. * * SEE: Stanford Lecture - Don Knuth: The Analysis of Algorithms * Donald Knuth recreates his very first lecture taught at Stanford University. * https://www.youtube.com/watch?v=jmcSzzN1gkc (JUMP TO 25:45) */ final class DataSet { private final String description; private final List<Double> values; private Double min = null, max = null, avg = null, stdDev = null, sum = 0.0; private boolean statsAreCurrent = true; public DataSet(String description, int initialCapacity) { this.description = description; values = new ArrayList<Double>(initialCapacity); } public DataSet(String description) { this.description = description; values = new LinkedList<Double>(); } public final void add(Integer value) { add(value.doubleValue()); } public final void add(Double value) { statsAreCurrent = false; values.add(value); sum += value; if (values.size() == 1) { min = value; max = value; } else { if (value < min) min = value; if (value > max) max = value; } } private void analyzeThis() { if (!statsAreCurrent) { avg = sum / values.size(); stdDev = Statistics.getStdDev(values, avg); statsAreCurrent = true; } }
public final String toString() { List<DataSet> listOfThis = new LinkedList<DataSet>(); listOfThis.add(this); return DataSet.toString(listOfThis); } private static String toFixed(Double d) { return String.format("%.1f", d); } private static String toFixed(String string) { final int length = 6; return String.format("%1$" + length + "s", string); } public static String toString(List<DataSet> dataSets) { String description = ""; final String SEP = " "; for (DataSet ds : dataSets) { description = ds.description; ds.analyzeThis(); } StringBuilder sb = new StringBuilder(); sb.append("Dataset:" + SEP + description + "\n"); sb.append("\n Count:"); for (DataSet ds : dataSets) sb.append(SEP + toFixed(((Integer)ds.values.size()).toString())); sb.append("\n Min:"); for (DataSet ds : dataSets) sb.append(SEP + toFixed(toFixed(ds.min))); sb.append("\n Max:"); for (DataSet ds : dataSets) sb.append(SEP + toFixed(toFixed(ds.max))); sb.append("\n Avg:"); for (DataSet ds : dataSets) sb.append(SEP + toFixed(toFixed(ds.avg))); sb.append("\n SD:"); for (DataSet ds : dataSets) sb.append(SEP + toFixed(toFixed(ds.stdDev))); sb.append("\n"); return sb.toString(); } }
/* Mathematicians */ interface MathProfessor { <E extends Comparable<E>> void analyze(List<E> sequence); String getAnalysis(); void nextDataSet(); } class ExecutionTimeAnalyst implements MathProfessor { private final String HR = "********************************\n"; private DataSet executionTimes; private final List<DataSet> executionTimesDataSets; private final Sorter sorter; public ExecutionTimeAnalyst(Sorter sorter) { this.sorter = sorter; executionTimesDataSets = new LinkedList<DataSet>(); } public <E extends Comparable<E>> void analyze(List<E> sequence) { sorter.sort(sequence); Long time = sorter.getElapsedTime(); executionTimes.add(time.doubleValue() / 1000); } public void nextDataSet() { executionTimes = new DataSet("Execution Time (microseconds)"); executionTimesDataSets.add(executionTimes); } protected final int getComparisonCount() { return sorter.getComparisonCount(); } protected final int getExchangeCount() { return sorter.getExchangeCount(); } public String getAnalysis() { return HR + sorter.toString() + "\n\n" + DataSet.toString(executionTimesDataSets) + "\n"; } }
class ComparisonCountAnalyst extends ExecutionTimeAnalyst { private DataSet comparisons; private final List<DataSet> comparisonsDataSets; public ComparisonCountAnalyst(Sorter sorter) { super(sorter); comparisonsDataSets = new LinkedList<DataSet>(); } public <E extends Comparable<E>> void analyze(List<E> sequence) { super.analyze(sequence); comparisons.add(getComparisonCount()); } public void nextDataSet() { super.nextDataSet(); comparisons = new DataSet("Number of Comparisons"); comparisonsDataSets.add(comparisons); } public String getAnalysis() { return super.getAnalysis() + DataSet.toString(comparisonsDataSets) + "\n"; } } final class ExchangeCountAnalyst extends ComparisonCountAnalyst { private DataSet exchanges; private final List<DataSet> exchangesDataSets; public ExchangeCountAnalyst(Sorter sorter) { super(sorter); exchangesDataSets = new LinkedList<DataSet>(); } public <E extends Comparable<E>> void analyze(List<E> sequence) { super.analyze(sequence); exchanges.add(getExchangeCount()); } public void nextDataSet() { super.nextDataSet(); exchanges = new DataSet("Number of Exchanges"); exchangesDataSets.add(exchanges); } public final String getAnalysis() { return super.getAnalysis() + DataSet.toString(exchangesDataSets) + "\n"; } }
/* Educational Entities */ final class StanfordUniversity { public static MathProfessor getProfessor(Sorter sorter) { if (sorter.countsComparisons()) { if (sorter.countsExchanges()) return new ExchangeCountAnalyst(sorter); else return new ComparisonCountAnalyst(sorter); } else { return new ExecutionTimeAnalyst(sorter); } } } final class ComputerScienceStudent { private Sorter sorter; private final MathProfessor donKnuth; public ComputerScienceStudent(Sorter sorter) { this.sorter = sorter; donKnuth = StanfordUniversity.getProfessor(sorter); } public final void analyze(List<List<String>> permutations) { donKnuth.nextDataSet(); for (List<String> permutation : permutations) { // Analyzing a permutation PERMUTES the permutation, and we // will need to use permutations again, so make a copy and // analyze the copy instead of the original permutation. List<String> copy = new ArrayList<String>(permutation.size()); for (String element : permutation) { copy.add(element); } donKnuth.analyze(copy); } } public final String getResults() { return donKnuth.getAnalysis(); } }
final class TeachingAssistant { private final List<List<List<String>>> setsOfAllPermutations; private final SortedSet<String> sortedSet = new TreeSet<String>(); public TeachingAssistant(String[] args) { final int n = args.length; setsOfAllPermutations = new ArrayList<List<List<String>>>(n); List<List<String>> permutations; for (String string : args) { sortedSet.add(string); permutations = Permuter.getAllPermutations(sortedSet); setsOfAllPermutations.add(permutations); } } public String getSuperset() { String superset = "SUPERSET:"; for (String element : sortedSet) { superset += " " + element; } return superset; } public final void createExercisesFor(List<ComputerScienceStudent> students) { for (List<List<String>> setOfAllPermutations : setsOfAllPermutations) { for (ComputerScienceStudent student : students) { student.analyze(setOfAllPermutations); } } } }
// The Main Program: It's about time... final public class AnalaysisOfSortingAlgorithms { // What kind of class doesn't have students? private static List<ComputerScienceStudent> students; // All sorts of sorters for students to study. private static Sorter[] allSortsOfSorters = { new CollectionsSorter(), new BadBubbleSorter(), new BetterBubbleSorter(), new BestBubbleSorter(), new InsertionSorter(), new MergeSorter(), new QuickSorter(), }; public static void main(String[] args) { // Recruit the students. students = new LinkedList<ComputerScienceStudent>(); for (Sorter sorter : allSortsOfSorters) { // Assign a sorter to each student. students.add(new ComputerScienceStudent(sorter)); } // Hire a TA and put her to work creating exercises for the students. TeachingAssistant annaKarlin = new TeachingAssistant(args); annaKarlin.createExercisesFor(students); // Print the results. System.out.println(annaKarlin.getSuperset() + "\n"); for (ComputerScienceStudent student : students) { System.out.println(student.getResults()); } // finis ... (Retire early and write books.) } }