- Don't forget the talk by Johnny Wong tomorrow. It's on agents, and looks to be quite interesting.
- Any questions on assignment 4?
- I've coded a variant of Bailey's recursive stamps program.

- As those of you who took the scheme-based 151 know, recursive expression of algorithms is often the most natural.
- A typical recursive algorithm has
- A
*base case*: what to do for simple inputs - A
*recursive case*: what to do for more complex inputs, involves a call to the same algorithm, using a simpler input.

- A
- In pseudocode,
public

*returnType*recursiveAlgorithm(*someType*input) {*someType*simpler_input; if (baseCase(input)) { return baseComputation(input); } else { simpler_input = simplify(input); return computation(input, recursiveAlgorithm(simpler_input)); } // recursive case } // recursiveAlgorithm - For example, to express a solution to
`n!`

, the factorial of n, we might write:public int factorial(int n) { if (n == 0) { return 1; } else { return n*factorial(n-1); } } // factorial

- Some people view recursion simply as a mechanism for looping. While it is that, it is also a way of attacking problems.
- It is often the case that a recursive algorithm is more efficient than the corresponding looping algorithm (at least for "first designs" of algorithms).
- Consider the problem of computing x^n for some integers x and n.
- Nonrecursive:
/** * 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

- This has a running time of O(n)
- Recursive:
public int exp(int x, int n) { int tmp; // Temporary value, used for int. calcs. 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

- Sometimes, we write
*mutually-recursive*algorithms, in which one algorithm call another, which calls the first, and they go back-and-forth until reaching a base-case. - These algorithms are still considered recursive, and you use similar analysis techniques to determine their running times.

- Bailey illustrates some recursive principles with an algorithm that computes the minimum number of stamps that make up a particular value.

- Here's one algorithm for computing the minimum number of stamps
needed to make up a particular price.
- For each stamp value, you assume that you can use one stamp of that value, and then compute the minimum number of stamps for the remaining price.
- If you minimize this count over all values, you get the minimum count.

- Here's a version of the algorithm in java
public int minimum_stamps(int price, int[] counts) { int min = -1; // The minimum int submin; // The minimum for a smaller price int[] subcounts = new int[stamp_values.length]; // Counts for smaller price // Sanity check if (price == 0) { for(int i = 0; i < stamp_values.length; ++i) { counts[i] = 0; } //for return 0; } // Try each stamp value for(int i = 0; i < stamp_values.length; ++i) { if (price >= stamp_values[i]) { submin = minimum_stamps(price-stamp_values[i],subcounts); // If it's a valid number of stamps, and "better" than our // previous best, update our best if ( (submin > -1) && ( (submin+1 < min) || (min == -1) ) ) { min = submin+1; for(int j = 0; j < stamp_values.length; ++j) { counts[j] = subcounts[j]; } // for counts[i] += 1; } // A better value } // if the value is smaller than the price } // for // That's it, we're done return min; } // minimum_stamps

- Unfortunately, this is a relatively inefficient algorithm. Why?
Consider the task of computing how many stamps are needed to make
up 10 cents, using 1, 2, and 5 cent stamps.
- That's 1 + (calls to solve 10-1 = 9 cents) + (calls to solve 10-2 = 8 cents) + (calls to solve 10-2 = 5 cents)
- Calls to solve 9 cents: 1 + (calls to solve 9-1 = 8 cents) + (calls to solve 9-2 = 7 cents) + (calls to solve 9-5 = 4 cents)
- Calls to solve 8 cents: 1 + (calls to solve 8-1 = 7 cents) + (calls to solve 8-2 = 6 cents) + (calls to solve 8-5 = 3 cents)
- Calls to solve 7 cents: 1 + (calls to solve 7-1 = 6 cents) + (calls to solve 7-2 = 5 cents) + (calls to solve 7-5 = 2 cents)
- Calls to solve 6 cents: 1 + (calls to solve 6-1 = 5 cents) + (calls to solve 6-2 = 4 cents) + (calls to solve 6-5 = 1 cent)
- Calls to solve 5 cents: 1 + (calls to solve 5-1 = 4 cents) + (calls to solve 5-2 = 3 cents) + (calls to solve 5-5 = 0 cents)
- Calls to solve 4 cents: 1 + (calls to solve 4-1 = 3 cents) + (calls to solve 4-2 = 2 cents)
- Calls to solve 3 cents: 1 + (calls to solve 3-1 = 2 cents) + (calls to solve 3-2 = 1 cent)
- Calls to solve 2 cents: 1 + (calls to solve 2-1 = 1 cent) + (calls to solve 2-2 = 0 cents)
- Calls to solve 1 cent: 1 + (calls to solve 1-1 = 0 cents)
- Calls to solve 0 cents: 1

- Working our way back up
- Calls to solve 0 cents: 1
- Calls to solve 1 cent: 2
- Calls to solve 2 cents: 4 (= 1 + 2 + 1)
- Calls to solve 3 cents: 7 (= 1 + 4 + 2)
- Calls to solve 4 cents: 12 (= 1 + 7 + 4)
- Calls to solve 5 cents: 21 (= 1 + 12 + 7 + 1)
- Calls to solve 6 cents: 36 (= 1 + 21 + 12 + 2)
- Calls to solve 7 cents: 62 (= 1 + 36 + 21 + 4)
- Calls to solve 8 cents: 106 (= 1 + 62 + 36 + 7)
- Calls to solve 9 cents: 181 (= 1 + 106 + 62 + 12)
- Calls to solve 10 cents: 309 (= 1 + 181 + 106 + 21)

- That's disgustingly large. Can you come up with an approximation?
- It's also inefficient, as we are clearly repeating work.
- There are a number of strategies for avoiding repeated work.

**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 Oct 7 17:12:58 1997.

This page generated on Wed Nov 5 12:38:38 1997 by SiteWeaver.

Contact our webmaster at rebelsky@math.grin.edu