Fundamentals of Computer Science II (CSC-152 97F)

[News] [Basics] [Syllabus] [Outlines] [Assignments] [Examples] [Readings] [Bailey Docs]

# Outline of Class 20: Recursion, continued

## Miscellaneous

• Today is bribe day in 152. Ask a good question or make a good comment, and you get a piece of candy.
• Since the last assignment was so long, I'm going to wait a little before giving you the next assignment, and it won't be due until the 16th.
• Don't forget that we have a test on the 17th.
• Warning! My sample sorting code has not yet been tested. Hopefully, I'll have tests working next week.

## Rethinking the stamps problem

• As we noted, our solution to the stamps problem is relatively inefficient.
• Why? In part, because we end up calling the same function with the same arguments again and again and again and again.
• If we could cache (remember) those intermediate results, then we could significantly affect the running time of our algorithm.

### Dynamic Programming

• In many recursively defined functions, it turns out that you end up making the same function call (that is, a call to the same function with the same arguements) many times.

• Consider the typical solution to the problem of finding the nth Fibonacci number.
• The Fibonacci numbers are defined as:
• 1 is the 0th Fibonacci number
• 1 is the 1st Fibonacci number
• The nth Fibonacci number is the n-1st Fibonacci number plus the n-2nd Fibonacci number.
• The sequence begins 1, 1, 2, 3, 5, 8, 13, 21, ...
• (The ratio of the n+1st to nth Fibonacci number approaches the golden ratio.)
• One might code this as
```/**
Compute the nth Fibonacci number.
pre: n >= 0
pre: fib(n) <= maximum integer
post: returns the nth fibonacci number
*/
public static int fib(int n) {
// Base case
if ((n == 0) || (n == 1)) {
return 1;
} // base case
// Recursive case
else {
return fib(n-1) + fib(n-2);
} // recursive case
} // fib(n)
```
• Unfortunately, this has a lot of repeated work. To compute fib(8) we have one call to fib(7) + one call to fib(6)
• Which is two calls to fib(6) + one call to fib(5)
• Which is three calls to fib(5) + two calls to fib(4)
• Which is five calls to fib(4) + three calls to fib(3)
• Which is eight calls to fib(3) + five calls to fib(2)
• Which is thirteen calls to fib(2) + eight calls to fib(1)
• Which is twenty-one calls to fib(1) + thirteen calls to fib(0)
• Which is 34 calls to the base case
• This is clearly inefficient.
• But our analysis gives us a clue as to a better way to compute the answer: build an array of solutions, and work our way up.
```/**
Compute the nth Fibonacci number.
pre: n >= 0
pre: fib(n) <= maximum integer
post: returns the nth fibonacci number
*/
public static int newfib(int n) {
int[] solutions = new int[n+1];
// Fill in the intial solutions
solutions[0] = 1;
solutions[1] = 1;
// Fill in the remaining solutions
for(int i = 2; i <=n; ++i) {
solutions[i] = solutions[i-1] + solutions[i-2]
} // for
// That's it
return solutions[n];
} // newfib
```
• Of course, this isn't recursive. If we wanted to rewrite it recursively, we might pass the array of solutions to our recursive calls.
```/**
Compute the nth Fibonacci number.
pre: n >= 0
pre: fib(n) <= maximum integer
post: returns the nth fibonacci number
*/
public static int newerfib(int n) {
int[] solutions = new int[n+1];
// We'll use 0 to indicate "no solution yet"
for(int i = 0; i <= n; ++i) {
solutions[i] = 0;
}
// Compute the fibonacci using this helper array

/**
Compute the nth Fibonacci number.
pre: n >= 0
pre: fib(n) <= maximum integer
pre: solutions.length >= n+1
post: returns the nth fibonacci number
*/
public static int newerfib(int n, int[] solutions) {
// Deal with precomputed solutions.  Note that return
// immediately exits the routine, so I don't need an
// else clause.
if (solutions[n] != 0) {
return solutions[n];
}
// Base case
if ((n == 0) || (n == 1)) {
solutions[n] = 1;
} // base case
// Recursive case
else {
} // recursive case
return solutions[n];
```
• What's the new running time? It's O(n). However, this may require more space, as we need to allocate an array of size theta(n).
• Yes, there are other ways to optimize the Fibonacci calculuation. For example, it turns out that you can simple keep track of the current two values.

• In the technique of dynamic programming, you use arrays of cached solutions, as above. It turns out that this is a good solution technique for many classes of problems, particularly problems that require us to consider a number of variations.

### Improved Solution

• So, we can improve our solution by passing around an array of solutions. This brings our running time down from exponential to linear.
• Note that the stamps problem is, in effect, only a slight variation of the problem of computing Fibonacci numbers.

## Tail recursion

• Some "practical programmers" consider recursion "inefficient". Why? Because in a standard implementation of a programming language, it is necessary to put a stack frame (information on the current invocation of a function) in memory for each function call, and to remove it from memory at the completion of the function call.
• However, many programming languages can more efficiently represent tail-recursion.
• A tail-recursive algorithm does no work after the recursive call.
• It has this form:
``` public returnType tailRecursiveAlgorithm(someType input, SomeType extraInfo) {
someType simpler_input;

if (baseCase(input)) {
return baseComputation(input);
}
else {
simpler_input = simplify(input);
return recursiveAlgorithm(simpler_input,more_stuff));
} // recursive case
} // tailRecursiveAlgorithm
```
• Here's a tail-recursive version of factorial (with a helper function). It uses a separate "accumulator" to gather the multiplications that we would normally have done after the recursive call.
``` public int factorial(int n) {
return tr_factorial(n, 1);
}
public int tr_factorial(int n, int acc) {
if (n == 0) {
return acc;
}
else {
return tr_factorial(n-1, acc*n);
}
} // tr_factorial
```
• Can you write a tail-recursive version of our exponent function?

Outlines: prev next

[News] [Basics] [Syllabus] [Outlines] [Assignments] [Examples] [Readings] [Bailey Docs]

Disclaimer Often, these pages were created "on the fly" with little, if any, proofreading. Any or all of the information on the pages may be incorrect. Please contact me if you notice errors.