The rabbit problem, from a math
competition in
from http://plus.maths.org/issue3/fibonacci
Beginning
with a single pair of rabbits, if every
month each productive
pair
bears a new pair, which becomes productive when they are 1 month old,
how many
rabbits will there be after n months?
Answer:
Imagine
that there are xn pairs
of rabbits after n months. The number of pairs in
month n+1 will
be xn (in this problem,
rabbits never die) plus the number of
new pairs born. But new pairs are only born to pairs at least 1 month
old, so
there will be xn-1
new pairs.
xn+1 = xn
+ xn-1
Which
is simply the rule for generating the Fibonacci numbers, along with the
initial
condition that x = 0 at n=0 and x=1 at n=1.
This is the same formula as in Weiss, with the subscripts translated: Fn = Fn-1 + Fn-2
This is easily turned into a recursive function, as shown in Figure 7.6. However, this is a very expensive recursion, that recalculates many things over and over, as indicated by Figure 7.7.
The telltale sign of trouble is the “double recursion”, the fact that fib() calls itself twice during one execution of fib(). This makes its call tree very bushy, as Figure 7.7 shows. Such recursion is exponential in T(n), T(n) = O(p^n) for some power-base p. You see this in lab 4.
There is no need for recursion in calculating these numbers. For n >= 2, do this:
int
prev = 0;
int curr = 1;
int newnum;
for (int i = 2; i <= n; i++) {
newnum = curr + prev;
// fib(i) = fib(i-1) + fib(i-2)
prev = curr; //
cycle numbers down
curr = newnum;
}
This is only O(n), a big improvement!
Look at the recursive binary search function, pg. 246, and compare it to the iterative version on pg. 168. We see that both have the same idea for finding x in the array: look at the middle array element vs. x, and focus on the left half of the (currently-active part of the ) array if x < a[mid] and the right half if x > a[mid] and return on equality.
Example: a[] = {2, 4, 10, 20};
x=2
binarySearch(a, 2, 0, 3)
mid = 0+3/2 = 1
a[mid] = 4, .compareTo(1) is >
binarySearch(a, 2, 0, 0)
mid = 0, a[mid] = 2, return 0;
Clearly we expect the iterative version to run faster, since it doesn’t have to push local variables on the stack as it runs. Both are O(log n), but the constant for the recursive version would be higher in T(n) = C log n, approximately, for large n.
Note that Fig. 7.11 shows two functions to do the job: the top-level function that takes in the whole array, and a recursion helper function that can handle any specific part of the array, since the recursion needs to focus on smaller and smaller parts of the array.
Figuring out what to use for a recursion helper is challenging, and will not be required for this course. We’ll stick to the simpler cases where the original specified function can itself be used in the recursion. But it’s good to look at one example.
Rest of Term
Apr 4: Chap 15: Inner Classes and ArrayList
So far we have been using iterators provided by JDK container classes. Now we will see how to implement them.
Nested classes vs. Inner classses.
A nested class (or static inner class) is a class nested within a class, as we saw with the calculator program of pa03: See pg.407
public class Evaluator
{
...
private static class Token
{
...
private int type = EOL; <--field of nested class, use of "EOL" (pg. 408)
}
private static class EvalTokenizer
{...}
private static class Precedence
{...}
private static final int EOL = 0; <--static field of outer class (pg. 412)
private Stack<Integer>postfixStack; <--object field of outer class (pg. 407)
...
}
Note the "static class" on all the classes inside Evaluator--that makes them "nested classes."
We see that the nested class Token gets to see a private static field of the outer class, EOL. However, the code in Token has no reference to an Evaluator object needed to see the postfixStack, a non-static field of the outer class. If we gave it such an object ref, it could access this private object field of the outer class. See Figure 15.5 for such an example.
Nested classes work very much like unnested classes. Their objects have independent lives. We have one Evaluator object for a calculation and one EvalTokenizer along with many Token objects that come and go and a group of Precedence objects that live through the calculation. We could make Evaluator into a class that never creates an object and still have all these nested class objects.
When we drop the "static" on the enclosed classes, they become "inner classes", a little trickier. They no longer live independent lives but rather require an instance of the outer class. In other words, we have to make an outer class object, then an inner class object to go with it.
Inner classes: like bumps on a log
Here the log is some Java object, and the bumps are helper objects for it.
Can’t have the bumps without the log, and can’t have “inner class objects” without their outer object. (This is in contrast with nested classes.. Nested class objects can exist without any class object of the outer class existing—they are more like top-level objects.)
Example in non-container objects: a Robot class.
Suppose robots can have one or two arms that can move independently. You are writing the class Robot, and your users want to be able to move the arms by use of a simple method of Robot of the form: moveArmTo(int armNo, int x, int y, int z); for example, for Robot r, r.moveArmTo(1, 10, 20, 30) would move arm 1 to point (10, 20, 30).
However since robot arms have lots of common behavior, you want a class RobotArm to handle each arm. So a Robot object would have one or two RobotArm objects inside it to represent the arms, even though the users want to think of them as simply arm #1 and arm #2.
This is a situation calling for a private inner class for RobotArm. Each RobotArm is an integral part of a certain Robot. A certain Robot can have one or two RobotArm objects within it. The setup looks like this:
public class Robot
{
private RobotArm arm1, arm2;
private baseX, baseY, baseZ;
public Robot(int nArms)
{
arm1 = new RobotArm(1); // set up
sub-objects of Robot
if (nArms == 2)
arm2 = new RobotArm(2);
}
public moveArmTo(int armNo,
int x, int y, int z) {…}
private class RobotArm //
note no “static” keyword, so this is an “inner class”
{
private int id;
private int currentX, currentY, currentZ;
public RobotArm(int id) { this.id = id; currentX = baseX; ...
}
public moveTo(int
x, int y, int z) { … } // actually do move operation
}
}
Now no code outside Robot can refer to the RobotArm class directly.
They can call moveArmTo() of Robot, and this could be implemented as follows:
public moveArmTo(int
armNo, int x, int y, int z)
{
switch (armNo) {
case 1:
arm1.moveTo(x, y, z);
break;
case 2: arm2.moveTo(x,
y, z);
break;
default: …
}
}
An inner class object has a hidden ref to its outer class object. The code inside the RobotArm class can refer to private fields of Robot, and vice versa—it’s all a little club inside the outer class. Thus code in RobotArm arm1 can refer to Robot’s arm2, and code in Robot could use “arm1.id” to refer to a RobotArm id.
Note: we will only use private inner classes, to avoid the problem discussed on pg. 523.
Iterators using Inner Classes
We have seen above how private inner classes can represent sub-objects, but we saw that the users ended up not being able to directly access the sub-object. For an iterator, we want the using code to use the Iterator object directly. How to proceed?
Iterator is an interface, and so no objects of that exact type can be instantiated. In all cases of “Iterator objects”, we actually have an object that ISA Iterator, via inheritance.
The trick is to have a private inner class that implements Iterator,
but itself is of a class unknown to the using code.
This is the setup of the final simple example of Weiss, on pg. 522. You should add remove() to the implementation of LocalIterator to implement the real JDK Iterator interface. Apparently Weiss’s package has a simpler Iterator interface.
See Figure 15.9 for the simpler version of the code of Figure 15.8. Figure 15.9 shows how easy it is to refer to the outer object’s private fields (size, items) from the inner class code.