// by John P. Spurgeon
/* Of course, “nonnumerical analysis” is a terribly negative name for this
* field of study, and it would be much better to have a positive, descriptive term
* which characterizes the subject. “Information processing” is too broad a
* designation for the matter I am considering, and “programming techniques”
* is too narrow. Therefore, I wish to propose analysis of algorithms as an appro-
* priate name for the subject matter covered in these books; as explained more
* fully in the books themselves, this name is meant to imply “the theory of the
* properties of particular computer algorithms.”
*
* — DONALD E. KNUTH, The Art of Computer Programming, vol. 1 (1968)
*
*
* Ernest Hemingway’s work is characterized by
* direct, uncomplicated prose and a lack of arti-
* fice. In his fiction, he describes only the tangible
* truths: dialog, action, superficial traits. He does
* not attempt to explain emotion; he leaves it alone.
* This is not because Hemingway doesn’t want his
* stories to convey feeling — quite the opposite: his
* intent is to create a vacuum so that it might be
* filled by the reader’s own experience. After all,
* emotion is more easily felt than described with
* words:
*
* I have tried to eliminate everything unneces-
* sary to conveying experience to the reader so
* that after he or she has read something it will
* become a part of his or her experience and
* seem actually to have happened.
*
* Hemingway’s prose is never showy, and his
* syntax is almost obsessively conventional. The
* short, unchallenging sentences and absence of
* difficult words add a childlike quality to his ca-
* dence. He assumes the role of naive observer, all
* the better to draw his readers into the emotional
* caos beneath.
*
* — ANGUS CROLL, If Hemingway Wrote JavaScript (2015)
*
*
* In the late summer of that year we lived in a house in a village
* that looked across the river and the plain to the mountains.
*
* — ERNEST HEMINGWAY, A Farewell to Arms (1929)
*/
// On 30 April 2018, this was at:
// http://scribbledecobble.blogspot.com/2018/04/analysis-of-algorithms.html
/* In the last years of my twenty at the corporation, we built a house on a hill that looks across
* the Sunset Highway and the Willamette Valley to town. I worked in the IT department back then.
*/
/* An arm is an upper limb of the body.
* Arm or ARM may also refer to:
*
* Computing
*
* - ARM architecture, a RISC instruction set family
* - ARM Holdings, a multinational company that designs the ARM computer processors
* - Application Response Measurement, an open standard for diagnosing performance bottlenecks
* - Abstract rewriting machine, a virtual machine
* - Arm (software), a CLI status monitor for Tor
*
* Weaponry
*
* - Armaments or weapon, any device used with intent to inflict damage or harm
* - Firearm, a portable gun
* - Anti-radiation missile, a missile designed to detect and home in on an enemy radio emission source
*
* from https://en.wikipedia.org/wiki/Arm_(disambiguation)
*/
/* Edmund Callis Berkeley (February 22, 1909 – March 7, 1988) was an American computer scientist
* who co-founded the Association for Computing Machinery (ACM) in 1947. His 1949 book Giant Brains,
* or Machines That Think popularized cognitive images of early computers. He was also a social
* activist who worked to achieve conditions that might minimize the threat of nuclear war.
*
* Berkeley received a BA in Mathematics and Logic from Harvard in 1930. He pursued a career as an
* insurance actuary at Prudential Insurance from 1934–48, except for service in the United States
* Navy during World War II.
*
* from https://en.wikipedia.org/wiki/Edmund_Berkeley
*/
/* The leaders of North and South Korea agreed on Friday to work to remove all nuclear weapons from the
* Korean Peninsula and, within the year, pursue talks with the United States to declare an official end
* to the Korean War, which ravaged the peninsula from 1950 to 1953.
*
* from “North and South Korea Set Bold Goals: A Final Peace and No Nuclear Arms”
* by Choe Sang-Hun, The New York Times (April 27, 2018); accessed on 29 April 2018 at:
* https://www.nytimes.com/2018/04/27/world/asia/north-korea-south-kim-jong-un.html
*/
/***********************************/
/* PROGRAM SOURCE CODE BEGINS HERE */
/***********************************/
import java.lang.Iterable;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.ArrayList;
import java.util.List;
/**************/
/* INTERFACES */
/**************/
// Values comprise datasets.
interface Labeled { String label(); }
interface Dataset<E> extends Labeled {
int size();
E getValue(int i);
E lastAdded();
}
interface ExtensibleDataset<E> extends Dataset<E> {
void add(E element);
}
// Datasets yield fundamental metrics.
interface IntegralMetrics extends Labeled {
Long sum();
Long min();
Long max();
Double avg();
Double sd(); // standard deviation
}
// Fundamental metrics can be used to analyze algorithms.
interface TimeAnalysis { IntegralMetrics elapsedTime(); }
interface ComparisonAnalysis { IntegralMetrics comparisons(); }
interface ExchangeAnalysis { IntegralMetrics exchanges(); }
// Doers do.
interface Searcher<E extends Comparable<E>> { int search(E element, List<E> list); }
interface Sorter<E extends Comparable<E>> { List<E> sort(List<E> list); }
interface Exchanger<E> { void exchange(List<E> list, int i, int j); }
interface Permuter<E> { List<List<E>> allPermutationsOf(List<E> list); }
interface Shuffler<E> { List<E> shuffle(List<E> list); }
interface ReadonlyCounter { long getCount(); }
interface Counter extends ReadonlyCounter { void increment(); }
// Some types are mashups, such as doers that think too.
interface ComparisonAlgorithmAnalysis extends TimeAnalysis, ComparisonAnalysis {}
interface ExchangeSortingAnalysis extends ComparisonAlgorithmAnalysis, ExchangeAnalysis {}
interface SearcherAnalyzer<E extends Comparable<E>>
extends Searcher<E>, ComparisonAlgorithmAnalysis, ReadonlyCounter {}
interface SorterAnalyzer<E extends Comparable<E>>
extends Sorter<E>, ComparisonAlgorithmAnalysis, ReadonlyCounter {}
interface ExchangeSorterAnalyzer<E extends Comparable<E>>
extends SorterAnalyzer<E>, ExchangeSortingAnalysis {}
/*****************************************/
/* COUNTERS, COMPARATORS, AND EXCHANGERS */
/*****************************************/
class CounterMayThrowException implements Counter
{
private long count = 0;
private long overflowCount = 0;
private final boolean throwExceptionIfCounterOverflows;
public CounterMayThrowException() { this(true); }
public CounterMayThrowException(boolean throwExceptionIfCounterOverflows) {
this.throwExceptionIfCounterOverflows = throwExceptionIfCounterOverflows;
}
public final long getCount() { return count; }
public final long getOverflowCount() { return overflowCount; }
public final void increment()
{
if (++count < 0) // Increment count first and then check the value.
{
count = 1;
onOverflow();
}
}
protected void onOverflow() {
if (throwExceptionIfCounterOverflows)
throw new ArithmeticException("Counter overflowed.");
if (++overflowCount < 0)
throw new ArithmeticException("Overflow counter overflowed. (Wow!)");
}
}
final class ComparatorCounter<E> implements Comparator<E>, ReadonlyCounter
{
private final Comparator<E> comparator;
private final Counter comparisonCounter = new CounterMayThrowException();
public ComparatorCounter(Comparator<E> comparator) { this.comparator = comparator; }
public final int compare(E left, E right)
{
comparisonCounter.increment();
return comparator.compare(left, right);
}
public final long getCount() { return comparisonCounter.getCount(); }
}
final class ExchangerCounter<E> implements Exchanger<E>, ReadonlyCounter
{
private final Counter exchangeCounter = new CounterMayThrowException();
public final void exchange(List<E> list, int i, int j)
{
exchangeCounter.increment();
final E tmp = list.get(i);
list.set(i, list.get(j));
list.set(j, tmp);
}
public final long getCount() { return exchangeCounter.getCount(); }
}
/********************************/
/* EXTENSIBLE DATASET + METRICS */
/********************************/
final class ExtensibleDatasetMetrics implements ExtensibleDataset<Long>, IntegralMetrics
{
private final String label;
private List<Long> values = new ArrayList<Long>();
private Long lastAdded = null;
private Long sum = null;
private Long min = null;
private Long max = null;
private Double sd;
private boolean isDirty = false;
public ExtensibleDatasetMetrics(String label) { this.label = label; }
public final String label() { return label; }
public final int size() { return values.size(); }
public final Long getValue(int i) { return values.get(i); }
public final Long lastAdded() { return lastAdded; }
public final Long sum() { return sum; }
public final Long max() { return max; }
public final Long min() { return min; }
public final Double avg() { return ((double)sum) / values.size(); }
public final Double sd() {
if (isDirty) {
sd = computeStdDev(values, this.avg());
isDirty = false;
}
return sd;
}
public void add(Long value) {
isDirty = true;
values.add(value);
lastAdded = value;
if (sum == null) min = max = sum = value;
else {
sum += value;
if (value < min) min = value;
else if (value > max) max = value;
}
}
private double computeStdDev(List<Long> values, double average) {
final int n = values.size();
double sumOfSquares = 0;
for (long value : values) {
final double difference = value - average;
sumOfSquares += difference * difference;
}
final double avgSquaredDifference = sumOfSquares / n;
return Math.sqrt(avgSquaredDifference);
}
}
/* Doctrine and dogma are the enemies of good JavaScript. Beware of the overly
* protective mentor; reject the dry and narrow confines of computer science
* classes. Some developers thrive on rules and constraint, which is why there
* is Java. If 25 famous authors wrote Java, the result would be more or less
* the same every time. But JavaScript is much less prescriptive and appeals to
* those who value creativity over predictability.
*
* —ANGUS CROLL, If Hemingway Wrote JavaScript (2015)
*/
/*********************************/
/* COMPARISON ALGORITHM ANALYZER */
/*********************************/
abstract class AbstractComparisonAlgorithmAnalyzer<E extends Comparable<E>>
implements ComparisonAlgorithmAnalysis
{
private final ComparatorCounter<E> comparatorCounter;
private long startCompareCount, startTime;
private final ExtensibleDatasetMetrics
timeData = new ExtensibleDatasetMetrics("Time"),
compareData = new ExtensibleDatasetMetrics("Comparisons");
// Constructors
protected AbstractComparisonAlgorithmAnalyzer() {
this((E left, E right) -> left.compareTo(right));
}
protected AbstractComparisonAlgorithmAnalyzer(Comparator<E> cmp) {
this(new ComparatorCounter<E>(cmp));
}
protected AbstractComparisonAlgorithmAnalyzer(ComparatorCounter<E> cmpCtr) {
this.comparatorCounter = cmpCtr;
}
// Protected Operations
protected final Comparator<E> getComparator() {
return comparatorCounter;
}
protected final int compare(E left, E right) {
return comparatorCounter.compare(left, right);
}
protected void doBeforeCallingMethod() {
startCompareCount = comparatorCounter.getCount();
startTime = System.nanoTime();
}
protected void doAfterMethodReturns() {
timeData.add(System.nanoTime() - startTime);
compareData.add(comparatorCounter.getCount() - startCompareCount);
}
// Public Operations
public final long getCount() { return timeData.size(); }
public final IntegralMetrics elapsedTime() { return timeData; }
public final IntegralMetrics comparisons() { return compareData; }
public String toString() { return this.getClass().getSimpleName(); }
}
/************************/
/* ALL SORTS OF SORTERS */
/************************/
abstract class AbstractSorterAnalyzer<E extends Comparable<E>>
extends AbstractComparisonAlgorithmAnalyzer<E> implements SorterAnalyzer<E>
{
// Constructors
protected AbstractSorterAnalyzer() { super(); }
protected AbstractSorterAnalyzer(Comparator<E> cmp) { super(cmp); }
// Additional Operations
protected abstract void applySortMethod(List<E> list); // SORTING ALGORITHM GOES HERE
public final List<E> sort(List<E> list)
{
doBeforeCallingMethod(); // START COLLECTING DATA
applySortMethod(list); // EXECUTE SORTING ALGORITHM
doAfterMethodReturns(); // FINISH COLLECTING DATA
return list;
}
}
abstract class AbstractExchangeSorterAnalyzer<E extends Comparable<E>>
extends AbstractSorterAnalyzer<E> implements ExchangeSorterAnalyzer<E>
{
// Additional Values
private final ExchangerCounter<E> exchangerCounter = new ExchangerCounter<E>();
private long startExchangeCount;
private final ExtensibleDatasetMetrics exchangeData =
new ExtensibleDatasetMetrics("Exchanges");
// Constructors
protected AbstractExchangeSorterAnalyzer() { super(); }
protected AbstractExchangeSorterAnalyzer(Comparator<E> cmp) { super(cmp); }
// Method Overrides
protected final void doBeforeCallingMethod() {
startExchangeCount = exchangerCounter.getCount();
super.doBeforeCallingMethod();
}
protected final void doAfterMethodReturns() {
super.doAfterMethodReturns();
exchangeData.add(exchangerCounter.getCount() - startExchangeCount);
}
// Additional Operations
protected final void exchange(List<E> list, int i, int j) {
exchangerCounter.exchange(list, i, j);
}
protected final ReadonlyCounter getExchangeCounter() { return exchangerCounter; }
public final IntegralMetrics exchanges() { return exchangeData; }
}
final class BubbleSorter<E extends Comparable<E>> extends AbstractExchangeSorterAnalyzer<E>
{
public BubbleSorter() { super(); }
public BubbleSorter(Comparator<E> cmp) { super(cmp); }
protected void applySortMethod(List<E> list)
{
final int n = list.size();
final ReadonlyCounter xCtr = getExchangeCounter();
for (int k = n - 1; k > 0; k--) // The next largest value bubbles up to its spot each pass,
{ // so we don't need to go all the way to the end each time.
final long xCt = xCtr.getCount(); // Note the current exchange count; we'll use it later.
for (int i = 0; i < k; i++) // We'll compare successive pairs of elements beginning at
{ // at index zero, proceeding as far to the right as necessary.
final int j = i + 1; // The index of left is i. Let j be the index of right.
final E left = list.get(i); // Get the value of left at position i.
final E right = list.get(j); // Get the value of right at position j.
if (compare(left, right) > 0) // If left is greater than right,
exchange(list, i, j); // then exchange.
} // Increment i and compare the next pair of elements.
if (xCt == xCtr.getCount()) // If we didn't make any more exchanges, then
return; // the list must be sorted, so return to whence you came.
} // Repeat until done. (Bubble up the next largest value.)
} // Done: Shake a cocktail and celebrate!
}
final class SelectionSorter<E extends Comparable<E>> extends AbstractExchangeSorterAnalyzer<E>
{
public SelectionSorter() { super(); }
public SelectionSorter(Comparator<E> cmp) { super(cmp); }
private int indexOfMin(List<E> list, int i, int j)
{
int indexOfMin = i;
E min = list.get(indexOfMin);
for (int k = i + 1; k <= j; k++)
{
final E curr = list.get(k);
if (compare(curr, min) < 0)
{
min = curr;
indexOfMin = k;
}
}
return indexOfMin;
}
protected void applySortMethod(List<E> list)
{
final int n = list.size(); // Let n be the number of elements to sort.
final int j = n - 1; // Let j be the index of the last element.
for (int i = 0; i < j; i++) // Consider each successively smaller tail of the list:
{ //
final int k = indexOfMin(list, i, j); // Find the index of the smallest element in the tail.
if (k != i) // If the element is not at the front of the tail,
exchange(list, i, k); // then exchange it with the head of the tail.
} // Repeat until the tail is no more.
} // Done.
}
final class InsertionSorter<E extends Comparable<E>> extends AbstractExchangeSorterAnalyzer<E>
{
public InsertionSorter() { super(); }
public InsertionSorter(Comparator<E> cmp) { super(cmp); }
protected void applySortMethod(List<E> list)
{
final int n = list.size(); // Let n be the number of elements to insert in order.
for (int i = 1; i < n; i++) // Insert elements one by one into a sorted part.
{ // The next element to insert, if any, is at index i.
final E right = list.get(i); // Let right be the value of the element at index i.
for (int j = i - 1; j >= 0; j--) // Start just to the left of right and move left.
{ // The next already inserted element is at index j.
final E left = list.get(j); // Let left be the value of the element at index j.
if (compare(right, left) < 0) // If right is less than left, then
exchange(list, j, j + 1); // exchange left and right (right must be at j + 1);
else // otherwise,
break; // right is inserted. Go insert the next element.
} // Consider the next already inserted element, if any.
} // Insert the next element until all are inserted.
} // Done.
}
final class QuickSorter<E extends Comparable<E>> extends AbstractExchangeSorterAnalyzer<E>
{
public QuickSorter() { super(); }
public QuickSorter(Comparator<E> cmp) { super(cmp); }
private void quickSort(List<E> list, int i, int j)
{
if (j <= i) return; // (There might be nothing to do.)
int p = i; // Let p be the index of the pivot.
final E left = list.get(p); // Let left be the value of the pivot.
for (int k = i + 1; k <= j; k++) // Consider each element to the right of the pivot.
{ //
final E right = list.get(k); // Let right be the value under consideration.
if (compare(right, left) < 0) // If right is less than left, then
{ // move right to the other side of left:
if (k > p + 1) // If right isn't already next to left, then
exchange(list, p + 1, k); // move right immediately to the right of left.
exchange(list, p, p + 1); // Exchange left and right.
p++; // Increment the index of the pivot.
} //
} // Consider the next element, if any.
quickSort(list, i, p - 1); // Sort the elements to the left of the pivot.
quickSort(list, p + 1, j); // Sort the elements to the right of the pivot.
} // Done.
protected void applySortMethod(List<E> list) { quickSort(list, 0, list.size() - 1); }
}
final class CollectionsSorter<E extends Comparable<E>> extends AbstractExchangeSorterAnalyzer<E>
{
public CollectionsSorter() { super(); }
public CollectionsSorter(Comparator<E> cmp) { super(cmp); }
protected void applySortMethod(List<E> list) { Collections.sort(list, getComparator()); }
}
/*
final class MergeSorter<E extends Comparable<E>> extends AbstractExchangeSorterAnalyzer<E>
{
private List<E> valuesToSort;
private List<E> mergedValues;
public MergeSorter() { super(); }
public MergeSorter(Comparator<E> cmp) { super(cmp); }
private void createMergedValues(int n) {
mergedValues = new ArrayList<E>(n);
while (n-- > 0) mergedValues.add(null);
}
private void merge(int iStart, int iEnd, int jStart, int jEnd) {
E nextValue;
int i = iStart, j = jStart;
int k = i;
// TO DO: FINISH
while (i <= iEnd && j <= jEnd) {
final E iValue = valuesToSort.get(i);
final E jValue = valuesToSort.get(j);
if (compare(iValue, jValue) < 0) {
nextValue = iValue;
i++;
}
else {
nextValue = jValue;
j++;
}
mergedValues.set(k++, nextValue);
}
while (i <= iEnd) {
nextValue = valuesToSort.get(i++);
mergedValues.set(k++, nextValue);
}
while (j <= jEnd) {
nextValue = valuesToSort.get(j++);
mergedValues.set(k++, nextValue);
}
}
private void mergeSort(int i, int j) {
final int n = (j - i) + 1;
if (n < 2) return;
int startRight = i + (n / 2);
int endLeft = startRight - 1;
// TO DO: FINISH
if (n > 1) {
mergeSort(i, endLeft);
mergeSort(startRight, j);
}
merge(i, endLeft, startRight, j);
for (int k = i; k <= j; k++)
valuesToSort.set(k, mergedValues.get(k));
}
protected void applySortMethod(List<E> list) {
final int n = list.size();
if (n < 2) return;
valuesToSort = list;
createMergedValues(n);
mergeSort(0, n - 1);
}
}
*/
/******************/
/* SORTER FACTORY */
/******************/
final class SorterFactory<E extends Comparable<E>> {
public List<ExchangeSorterAnalyzer<E>> makeExchangeSorters() {
List<ExchangeSorterAnalyzer<E>> sorters;
sorters = new LinkedList<ExchangeSorterAnalyzer<E>>();
sorters.add(new BubbleSorter<E>());
sorters.add(new SelectionSorter<E>());
sorters.add(new InsertionSorter<E>());
sorters.add(new QuickSorter<E>());
sorters.add(new CollectionsSorter<E>());
//sorters.add(new MergeSorter<E>());
return sorters;
}
}
/**********/
/* COPIER */
/**********/
final class Copier {
public static <E> List<E> copy(Iterable<E> list) {
List<E> copyOfList = new LinkedList<E>();
for (E element : list) copyOfList.add(element);
return copyOfList;
}
}
/*************/
/* SHUFFLERS */
/*************/
final class KnuthShuffler<E> implements Shuffler<E> {
private int getRandomIntegerBetween(int lower, int upper) {
final int diff = upper - lower;
return lower + (int)(Math.random() * (diff + 1));
}
public List<E> shuffle(List<E> list) {
final int lastIndex = list.size() - 1;
for (int i = 0; i < lastIndex; i++) {
int j = getRandomIntegerBetween(i, lastIndex);
E tmp = list.get(i);
list.set(i, list.get(j));
list.set(j, tmp);
}
return list;
}
}
final class SortShuffler<E> implements Shuffler<E> {
public List<E> shuffle(List<E> list) {
Collections.sort(list, (E l, E r) -> (int)(Math.random() - 0.5));
return list;
}
}
/* A permutation of a finite set is an arrangement of its elements into a row.
*
* — DONALD E. KNUTH, The Art of Computer Programming, vol. 3 (1998)
*/
/************/
/* PERMUTER */
/************/
final class RecursivePermuter<E> implements Permuter<E>
{
public final List<List<E>> allPermutationsOf(List<E> list) {
return allPermutationsOf(list, 0);
}
private List<List<E>> allPermutationsOf(List<E> list, int headIndex) {
final int permutationLength = list.size() - headIndex;
List<List<E>> allPermutations = new ArrayList<List<E>>();
if (permutationLength > 0)
{
E theHeadElement = list.get(headIndex);
List<List<E>> allPermutationsOfTail = allPermutationsOf(list, headIndex + 1);
for (List<E> permutationOfTail : allPermutationsOfTail)
{
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);
}
allPermutations.add(newPermutation);
}
}
}
else {
allPermutations.add(new ArrayList<E>()); // There is one way to arrange no elements.
}
return allPermutations;
}
}
/********************/
/* ANALYSIS PRINTER */
/********************/
final class AnalysisPrinter<E extends Comparable<E>>
{
private final String TAB = "\t", NEW_LINE = "\n";
public final void printHeaderRow()
{
// OOPS! These headings are out of order!!
final String headings[] = { "row_size", "times_sorted",
"min_cmp", "max_cmp", "avg_cmp", "sd_cmp",
"min_xch", "max_xch", "avg_xch", "sd_xch",
"min_nano_sec", "max_nano_sec", "avg_nano_sec", "sd_nano_sec" };
final StringBuilder sb = new StringBuilder("sorter");
for (String heading : headings) sb.append( TAB + heading);
System.out.print(sb + NEW_LINE);
}
private final void printMetrics(IntegralMetrics metrics)
{
System.out.print(TAB + metrics.min());
System.out.print(TAB + metrics.max());
System.out.print(TAB + metrics.avg());
System.out.print(TAB + metrics.sd());
}
private final void printAnalysis(ComparisonAlgorithmAnalysis analysis)
{
printMetrics(analysis.elapsedTime());
printMetrics(analysis.comparisons());
}
private final void printAnalysis(ExchangeSortingAnalysis analysis)
{
printMetrics(analysis.elapsedTime());
printMetrics(analysis.comparisons());
printMetrics(analysis.exchanges());
}
public final void printDataRow(SearcherAnalyzer<E> searcher, int n)
{
System.out.print(searcher + TAB + n + TAB + searcher.getCount());
printAnalysis(searcher);
System.out.println();
}
public final void printDataRow(SorterAnalyzer<E> sorter, int n)
{
System.out.print(sorter + TAB + n + TAB + sorter.getCount());
printAnalysis(sorter);
System.out.println();
}
public final void printDataRow(ExchangeSorterAnalyzer<E> sorter, int n)
{
System.out.print(sorter + TAB + n + TAB + sorter.getCount());
printAnalysis(sorter);
System.out.println();
}
}
/* The time required to sort N records, using a decent general-purpose sorting algorithm,
* is roughly proportional to N log N; we make about log N “passes” over the data. This is
* the minimal time, as we shall see in Section 5.3.1 if the records are in random order
* and if sorting is done by pairwise comparisons of keys.
*
* — DONALD E. KNUTH, The Art of Computer Programming, vol. 3 (1998)
*/
/********************/
/* SORTING ANALYZER */
/********************/
final class SortingAnalyzer<E extends Comparable<E>>
{
private final int rowSizeTippingPoint, sampleSize;
private final Permuter<E> permuter = new RecursivePermuter<E>();
private final SorterFactory<E> sorterFactory = new SorterFactory<E>();
private final Shuffler<E> shuffler = new KnuthShuffler<E>();
private final AnalysisPrinter<E> printer = new AnalysisPrinter<E>();
private List<ExchangeSorterAnalyzer<E>> sorters;
private boolean headerPrinted = false;
public SortingAnalyzer(int rowSizeTippingPoint, int sampleSize) {
this.rowSizeTippingPoint = rowSizeTippingPoint;
this.sampleSize = sampleSize;
}
private void sortCopiesOf(List<E> permutation) {
for (Sorter<E> sorter : sorters)
sorter.sort(Copier.copy(permutation));
}
private final void sortAllPermutationsOf(List<E> row) {
for (List<E> permutation : permuter.allPermutationsOf(row))
sortCopiesOf(permutation);
}
private final void sortShufflingsOf(List<E> row, int timesToSort) {
final List<E> list = Copier.copy(row);
while (timesToSort-- > 0)
sortCopiesOf(shuffler.shuffle(list));
}
public final void sortPermutationsOf(List<E> row) {
sorters = sorterFactory.makeExchangeSorters();
final int rowSize = row.size();
if (rowSize < rowSizeTippingPoint)
sortAllPermutationsOf(row);
else
sortShufflingsOf(row, sampleSize);
if (!headerPrinted) {
printer.printHeaderRow();
headerPrinted = true;
}
for (ExchangeSorterAnalyzer<E> sorter : sorters)
printer.printDataRow(sorter, rowSize);
}
}
/* The character is believed to have initiated a phenomenon referred to as
* “The Scully Effect”; as the medical doctor and the FBI Special Agent inspired
* many young women to pursue careers in science, medicine and law enforcement,
* and as a result brought a perceptible increase in the number of women
* in those fields.
*
* https://en.wikipedia.org/wiki/Dana_Scully (accessed on 30 April 2018)
*/
/**************************/
/* SORTING ANALYZER AGENT */
/**************************/
final class SortingAnalyzerAgent
{
private final SortingAnalyzer<String> analyzer;
public SortingAnalyzerAgent(int rowSizeTippingPoint, int sampleSize) {
analyzer = new SortingAnalyzer<String>(rowSizeTippingPoint, sampleSize);
}
private List<List<String>> makeRowsToSort(String[] args) {
final List<List<String>> rows = new LinkedList<List<String>>();
final int n = args.length;
for (int i = 1; i < n; i++) {
final List<String> row = new LinkedList<String>();
final int rowSize = Integer.parseInt(args[i]);
for (Integer element = 1; element <= rowSize; element++)
row.add(element.toString());
rows.add(row);
}
return rows;
}
private List<String> makeRowToSort(String[] args) {
final List<String> row = new LinkedList<String>();
final int n = args.length;
for (int i = 1; i < n; i++)
row.add(args[i]);
return row;
}
public final void analyzeSortingAlgorithms(String[] args, String arg0) {
if (arg0.equals("sort_rows_of_size")) {
System.out.println("Sorting permutations of zero or many rows...\n");
for (List<String> row : makeRowsToSort(args))
analyzer.sortPermutationsOf(row);
}
else if (arg0.equals("sort")) {
System.out.println("Sorting permutations of a single row...\n");
analyzer.sortPermutationsOf(makeRowToSort(args));
}
}
}
/* Ada Lovelace was the first person to publish an algorithm intended to be executed
* by the first modern computer, the Analytical Engine created by Charles Babbage.
* Because of this, she is often regarded as the first computer programmer, ...
*
* https://en.wikipedia.org/wiki/Women_in_computing (accessed on 30 April 2018)
*/
/***************/
/* MAIN METHOD */
/***************/
final public class AnalysisOfAlgorithms
{
private static final int ROW_SIZE_TIPPING_POINT = 8;
private static final int SAMPLE_SIZE = 1000;
private static void println(String s) { System.out.println(s); }
public static void main(String[] args)
{
final String arg0 = args.length > 0 ? args[0] : "";
final String arg2 = args.length > 2 ? args[2] : "";
if (arg0.equals("search_for") &&
(arg2.equals("in") || arg2.equals("in_rows_of_size"))) {
System.out.println("Sorry. Searching is not implemented yet.");
}
else if (arg0.equals("sort") || arg0.equals("sort_rows_of_size")) {
final SortingAnalyzerAgent agentLovelace = new SortingAnalyzerAgent(
ROW_SIZE_TIPPING_POINT, SAMPLE_SIZE);
agentLovelace.analyzeSortingAlgorithms(args, arg0);
}
else {
println("This program is designed to sort or search permutations of values and output an");
println("analysis of the sorting or searching algorithms used. When fully implemented,");
println("the program will respond to command-line arguments as follows.\n");
println("1. If the first command-line argument is sort or sort_rows_of_size, then");
println(" the program will attempt to sort several permutations of rows of values.\n");
println(" a) If the first argument is sort_rows_of_size, then the remaining arguments");
println(" should be non-negative integers specifying the sizes of rows of strings");
println(" to be generated, permuted, and sorted.");
println(" b) Otherwise, the remaining arguments will be the elements of a row of strings");
println(" to be permuted and sorted.\n");
println("2. If the first command-line argument is search_for and the third argument");
println(" is in or in_rows_of_size, then the program will attempt to search for");
println(" the string passed as the second argument.\n");
println(" a) If the third argument is in_rows_of_size, then the remaining arguments");
println(" should be non-negative integers specifying the sizes of rows of strings");
println(" to be generated, permuted, and searched.");
println(" b) Otherwise, the remaining arguments will be the elements of a row of strings");
println(" to be permuted and searched.\n");
println("3. Otherwise, this documentation is printed.\n");
println("An exception will be thrown if an argument that should represent a row size is not");
println("an integer or if the integral value is too large or too small.\n");
println("This version is not fully implemented. It doesn't support searching yet.");
}
}
}
/*********************************************************************************************************/
/* A SHORT ROW TO HOE: a title devised to avoid the word EXERCISES, which many students find frightening */
/*********************************************************************************************************/
/* 0. Use this program to sort the row: 503 087 512 061 908 170 897 275 653 426 154 509 612 677 765 703
* Use it to sort permutations of rows of size: 0 1 2 3 4 ...
* Use it to sort permutations of rows of size: 1 2 4 8 16 ...
*
* Copy the program’s output and paste it into a spreadsheet application like Excel or Libre Office
* Calc. Use pivot tables and charts to study how execution time, comparison counts, and exchange
* counts vary across different sorting algorithms and row sizes. What did you learn? Did anything
* surprise you? Were any unanswered questions raised? If so, which question is most interesting to
* you, and how might you go about trying to find an answer? Where did those random numbers come from?
*
* 1. What is the largest row size you can sort permutations of using the program as it is written? How can
* you change the program to sort permutations of larger rows? What is the largest row size you can sort
* permutations of if you are only allowed to modify the AnalysisOfAlgorithms class? What did you change?
*
* 2. Using a slightly modified version of this program (you may modify the AnalysisOfAlgorithms class),
* what is the maximum number of times per sorting algorithm that you can sort the sixteen three-digit
* numbers shown above in exercise 1? What limits you? Your own patience? Some other finite resource?
*
* 3. How do the sorting algorithms implemented by the program compare in terms of execution time,
* comparison counts, and exchange counts? Which algorithm is best? Which algorithm is worst? Which
* algorithm is the most predictable? Which is the least predictable? What assumptions are you making?
*
* 4. How does the best algorithm compare to the second best algorithm? Define best. Can you make any
* improvements to either one? Can you implement an algorithm that is better than the best one? Are
* you encouraged or discouraged by what you’ve discovered? Are you motivated? In what sense?
*
* 5. Run this program on different computers. Try using different online Java IDEs. What can you learn
* about the capabilities of different platforms using this program? What did you learn?
*
* 6. Enhance this program so that it can be used, per the program’s documentation, to analyze searching
* algorithms in addition to sorting algorithms. Use the search-related interfaces provided, and
* extend the AbstractComparisonAlgorithmAnalyzer class. (Use the AbstractSorterAnalyzer class as
* an example.) Implement binary and linear search algorithms, and use the enhanced program to
* analyze your algorithms. What is an elephant in Cairo, and why is this question relevant?
*
* 7. List the basic features and capabilities of the Java programming language used by this program.
* What basic features are not used? What advanced features does this program use? Explain
* what the following terms mean and provide examples from this program of each: interface, class
* constructor, local variable, constant variable, instance variable, class variable, parameter
* argument, lambda expression, inheritance, subtyping, subclassing, polymorphism, method overriding,
* method overloading, lambda expression, generics, recursion. Cite your sources.
*
* 8. On page 453, the authors of Introduction to Computer Science: An Interdisiplinary Approach (2017)
* say that subclassing is controversial because its advantages over subtyping are debatable. What are
* some advantages and disadvantages of subclassing? Is the use of subclassing in this program safe?
* Is it justified? Why? How can subclassing be used responsibly? How can it be prevented? Should
* CounterMayThrowException be final? Which of the overloaded versions of Collections.sort exemplifies
* dependency injection? How are dependency injection and subtyping related? Is dependency injection
* dangerous? Is SortShuffler.shuffle using or being reckless with Collections.sort?
*
* 9. Is Java too full of rules and constraints? Do you value creativity over predictability?
*/