chapter 5 recursion - george mason universitysetia/cs310/slides/ch5.pdf · chapter 5 recursion 1....
TRANSCRIPT
Chapter 5
RECURSION
1. Introduction to Recursion
(a) Stack Frames(b) Recursion Trees(c) Examples
2. Principles of Recursion
3. Backtracking: Postponing the Work
(a) The Eight-Queens Puzzle(b) Review and Refinement(c) Analysis
4. Tree-Structured Programs: Look-Ahead in Games
Outline Data Structures and Program Design In C++Transp. 1, Chapter 5, Recursion 80 1999 Prentice-Hall, Inc., Upper Saddle River, N.J. 07458
Stacks and Trees
Stackspace
fordata
Time
D
C
A
M
D
D D D
D D DD D
C C
A A A A A A
M M M M M M M M M M M M M M
B
Start FinishM
B
D
C
A
D
D
D
THEOREM 3.1 During the traversal of any tree, vertices areadded to or deleted from the path back to the root in thefashion of a stack. Given any stack, conversely, a tree can bedrawn to portray the life history of the stack, as items arepushed onto or popped from it.
Stacks and Trees Data Structures and Program Design In C++Transp. 2, Sect. 5.1, Introduction to Recursion 81 1999 Prentice-Hall, Inc., Upper Saddle River, N.J. 07458
Tree-Diagram Definitions
The circles in a tree diagram are called vertices or nodes.
The top of the tree is called its root.
The vertices immediately below a given vertex are called thechildren of that vertex.
The (unique) vertex immediately above a given vertex is calledits parent. (The root is the only vertex in the tree that hasno parent.)
The line connecting a vertex with one immediately above orbelow is called a branch.
Siblings are vertices with the same parent.
A vertex with no children is called a leaf or an external ver-tex.
Two branches of a tree are adjacent if the lower vertex of thefirst branch is the upper vertex of the second. A sequence ofbranches in which each is adjacent to its successor is called apath.
The height of a tree is the number of vertices on a longest-possible path from the root to a leaf. (Hence a tree containingonly one vertex has height 1.)
The depth or level of a vertex is the number of branches ona path from the root to the vertex.
Tree-Diagram Definitions Data Structures and Program Design In C++Transp. 3, Sect. 5.1, Introduction to Recursion 82 1999 Prentice-Hall, Inc., Upper Saddle River, N.J. 07458
Factorials: A Recursive Definition
Informal definition: The factorial function of a positive integeris
n! = n × (n − 1) × · · · × 1.
Formal definition:
n! ={
1 if n = 0n × (n − 1)! if n > 0.
Every recursive process consists of two parts:
A smallest, base case that is processed without recur-sion; and
A general method that reduces a particular case to oneor more of the smaller cases, thereby making progresstoward eventually reducing the problem all the way tothe base case.
int factorial(int n)/* Pre: n is a nonnegative integer.
Post: Return the value of the factorial of n. */{
if (n == 0) return 1;else return n * factorial(n − 1);
}
Factorials: A Recursive Definition Data Structures and Program Design In C++Transp. 4, Sect. 5.1, Introduction to Recursion 83 1999 Prentice-Hall, Inc., Upper Saddle River, N.J. 07458
Towers of Hanoi
1 2 3
Rules:
Move only one disk at a time.
No larger disk can be on top of a smaller disk.
void move(int count, int start, int finish, int temp);
Pre: There are at least count disks on the tower start. Thetop disk (if any) on each of towers temp and finish islarger than any of the top count disks on tower start.
Post: The top count disks on start have been moved to finish;temp (used for temporary storage) has been returnedto its starting position.
Towers of Hanoi Data Structures and Program Design In C++Transp. 5, Sect. 5.1, Introduction to Recursion 84 1999 Prentice-Hall, Inc., Upper Saddle River, N.J. 07458
move (2, 1, 3, 2)
Outer call.
First recursive call.
Trivial recursive call.
First instruction printed.
Trivial recursive call.
End of first recursive call.
Second instruction printed.
Second recursive call.
Trivial recursive call.
Third instruction printed.
Trivial recursive call.End of second recursive call.End of outer call.
move (0, 3, 1, 2)
"Move disk 1 from 1 to 2."
"Move disk 1 from 2 to 3."
move (0, 3, 2, 1)
move (0, 2, 1, 3)
move (0, 1, 3, 2)
move (1, 2, 3, 1)
move (1, 1, 2, 3)
"Move disk 2 from 1 to 3."
move (3, 1, 3, 2)
move (2, 1, 2, 3) move (2, 2, 3, 1)
move (1, 1, 3, 2)
move (1, 3, 2, 1)
move (1, 2, 1, 3) move (1, 1, 3, 2)
move (0, 1, 2, 3)move (0, 2, 3, 1)move (0, 3, 1, 2)move (0, 1, 2, 3)
move (0, 2, 3, 1) move (0, 3, 1, 2)move (0, 1, 2, 3)
move (0, 2, 3, 1)
Trace and tree for Hanoi Data Structures and Program Design In C++Transp. 6, Sect. 5.1, Introduction to Recursion 85 1999 Prentice-Hall, Inc., Upper Saddle River, N.J. 07458
Designing Recursive Algorithms
Find the key step. Begin by asking yourself, “How can thisproblem be divided into parts?” or “How will the key step inthe middle be done?”
Find a stopping rule. This stopping rule is usually thesmall, special case that is trivial or easy to handle withoutrecursion.
Outline your algorithm. Combine the stopping rule andthe key step, using an if statement to select between them.
Check termination. Verify that the recursion will alwaysterminate. Be sure that your algorithm correctly handles ex-treme cases.
Draw a recursion tree. The height of the tree is closely re-lated to the amount of memory that the program will require,and the total size of the tree reflects the number of times thekey step will be done.
Designing Recursive Algorithms Data Structures and Program Design In C++Transp. 7, Sect. 5.2, Principles of Recursion 86 1999 Prentice-Hall, Inc., Upper Saddle River, N.J. 07458
Implementation of Recursion
Multiple Processors: Concurrency:
Processes that take place simultaneously are called concur-rent. Recursion can run on multiple processors concurrently.
Single Processor Implementation:
Recursion can use multiple storage areas with a single pro-cessor.
Example of Re-entrant Processes:
Instructions
Data
DataData
Implementation of Recursion Data Structures and Program Design In C++Transp. 8, Sect. 5.2, Principles of Recursion 87 1999 Prentice-Hall, Inc., Upper Saddle River, N.J. 07458
Execution Tree and Stack Frames
A
M
Tree ofsubprogram
calls
Time
Subp
rogr
am c
alls
M
A CB
B
B C
D E
C
A A B C
B D E
M
C C
M M M M
A A B B C C C
B B D E E E
B C C
B
Time
Stac
k sp
ace
Execution Tree and Stack Frames Data Structures and Program Design In C++Transp. 9, Sect. 5.2, Principles of Recursion 88 1999 Prentice-Hall, Inc., Upper Saddle River, N.J. 07458
Tail Recursion
DEFINITION Tail recursion occurs when the last-executedstatement of a function is a recursive call to itself.
If the last-executed statement of a function is a recursivecall to the function itself, then this call can be eliminated byreassigning the calling parameters to the values specified inthe recursive call, and then repeating the whole function.
RecursionTail
recursion
(a) (b)
(c)
Iteration
M
P
P
P
M
P
P
P
M
P P P
Tail Recursion Data Structures and Program Design In C++Transp. 10, Sect. 5.2, Principles of Recursion 89 1999 Prentice-Hall, Inc., Upper Saddle River, N.J. 07458
F1 F0
F2
F3
F4
F1
F0
F2F2
F0F1F1
F3
F1
F0F1
F2
F5
F1
F0F1
F3
F4
F7
F0F1
F0F1 F0F1F2
F2 F3
F4
F5F6
F2 F2
F1
F3
F1
Recu
rsiontree
forth
ecalcu
lationof
F7
Data
Stru
ctures
and
Program
Design
InC
++
Tran
sp.11,Sect.5.2,P
rinciples
ofR
ecursion
90
1999P
rentice-H
all,Inc.,U
pperS
addleR
iver,N.J.07458
1
1
6
5
15
10
20
10
15
5
6
1
1
1
1
4
3
6
3
4
1
1
1
1
2
1
1
1
1 6 15 20 15 6 1
1 5 10 10 5 1
1 4 6 4 1
1 3 3 1
1 2 1
1 1
1
6
5
4
3
2
1
0
20 63 4 51
(a) Symmetric form (b) In square array
Th
etop
ofP
ascal’strian
gleof
binom
ialcoefficien
tsD
ataS
tructu
resan
dP
rogramD
esignIn
C+
+T
ransp.12,S
ect.5.2,Prin
ciplesof
Recu
rsion91
1999
Pren
tice-Hall,In
c.,Upper
Saddle
River,N
.J.07458
Eight Queens Puzzle
(a) (b) (c) (d)Dead end Dead end Solution Solution
X X
?
XX X
?
? ? ?? ?
X X
XX XX
X
X XXX X XX
XXX
X X X
X ? ?
Eight Queens Puzzle Data Structures and Program Design In C++Transp. 13, Sect. 5.3, Backtracking: Postponing the Work 92 1999 Prentice-Hall, Inc., Upper Saddle River, N.J. 07458
Methods in the Queens Class
bool Queens :: unguarded(int col) const;
Post: Returns true or false according as the square in thefirst unoccupied row (row count) and column col is notguarded by any queen.
void Queens :: insert(int col);
Pre: The square in the first unoccupied row (row count) andcolumn col is not guarded by any queen.
Post: A queen has been inserted into the square at row countand column col; count has been incremented by 1.
void Queens :: remove(int col);
Pre: There is a queen in the square in row count − 1 andcolumn col.
Post: The above queen has been removed; count has beendecremented by 1.
bool Queens :: is solved( ) const;
Post: The function returns true if the number of queens al-ready placed equals board size; otherwise, it returnsfalse.
Methods in the Queens Class Data Structures and Program Design In C++Transp. 14, Sect. 5.3, Backtracking: Postponing the Work 93 1999 Prentice-Hall, Inc., Upper Saddle River, N.J. 07458
The Backtracking Function
void solve from(Queens &configuration)/* Pre: The Queens configuration represents a partially completed arrangement of
nonattacking queens on a chessboard.Post: All n-queens solutions that extend the given configuration are printed. The
configuration is restored to its initial state.Uses: The class Queens and the function solve from, recursively. */
{if (configuration.is solved( )) configuration.print( );else
for (int col = 0; col < configuration.board size; col++)if (configuration.unguarded(col)) {
configuration.insert(col);solve from(configuration); // Recursively continue to add queens.configuration.remove(col);
}}
The Backtracking Function Data Structures and Program Design In C++Transp. 15, Sect. 5.3, Backtracking: Postponing the Work 94 1999 Prentice-Hall, Inc., Upper Saddle River, N.J. 07458
The First Data Structure
This implementation uses a square array with bool entries:
const int max board = 30;
class Queens {public:
Queens(int size);bool is solved( ) const;void print( ) const;bool unguarded(int col) const;void insert(int col);void remove(int col);int board size; // dimension of board = maximum number of queens
private:int count; // current number of queens = first unoccupied rowbool queen square[max board][max board];
};
Sample Results
Size 8 9 10 11 12 13Number of solutions 92 352 724 2680 14200 73712Time (seconds) 0.05 0.21 1.17 6.62 39.11 243.05Time per solution (ms.) 0.54 0.60 1.62 2.47 2.75 3.30
Sample Results Data Structures and Program Design In C++Transp. 16, Sect. 5.3, Backtracking: Postponing the Work 95 1999 Prentice-Hall, Inc., Upper Saddle River, N.J. 07458
Components of a Chessboard
0
0 21 3
(a) Rows (b) Columns
difference = row − column sum = row + column
(d) Downward diagonals (e) Upward diagonals
0,20,10,0
0,0 0,3
1,1 1,2
2,2 2,1
3,3 3,0
1,0 1,3
2,1 2,2
3,2 3,1
2,0 2,3
3,1 3,23,0 3,3
0,1 0,2
1,2 1,1
2,3 2,0
0,2 0,1
1,3 1,0
0,3 0,0
0,3
1 1,21,11,0 1,3
2 2,22,12,0 2,3
3 3,23,13,0 3,3
0,20,10,0 0,3
1,21,11,0 1,3
2,22,12,0 2,3
3,23,13,0 3,3
lower left
(c) Diagonal directions
0,20,10,0 0,3
1,21,11,0 1,3
2,22,12,0 2,3
3,23,13,0 3,3
−3 0
1
2
34
56
−2
−1
01
23
upper left upper right
lower right
Components of a Chessboard Data Structures and Program Design In C++Transp. 17, Sect. 5.3, Backtracking: Postponing the Work 96 1999 Prentice-Hall, Inc., Upper Saddle River, N.J. 07458
Revised Data Structures
This implementation keeps arrays to remember which compo-nents of the chessboard are free or are guarded.
class Queens {public:
Queens(int size);bool is solved( ) const;void print( ) const;bool unguarded(int col) const;void insert(int col);void remove(int col);int board size;
private:int count;bool col free[max board];bool upward free[2 * max board − 1];bool downward free[2 * max board − 1];int queen in row[max board]; // column number of queen in each row
};
Sample Results
Size 8 9 10 11 12 13Number of solutions 92 352 724 2680 14200 73712Time (seconds) 0.01 0.05 0.22 1.06 5.94 34.44Time per solution (ms.) 0.11 0.14 0.30 0.39 0.42 0.47
Sample Results Data Structures and Program Design In C++Transp. 18, Sect. 5.3, Backtracking: Postponing the Work 97 1999 Prentice-Hall, Inc., Upper Saddle River, N.J. 07458
7
8
Solution Solution
5
3
5
1
5
7
1
7
3
5
3
7
1
55
1 6 8
4
1
5 6 7 8
2 3 4 5 6 7
1 2 3
61
87
8
4 6 7
2 4 8
2
7 4
2
7
7
8
Part
ofth
erecu
rsiontree,eigh
t-queen
sproblem
Data
Stru
ctures
and
Program
Design
InC
++
Tran
sp.19,Sect.5.3,B
acktracking:
Postpon
ing
the
Work
98
1999P
rentice-H
all,Inc.,U
pperS
addleR
iver,N.J.07458
Game Trees
7 5
5 3 8 2 3
10
1 1
100
6
83
12
7
5 3 8 2 3
10
1 1
100
6
83
12
0
7
510 3 1
157
6
128
10
3
firstBest move
second
first
second
Game Trees Data Structures and Program Design In C++Transp. 20, Sect. 5.4, Tree-Structured Programs: Look-Ahead in Games99 1999 Prentice-Hall, Inc., Upper Saddle River, N.J. 07458
2
32
1
1 3
1 2 2 3 1
132
2 31 231
2 3 1
FS S S S F S S S F S S S S S
F F
S FS S S S F FS F F FS S
F
2
1
3
3 1 2 2 3 1 3 2 3 2 3 2 3
2 2 3 1 3 1 3 1 2 2 3 1 3 1 3 1 2 2 3
3
2 3
first
second
first
second
first
second
Tree
forth
egam
eof
Eigh
tD
ataS
tructu
resan
dP
rogramD
esignIn
C+
+T
ransp.21,S
ect.5.4,Tree-S
tructu
redP
rograms:
Look-A
head
inG
ames
100
1999P
rentice-H
all,Inc.,U
pperS
addleR
iver,N.J.07458
Algorithm Development
Use two classes called Move and Board. A Move object representsa single game move. A Board object represents a single game po-sition.
The class Move requires only constructor methods.The class Board requires methods to initialize the Board, to
detect whether the game is over, to play a move that is passedas a parameter, to evaluate a position, and to supply a list of allcurrent legal moves.
The method legal moves uses a stack to return the currentmove options.
The method better uses two integer parameters and returns anonzero result if the (current) mover would prefer a game valuegiven by the first rather than the second parameter.
The method worst case returns a constant value that the moverwould like less than the value of any possible game position.
class Board {public:
Board( ); // constructor for initializationint done( ) const; // Test whether the game is over.void play(Move try it);int evaluate( ) const;int legal moves(Stack &moves) const;int worst case( ) const;int better(int value, int old value) const;
// Which parameter does the mover prefer?void print( ) const;void instructions( ) const;/* Additional methods, functions, and data will depend on the game under considera-
tion. */};
Algorithm Development Data Structures and Program Design In C++Transp. 22, Sect. 5.4, Tree-Structured Programs: Look-Ahead in Games101 1999 Prentice-Hall, Inc., Upper Saddle River, N.J. 07458
Look-Ahead Algorithm
int look ahead(const Board &game, int depth, Move &recommended)/* Pre: Board game represents a legal game position.
Post: An evaluation of the game, based on looking ahead depth moves, is re-turned. The best move that can be found for the mover is recorded as Moverecommended.
Uses: The classes Stack, Board, and Move, together with function look aheadrecursively. */
{if (game.done( ) || depth == 0)
return game.evaluate( );
else {Stack moves;game.legal moves(moves);int value, best value = game.worst case( );
while (!moves.empty( )) {Move try it, reply;moves.top(try it);Board new game = game;new game.play(try it);value = look ahead(new game, depth − 1, reply);if (game.better(value, best value)) {
// try it is the best move yet foundbest value = value;recommended = try it;
}moves.pop( );
}return best value;
}}
Look-Ahead Algorithm Data Structures and Program Design In C++Transp. 23, Sect. 5.4, Tree-Structured Programs: Look-Ahead in Games102 1999 Prentice-Hall, Inc., Upper Saddle River, N.J. 07458
Tic-Tac-Toe
The tic-tac-toe grid is a 3×3 array of integers, with 0 to denotean empty square and the values 1 and 2 to denote squaresoccupied by the first and second players, respectively.
A Move object stores the coordinates of a square on the grid.
The Board class contains private data members to record thecurrent game state in a 3 × 3 array and to record how manymoves have been played.
The game is finished either after nine moves have been playedor when one or the other player has actually won.
The legal moves available for a player are just the squares witha value of 0.
Evaluate a Board position as 0 if neither player has yet won;however, if one or other player has won, we shall evaluate theposition according to the rule that quick wins are consideredvery good, and quick losses are considered very bad.
A program that sets the depth of look-ahead to a value of 9 ormore will play a perfect game, since it will always be able tolook ahead to a situation where its evaluation of the positionis exact. A program with shallower depth can make mistakes,because it might finish its look-ahead with a collection of po-sitions that misleadingly evaluate as zero.
Tic-Tac-Toe Data Structures and Program Design In C++Transp. 24, Sect. 5.4, Tree-Structured Programs: Look-Ahead in Games103 1999 Prentice-Hall, Inc., Upper Saddle River, N.J. 07458
Pointers and Pitfalls
1. Recursion should be used freely in the initial design of al-gorithms. It is especially appropriate where the main steptoward solution consists of reducing a problem to one or moresmaller cases.
2. Study several simple examples to see whether or not recursionshould be used and how it will work.
3. Attempt to formulate a method that will work more generally.Ask, “How can this problem be divided into parts?” or “Howwill the key step in the middle be done?”
4. Ask whether the remainder of the problem can be done in thesame or a similar way, and modify your method if necessaryso that it will be sufficiently general.
5. Find a stopping rule that will indicate that the problem or asuitable part of it is done.
6. Be very careful that your algorithm always terminates andhandles trivial cases correctly.
7. The key tool for the analysis of recursive algorithms is therecursion tree. Draw the recursion tree for one or two simpleexamples appropriate to your problem.
8. The recursion tree should be studied to see whether the re-cursion is needlessly repeating work, or if the tree representsan efficient division of the work into pieces.
9. A recursive function can accomplish exactly the same tasksas an iterative function using a stack. Consider carefullywhether recursion or iteration with a stack will lead to aclearer program and give more insight into the problem.
Pointers and Pitfalls Data Structures and Program Design In C++Transp. 25, Chapter 5, Recursion 104 1999 Prentice-Hall, Inc., Upper Saddle River, N.J. 07458
10. Tail recursion may be removed if space considerations are im-portant.
11. Recursion can always be translated into iteration, but the gen-eral rules will often produce a result that greatly obscures thestructure of the program. Such obscurity should be toleratedonly when the programming language makes it unavoidable,and even then it should be well documented.
12. Study your problem to see if it fits one of the standard para-digms for recursive algorithms, such as divide and conquer,backtracking, or tree-structured algorithms.
13. Let the use of recursion fit the structure of the problem. Whenthe conditions of the problem are thoroughly understood, thestructure of the required algorithm will be easier to see.
14. Always be careful of the extreme cases. Be sure that youralgorithm terminates gracefully when it reaches the end ofits task.
15. Do as thorough error checking as possible. Be sure that everycondition that a function requires is stated in its precondi-tions, and, even so, defend your function from as many viola-tions of its preconditions as conveniently possible.
Pointers and Pitfalls Data Structures and Program Design In C++Transp. 26, Chapter 5, Recursion 105 1999 Prentice-Hall, Inc., Upper Saddle River, N.J. 07458