View
1
Download
0
Category
Preview:
Citation preview
Recursion
4-11-2013
Clarkson Career Center
“Technical Careers: Preparation & Opportunities”
Alumni to Student Program:
CS, DA&S, Comm & Media, ISBP, SoftEng
Thursday, April 18th , 4:00 pm to 7:00 pm
(panel discussions, roundtables, … details to follow)
Recursion
Reading: Maciel
Chapter 14 Recursion
Chapter 15 Sorting
Project#2: Evil Hangman, due Wed. 4/24
see the sample output
To err is human, to forgive divine.
Alexander Pope, An Essay on Criticism, English poet and satirist (1688 - 1744)
To iterate is human, to recurse, divine.
L. Peter Deutsch, computer scientist, or ....
Robert Heller, computer scientist, or ....
unknown ....
Recursion is:
A problem-solving approach, that can ...
generate simple solutions to ...
certain kinds of problems that ...
would be difficult to solve in other ways
Recursion splits a problem:
into one or more simpler versions of itself
Strategy for processing nested dolls:
1.if there is only one doll
2. do what it needed for it
else
3. do what is needed for the outer doll
4. Process the inner nest in the same way
if problem is “small enough”
solve it directly
else
break into one or more smaller subproblems
solve each subproblem recursively
combine results into solution to whole problem
At least one “small” case that you can solve directly
A way of breaking a larger problem down into:
One or more smaller subproblems
Each of the same kind as the original
A way of combining subproblem results into an overall solution to the larger problem
1. The algorithm has at least one base case; one where the problem is solved directly, without a recursive call.
2. Every recursive call gets closer to a base case, in such a way that a base case will eventually be reached.
3. The algorithm works when you assume that the recursive call works.
The first two properties guarantee that the algorithm will eventually terminate. The third property guarantees that the algorithm solves the problem correctly.
// print n copies of the character c
void print( int n , char c ) {
for (int i = 0; i < n; i++ ) {
cout << c;
}
cout << endl;
}
Figure 14.1: A simple iterative algorithm
(Maciel, p. 245)
// print n copies of the character c
void print( int n , char c ) {
base case: no characters to print
cout << endl;
recursive case:
print character c
print (n-1) copies of c
cout << c;
print ( n-1, c )
n is zero
n > 0
// print n copies of the character c
void print( int n , char c ) {
if ( n > 0 ) { // recursive case
cout << c;
print( n-1, c );
} else { // base case
cout << endl;
Figure 14.2: A recursive algorithm (Maciel, p. 245)
recursive case:
print character c
print (n-1) copies of c
// print n copies of the character c
void print( int n , char c ) {
if ( n > 0 ) {
cout << c;
print( n-1, c );
} else {
cout << endl;
1. The algorithm has at least one base case
2. Every recursive call gets closer to a base case, in such a way that a base case will eventually be reached.
3. The algorithm works when you assume that the recursive call works.
√
√
√
Don’t be concerned about how recursion works. Use the definition of what the function is designed to do, ignoring implementation.
Typically an “if” is used to select between base cases and recursive cases.
Make sure that any recursive call is only made on part of the original parameters. This guarantees termination.
Using recursion in two places results in a program that would be very difficult to write iteratively.
// print n copies of the character c
void print( int n , char c )
print(0, ‘X’) correctly prints nothing
print(1, ‘X’) correctly prints 1 X, given that
print (0, ‘X’) correctly nothing
print(2, ‘X’) correctly prints 2 X’s, given that
print (1, ‘X’) correctly prints 1 X
. . .
print(n, ‘X’) correctly prints n X’s, given that
print (n-1, ‘X’) correctly prints (n-1) X’s
Principle of Mathematical Induction
Notes done in class
main advantage: recursive algorithms can be simpler than non-recursive algorithms that solve the same problem => easier to design, understand, implement and modify
Some good examples are efficient sorting algorithms
main disadvantage: overhead of function calls (which take more time and more space)
The additional time is usually not very significant, but the amount of space is proportional to the number of recursive calls. “Tail-recursive” solutions are very efficient.
/* Compute n!, n >= 0 */
solution 1: Iterative
int iFact (int n) {
int result = 1;
for (int k = 1; k <= n; k++) {
result = result * k;
}
return result;
}
solution 2: Recursive
solution 3: Tail-recursive
Notes done in class
0! = 1
n! = n*(n-1)!, n>0
1. if array is empty
2. return -1 as result
3. else if middle element matches
4. return index of middle element as result
5. else if target < middle element
6. return result of searching lower portion of array
7. else
8. return result of searching upper portion of array
template <typename T>
int bin_search(const std::vector<T>& items,
int first, int last, const T& target) {
if (first > last)
return -1; // Base case, item not found
else { // Next probe index.
int mid = (first + last)/2;
if (target < items[mid])
return bin_search(items, first, mid-1, target);
else if (items[mid] < target)
return bin_search(items, mid+1, last, target);
else
return middle; // Base case for
// successful search.
}
}
template <typename Item_Type>
int binary_search(const std::vector<Item_Type>items,
const Item_Type& target) {
return binary_search(items, 0, items.size()-1, target);
}
C++ Standard library function binary_search defined in <algorithms> does this.
Towers of Hanoi
Counting grid squares in a blob
Backtracking, as in maze search
Desire: Process an image presented as a two-dimensional array of color values
Information in the image may come from
X-Ray
MRI
Satellite imagery
Etc.
Goal: Determine size of any area considered abnormal because of its color values
A blob is a collection of contiguous cells that are abnormal
By contiguous we mean cells that are adjacent, horizontally, vertically, or diagonally
user enters the position of a cell in a blob
o e.g. <1,4>, where rows & columns start at 0
algorithm returns the number of cells in that blob
o what is the size of the blob which contains cell <1,4>?
white => cell is OK
blue => cell is abnormal
blob == contiguous abnormal
cells (horizontal, vertical and
diagonal)
Algorithm count_cells(x, y):
if (x, y) outside grid
return 0
else if color at (x, y) normal
return 0
else
Set color at (x, y) to “Temporary” (normal)
return 1 + sum of count_cells on neighbors
int countCells(color grid[ROWS][COLS], int r, int c) {
if (r < 0 || r >= ROWS || c < 0 || c >= COLS) {
return 0;
} else if (grid[r][c] != ABNORMAL) {
return 0;
} else {
grid[r][c] = TEMPORARY;
return 1
+ countCells(grid,r-1,c-1) + countCells(grid,r-1,c)
+ countCells(grid,r-1,c+1) + countCells(grid,r,c+1)
+ countCells(grid,r+1,c+1) + countCells(grid,r+1,c)
+ countCells(grid,r+1,c-1) + countCells(grid,r,c-1);
}
}
Backtracking: systematic trial and error search for solution to a problem
Example: Finding a path through a maze
In walking through a maze, probably walk a path as far as you can go
Eventually, reach destination or dead end
If dead end, must retrace your steps
Loops: stop when reach place you’ve been before
Backtracking systematically tries alternative paths and eliminates them if they don’t work
If you never try exact same path more than once, and
You try all possibilities,
You will eventually find a solution path if one exists
Problems solved by backtracking: a set of choices
Recursion implements backtracking straightforwardly
Activation frame remembers choice made at that decision point
A chess playing program likely involves backtracking
1. if (x,y) outside grid, return false
2. if (x,y) barrier or visited, return false
3. if (x,y) is maze exit, color PATH and return true
4. else:
5. set (x,y) color to PATH (“optimistically”)
6. for each neighbor of (x,y)
7. if findPath(neighbor), return true
8. set (x,y) color to TEMPORARY (“visited”)
9. return false
bool findMazePath(color grid[ROWS][COLS],int r,int c) {
if (r < 0 || c < 0 || r >= ROWS || c >= COLS)
return false; // Cell is out of bounds.
else if (grid[r][c] != BACKGROUND)
return false; // Cell is on barrier or dead end.
else if (r == ROWS - 1 && c == COLS - 1) {
grid[r][c] = PATH; // Cell is on path
return true; // and is maze exit.
}
else
. . .
}
{ // Recursive case.
// Attempt to find a path from each neighbor.
// Tentatively mark cell as on path.
grid[r][c] = PATH;
if (findMazePath(grid, r - 1, c)
|| findMazePath(grid, r + 1, c)
|| findMazePath(grid, r, c - 1)
|| findMazePath(grid, r, c + 1 ) ) {
return true;
} else {
grid[r][c] = TEMPORARY; // Dead end.
return false;
}
}
Recursion
Maciel: Chapter 14
Sorting
Maciel: Chapter 15
Recommended