# Class 18: Recursion, Revisited

Back to Binary Search. On to A Stamp Problem.

Held Tuesday, February 23

Summary

• Selected recursive algorithms
• Factorial
• Exponentiation
• Algorithm design techniques
• Divide and conquer
• Reading: Java Plus Data Structures, Chapter 5: Recursion.

Contents

Notes

• As you have guessed, I'm falling somewhat behind on my writing. I apologize for that. However, I think that most of you are busy enough that you may be happy not to have to read the book :-)
• Are there any questions on the exam?
• Mr. Flynt has designed some solutions for assignment 4
• `FractionTest.java`
• `Fraction.java`
• `Alert.java`

## Recursion

• As you've seen, recursion is a powerful algorithm design technique. Today we'll consider a few more interesting recursive algorithms as well as a technique for improving the efficiency of recursive algorithms.
• If there's time, we'll also consider some general program design strategies.

## 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, including local variables) 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 implement tail-recursive programs.
• A tail-recursive algorithm does no work after any recursive call.
• Tail recursive algorithms have the form
```public returnType tailRecursiveAlgorithm(someType input, someType extraInfo) {
someType simpler_input;

if (baseCase(input)) {
return baseComputation(input);
}
else {
simpler_input = simplify(input);
return tailRecursiveAlgorithm(simpler_input,more_stuff));
} // recursive case
} // tailRecursiveAlgorithm
```
• Recall that the more general form is
```public returnType recursiveAlgorithm(someType input, someType extraInfo) {
someType simplerInput;
someType recursiveResult;
if (baseCase(input)) {
return baseComputation(input);
}
else {
simplerInput = simplify(input);
moreStuff = doSomeNonrecursiveComputations(input, extraInfo);
recursiveResult = recursiveAlgorithm(simplerInput,moreStuff));
return doMoreWith(recursiveResult);
} // recursive case
} // recursiveAlgorithm
```
• Yes, the recursive call often tends to look more like
```    return doMoreWith(recursiveAlgorithm(simplify(input), ...));
```
• Tail-recursive algorithms can be implemented by using one stack frame. In a recursive call, you simply update the parameters and jump to the start of the code.
• Scheme requires compilers to implement tail-recursion efficiently.
• Java may not (although optimizing Java compilers seem to).
• Nonetheless, it's good practice to think about how to make functions tail-recursive (or at least whether they're tail-recursive).

### A tail-recursive formulation of the factorial function

• Is the common factorial function tail-recursive? Let's consider the ``normal'' expression of that function.
```/**
* Compute n!
* <br>Precondition: n >= 0
* <br>Precondition: n! <= Integer.MAX_VALUE
* <br>Postcondition: returns n!
*/
public int factorial(int n) {
// Base case.  0! = 1.
if (n == 0) return 1;
// Recursive case.  n! = n * ((n-1)!)
else return n * factorial(n-1);
} // factorial(int)
```
• This isn't tail-recursive because after the recursive call to `factorial` we multiply by n.
• How do we make it tail-recursive? We need to deal with that multiplication. We'll add an accumulator to gather the multiplications that we would normally have done after the recursive call.
• The accumulator is a parameter to the tail-recursive factorial.
• Here's a tail-recursive version of factorial (with helper function).
```/**
* Compute n!
* <br>Precondition: n >= 0
* <br>Precondition: n! <= Integer.MAX_VALUE
* <br>Postcondition: returns n!
*/
public int factorial(int n) {
return factorial(n, 1);
} // factorial(int)
/**
* Compute acc*n!
* <br>Precondition: n >= 0
* <br>Precondition: (acc*n!) <= Integer.MAX_VALUE
* <br>Postcondition: returns n!
*/
public int factorial(int n, int acc)
{
// Base case.  acc*0! = acc*1 = acc.
if (n == 0) return acc;
// Recursive case. acc*(n!) = (acc*n) * ((n-1)!)
else return factorial(n-1, acc*n);
} // factorial(int,int)
```

## Exponentiation

• Consider the problem of computing x^n for integers x and n (n should be non-negative).
• Here's a simple iterative solution
```/**
* Compute x^n for integers x and n
* <br>Precondition: n >= 0
* <br>Precondition: x > 0
* <br>Precondition: x^n <= Integer.MAX_VALUE (can't really check this, but ...)
* <br>Postcondition: returns x^n
*/
public int exp(int x, int n) {
int result = 1; // The result, computed as we go
for(int i = 0; i < n; ++i) {
result *= x;
}
return result;
} // exp(int, int)
```
• This has a running time of O(n).
• We could rewrite it also rewrite it recursively as
```public int exp(int x, int n) {
// Base case.  x^0 = 1.
if (n == 0) return 1;
// Recursive case.  x^n = x * x^(n-1).
else return x * exp(x, n-1);
} // exp(int, int)
```
• However, writing it recursively might lead us to think about other ways to take advantage of recursion. For example, we might note that if the power is even, then we can split the power in half. That is, x^(2k) = x^k * x^k.
```public int exp(int x, int n) {
// Temporary value, used for integer calculations.
int tmp;
// Base case.  x^0 = 1.
if (n == 0) return 1.
// Recursive case.  x^(2k) = x^k * x^k.
else if (even(n)) {
tmp = exp(x, n/2);
return tmp*tmp;
} // n is even
// Recursive case.  x^(k+1) = x * x^k
else { // n is odd
tmp = exp(x, n-1);
return x*tmp;
} // n is odd
} // exp
```
• This has running time O(log_2(n)).
• Can you figure out how to write this iteratively?
• Can you figure out how to write it tail-recursively?

History

• Created Monday, January 11, 1999.
• Added short summary on Friday, January 22, 1999.
• Filled in the details on Tuesday, February 23, 1999. Some of these details were taken from outline 20 and outline 22 of CS152 98S.

Back to Binary Search. On to A Stamp Problem.

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.

This page may be found at http://www.math.grin.edu/~rebelsky/Courses/CS152/99S/Outlines/outline.18.html

Source text last modified Tue Feb 23 14:34:04 1999.

This page generated on Tue Feb 23 14:59:01 1999 by SiteWeaver. Validate this page's HTML.

Contact our webmaster at rebelsky@math.grin.edu