Overview
average
must use O(1) space.long
values, so the average is likely to be
slightly off if the sum of the values is not a multiple of the length.put
(or enqueue
) method.Did the morning section ask questions?
Yes. See their eboard for details.
What is the average of 2 and 3?
2, if we’re dealing with longs.
Are we getting graded work back soon?
Yes. Hopefully tonight for a few homework assignments, Wednesday for quizzes.
Do we have to implement remove for problem 3 or problem 4?
Both.
How much can we be off in the average
method?
abs(average*length - sum) < length
How should we initialize our arrays for testing?
Here are some approaches. Don’t use them all at once.
for (int i = 0; i < a.length; i++) {
a[i] = 3;
}
for (int i = 0; i < a.length; i++) {
a[i] = i;
} // average is (length-1)/2, or so Sam thinks
for (int i = 0; i < a.length; i++) {
a[i] = Long.MAX_VALUE - i;
} // average is
for (int i = 0; i < a.length; i++) {
a[i] = Long.MIN_VALUE + i;
}
randomlyPermute(a);
randomlyIncrementAndDecrement(a);
I’m also a firm believer in nesting for loops.
Are you sure about those calculations?
Reasonably.
Suppose length is n. We are adding 0 + 1 + … + n-1. Sum is (n-1)n/2. If there are n elements, the average value is (n-1)n/2n = (n-1)/2
What should we do if we don’t know the average of the array?
I would not recommend that you build arrays whose average you do not know.
Or you could write an average that uses BigIntegers and compare the results of that one to yours.
Yay! Big-O. (or Oh, big-Yay!)
Experience suggests that people have difficulty writing correct code.
We need strategies for
A variety of techniques have been developed to deal with this issue, we’re looking at a high-level, general approach.
Often, our reasoning is based on the “state” of the system (stack and heap; settings of the various variables and parameters).
We know the state of the program at the start of a chunk of code (e.g., the start of a procedure). We know because we have done other analysis or have preconditions.
We have a goal state at the end of the chunk of code.
We show that the instructions we give go from one to the other.
Sometimes we start with the goal and work backwards.
E.g., our goal is that z is the greater of x and y.
know: x is an integer, y is an integer, z?
if (y > x) {
z = y
// z = y
// z >= y
// z > x (because of transitivity of >)
} else {
// x >= y
z = x;
// z = x, z >= x, z >= y (by transitivity)
}
goal: z is an integer, z >= x, z >= y, z = x or z = y
Lots of errors happen in loops. Loop invariants are our tool.
A loop invariant is a statement about the state of the system
Here’s a useful thing to say about an array during sorting.
+-------------------+------------------+
| sorted | unprocessed |
+-------------------+------------------+
| | |
0 i length
If I can maintain that invariant while increasing i repeatedly, I will sort the array.
Of course, not all loops terminate.
We also want to “prove” that our loops terminate. Measure the “size” of the remaining work and show that it decreases in each iteration of the loop.
Termination plus loop invariant generally guarantee our goal.
Input:
Goal: Rearrange the array so that all the values <= pivot are on the left and all the values > pivot are on the right. We want to do this efficiently O(n).
Return: The number of values <= the pivot.
Questions:
+--------------+--------------------------------------+------------+
| <= pivot | unprocessed | > pivot |
+--------------+--------------------------------------+------------+
| | | |
0 p r length
+----------+--------------+----------------------------------------+
| <= pivot | > pivot | unprocessed |
+----------+--------------+----------------------------------------+
| | | |
0 s i length
+----------------+----------------+--------------+-----------------+
| unprocessed | <= pivot | > pivot | unprocessed |
+----------------+----------------+--------------+-----------------+
| | | | |
0 length
We’re working with this one.
+--------------+--------------------------------------+------------+
| <= pivot | unprocessed | > pivot |
+--------------+--------------------------------------+------------+
| | | |
0 p r length
How can we move p and r? (Alternately, what can you do with the knowledge of the values at p and r-1?)
When does this terminate? When p and r are equal.
Can we argue that they never cross? If we are down to one element, it has to be one of the first two cases, so we will never have a case in which we increment p and decrement r.
We can return p or r.
How do we initialize p and r so that the invariant holds when we start? We can let p = 0 and r be length.
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.
Input:
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| r | b | w | w | r | b | w | r | w | w | w | r | w | b | r | b | w |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Output
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| r | r | r | r | r | w | w | w | w | w | w | w | w | b | b | b | b |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Invariant
+---------+------------------------------------------+-------------+
| red | unprocessed | not red |
+---------+------------------------------------------+-------------+
In effect, we run the partition algorithm twice, once to separate red and not red and once to separate white and blue.
+---------+-----+---------------------------+--------+-------------+
| red |white|unprocessed | white | blue |
+---------+-----+---------------------------+--------+-------------+
+----------+-----------+-------------------------------+-----------+
| red | white | unknown | blue |
+----------+-----------+-------------------------------+-----------+
| | | | |
0 r i b length