# Outline of Class 20: Recursion

Held: Friday, February 20, 1998

## Recursion

• In a typical algorithmic language there are many ways of expressing repetition.
• For many people (particularly for many mathematicians), a common way of expressing repetition is recursion.
• A recursive function is a function that (sometimes) calls itself.
• In some ways, this is like the legendary dragon/worm that eats its own tail.
• However, it is a natural way to represent repetition.
• Consider the task of sorting a list (ordering the elements so that the smallest element is first, the next smallest is second, and so on and so forth). One way to do this is:
• Remove the smallest element from the list.
• Sort the rest of the list.
• Put the smallest element back at the front of the list.
• How do we sort the rest of the list? Using the same procedure.
• Remove the smallest element from the remaining elements.
• Sort the rest of the list.
• Put the smallest element back at the front of the list.
• How do we sort the list that is now two elements shorter? The same way.
• Eventually, we wind up with a zero-length list, which is sorted by definition.
• We might write this in pseudocode as
```public List sort(List l)
{
if (l.empty()) {
return new List();
}
else {
Object smallest = l.deleteSmallest();
List sorted = sort(l);
sorted.prepend(smallest);
return sorted;
}
} // sort(List)
```
• We can define the mathematical factorial function in a similar way.
• What is `N!`? It's `N * (N-1)!`
• What is `(N-1)!`? It's `(N-1) * (N-2)!`
• ...
• What is `0!`? It's `1`.
• In pseudocode
```public int factorial(int N)
throws Exception
{
if (n < 0) {
throw new Exception("Cannot take factorial of negative numbers.");
}
else if (n == 0) {
return 1;
}
else {
return n * factorial(n-1);
}
} // factorial
```
• Note that both of these functions had a common form.
• A base case indicates what you do for simple inputs.
• A recursive case says what you do for more complex inputs and involves another call to the same function, but with different parameters. (Hopefully, a "smaller" parameter.)
• In pseudocode
```public returnType recursiveAlgorithm(someType input)
{
if (baseCase(input)) {
return baseComputation(input);
}
else {
return computation(input, recursiveAlgorithm(simplify(input)));
}
} // recursiveAlgorithm(someType)
```

### Analysis of Recursive Functions

• How do we determine the running time of a recursive algorithm? In a way similar to the way in which we determine the running time of an iterative algorithm.
• Count the number of recursive calls.
• Count the time spent during each recursive call (except for the time spent in subsequent recursive calls).
• How do you count the number of recursive calls? Often, the "shrink" function gives you a sense of how many recursive calls you need to do (how many calls to "shrink" does it take to reduce the argument to the base case).
• It is also possible to use the concept of recurrence relations to determine the running time of an algorithm.
• For example, if a recursive algorithm takes c steps and reduces the parameter by one, we might express the running time as a function, `time(N)` which represents the running time on input of N elements:
`time(N) = c + time(N-1)`
`time(1) = c`
• This equation is relatively easy to solve "intuitively".
• time(N) = c + time(N-1)
• c + c + time(N-1)
• ...
• c + c + ... + time(1)
• c + c + ... + c (N times)
• c*N
• Other interesting recurrence relations:
• time(N) = N + time(N-1)
• time(N) = c + time(N/2)
• time(N) = N + time(N/2)
• time(N) = c * time(N-1)
• time(N) = c * time(N/2)

### 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
* pre: n >= 0
* pre: x > 0
* pre: x^n < maxint (can't really check this, but ...)
* post: 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 recursively as
```public int exp(int x, int n)
{
if (n == 0) return 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;
// Choose what to do based on the exponent
if (n == 0) {
// x^0 = 1 for x != 0
return 1;
} // n is 0
else if (even(n)) {
// x^(2k) = x^k * x^k
tmp = exp(x, n/2);
return tmp*tmp;
} // n is even
else { // n is odd
// x^(k+1) = x * x^k
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?

On to Design
Back to Algorithm Analysis, Continued
Outlines: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
Current position in syllabus

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.

Source text last modified Tue Jan 12 11:52:22 1999.

This page generated on Mon Jan 25 09:49:12 1999 by SiteWeaver. Validate this page.

Contact our webmaster at rebelsky@math.grin.edu