Overview
Why doesn’t my algorithm work?
Run it through by hand (with pictures).
Run it in the debugger.
Compare the outputs.
You have an array with three values, which we’ll call red, white, and blue. They are in no particular order. Your goal is to rearrange it so that all of the red are at the left, all the blue are at the right, and all the white are in between.
Sample input:
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| b | r | w | w | r | b | w | r | w | w | w | r | w | b | r | b | w |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Our invariant
+----------------+--------------+-------------+--------------+
| red | white | unknown | blue |
+----------------+--------------+-------------+--------------+
| | | | |
0 r i b length
Our algorithm
How do you make progress while maintaining the invariant?
swap(A, r++, i++);swap(A, i, --b);When do you stop? When i == b.
Question:
Initialization
Running the algorithm
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| B | R | W | W | R | B | W | R | W | W | W | R | W | B | W | B | R |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| |
0,i,r length,b
A[i] is B, swap with A[b-1], decrement b
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| R | R | W | W | R | B | W | R | W | W | W | R | W | B | W | B | B |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| | |
0,i,r b length
A[i] is R, `swap(A, r++, i++);`
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| R | R | W | W | R | B | W | R | W | W | W | R | W | B | W | B | B |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| | | |
0 i,r b length
A[i] is R, `swap(A, r++, i++);`
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| R | R | W | W | R | B | W | R | W | W | W | R | W | B | W | B | B |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| | | |
0 i,r b length
A[i] is W
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| R | R | W | W | R | B | W | R | W | W | W | R | W | B | W | B | B |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| | | | |
0 r i b length
Suppose we want a functional interface for a sorter for arrays. What does it look like?
public Interface Sorter1<T> {
/** Create a new sorted version of original. */
public T[] sort(T[] original, Comparator<? super T> comparator);
} // Interface Sorter1
What does ? super T mean? This means that we’ll accept anything that
is a superclass of T or equal to T. For example, if we have an object
comparator (Comparator<Object>), we can use that to sort an array
of strings. (String[] original). If we have a Person comparator,
we can use that to sort an array of Student objects.
What does ? extends T mean? It means we’ll accept any subclass of T,
or T.
public Interface Sorter2<T> {
/** Sort in place. */
public void (T[] original, Comparator<? super T> comparator);
} // Interface Sorter2
Sorter2 saves space.Can we translate between the two? That is, if someone writes a class that implements Sorter2, can you adapt it to implement Sorter1, or vice versa?
What do you make the preconditions of your sort method?
What do you make the postconditions of your sort method? (The one
that returns a new array.) Call the return value sorted.
comparator(sorted[i], sorted[i+1]) <= 0. (“reasonable” means
between 0 and length-2.)Breaking the implicit contract.
Can we meet these postconditions without achieving the underlying goal?
New additional postconditions
Breaking the implicit contract again. That is, can we return something that meets these three postconditions that doesn’t meet the implicit goal?
“Elements with the same value should stay in the same order.”
E.g., if we sort by name (last then first), alphabetical order
{("Sam" "Are" 121),("Jane" "Doe" 412),("Sam" "Are" 001),("Homer" "Doh" 123)}
Two possible results
{("Sam" "Are" 121),("Sam" "Are" 001),("Jane" "Doe" 412),("Homer" "Doh" 123)}
{("Sam" "Are" 001),("Sam" "Are" 121),("Jane" "Doe" 412),("Homer" "Doh" 123)}
The one that maintains the order of the two Sam Are’s is stable. The other is not.
How would you test systematically? (What are some things you would test in your systematic tests?)
assertEquals({1,2,2,2,3}, s.sort({2,2,2,1,3}), (a,b) -> a - b);
vs.
Comparator<Integer> lessthan = (a,b) a-b;
sorted = s.sort({2,2,2,1,3},lessthan);
assertTrue(isSorted(sorted, lessthan));
public static <T> boolean isSorted(T[] values, Comparator<? super T> comparator) {
for (int i = 0; i < values.length-1; i++) {
if (comparator.compare(values[i], values[i+1]) > 0) {
return false;
} // if
} // for
return true;
} // isSorted
Which do you prefer?
isSorted, and evidence suggests that even
Sam will get it wrong the first time, and the second time, and the third
time.How would you test “randomly”? (Don’t assume you have access to another
sort method.)
for (int i = 0; i < 100; i++) {
assertEquals(myArray, sorter.sort(randomPermutation(myArray)));
}
// Pre: n < Integer.MAX_VALUE/3
public static Integer[] randomSortedArray(int n) {
Random rng = new Random();
Integer[] values = new Integer[n];
values[0] = 500 - rng.nextInteger(1000);
for (int i = 1; i < n; i++) {
A[i] = A[i-1] + rng.nextInteger(3);
} // for
} // randomSortedArray
rsa = randomSortedArray(1000);
assertEquals(rsa, sorter.sort(permutation(rsa)), () -> Arrays.toString(rsa));
Idea: Divide the array into sorted and unsorted. Repeatedly insert the first element from the unsorted part into the sorted part.
Invariant?
Stable?
Idea: Divide the array into sorted and unsorted. Repeatedly insert the smallest element from the unsorted part at the end of the sorted part.
Invariant?
Stable?
Nope, there’s not time.