[Instructions] [Search] [Current] [Syllabus] [Links] [Handouts] [Outlines] [Labs] [More Labs] [Assignments] [Quizzes] [Examples] [Book] [Tutorial] [API]

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

- 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.

- 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), ...));

- Yes, the recursive call often tends to look more like
- 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).

- 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)**

- 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.

[Instructions] [Search] [Current] [Syllabus] [Links] [Handouts] [Outlines] [Labs] [More Labs] [Assignments] [Quizzes] [Examples] [Book] [Tutorial] [API]

**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