200932523127

advertisement
Recursion (递归)
1. Introduction to Recursion
-- Principles of recursion
-- Recursion trees
2. How recursion is implemented on the machine
-- Stacks and recursion
3. Analyzing Recursion
-- when to use recursion
4. Backtracking (回溯法)– a method to try all
possibilities using recursion
An algorithm is recursive (递归的) if it calls
itself (directly or indirectly) to do part of its
work.
Factorials: A Recursive Definition
• In Mathematics:
Base case
(递归基)
General case that
reduces to simpler
case(递归步)
+: concise and elegant
-: can require keeping track of many
partial computations
The process of recursive call
Every recursive process consists of two parts:
1. A smallest, base case that gives a solution;
2. A general method that reduces a particular case
to one or more of the smaller cases, reducing
the problem all the way to the base case
eventually.
The tower of Hanoi
Task: move the tower from 1 to 3 using 2 as
temporary storage.
Rules: 1. move only one disk at one time;
2. No larger disk can be placed on a
smaller disk.
Divide and conquer (分治法)
1
2
3
We have to get to this state and then move
the bottom disk
1
A similar problem
2
3
1
2
Solved by
recursion
3
The Solution
典故:
古印度有一個傳說,婆羅賀摩(Brahma,眾生地方的廟宇安置了一
個含有64個金盤的婆羅賀摩塔,婆羅門教的憎侶們,奉命依遊戲規
則要不停移動這些金盤,傳說中還說,當重排完成時,就是世界末
日的到來,而經過計算,這64個金盤的遊戲至少要經過 264-1次才會
完成,如果移動一次以1秒鐘計算,還要經過
184467440737095551615天才能完成,照這樣看來,這世界還算蠻
安全的。
Our goal: move(8, 1, 3, 2)
Our goal: move(8, 1, 3, 2)
Steps to solve the problem:
move(7, 1, 2, 3); /*move 7 disks from tower 1
to 2 */
cout << “move disk 8 from tower 1 to
3.”<<endl;
move(7, 2, 3, 1); /* move 7 disks from tower
2 to 3 */
Recursive Function
The base case is
count == 0
Designing recursive algorithms
Tree of function calls(函数调用树)
Tree of function calls is a tool to analyzing the process of
function calls.
• A node(结点) denotes a function call with the function
name;
• A function may invokes other functions, which are
denoted as children;
• Different recursive calls appear as different vertices
with the same name;
• Recursion tree: the recursive part of the tree of function
calls.
invoke the
function
Meaning of the tree of
function calls: the
traversal of the tree is
the trace of the function
calls
Return from
the function
Tree of function calls and the
execution order
Recursive call
Execution
order
Recursion tree for three
disks
Number of nodes is
the number of disk
movements.
The number of moves to solve Hanoi
for 64 disks?
The number of inner nodes:
1+2+22+ … +263 = 264 – 1 ≈ 1.6 × 1019
It will take more than 500 billion (五千亿年)years to
finish the task suppose one can move one disk every
second.
Note also that our program also produces the best
possible solution.
Recursion (递归)
1. Introduction to Recursion
-- Solving problems using recursion
-- Recursion trees
2. How recursion is implemented on the machine
-- Stacks and recursion
3. Analyzing Recursion
-- when to use recursion
4. Backtracking (回溯法)– a method to try all
possibilities using recursion
Recursion and Stacks
• Recursive calls are implemented by using stacks;
• Recursion can be replaced by stacks and
iteration (迭代);
Function A calls B, B calls C:
Save returning
address and
parameters
Start from the
saved address
A
B
C
First started, last
finished
Computation process of factorial (4)
Factorial(4) = 4*factorial(3)
3*factorial(2)
2*factorial(1)
1*factorial(0)
1*1
Stack Frames for Subprograms
When a program calls a subprogram, the system:
• Store returning location and parameters;
• Allocate storage for all the local variables.
• Then the control is passed to the subprogram.
The information about the returning address and
parameters is stored in the activation record(活
动记录) for the function call.
When the subprogram is finished,
• Return the result of the subprogram;
• Release the storage allocated for the subprogram;
• the control is passed to the caller and execution
starts at the returning address.
Temporary storage areas for use by functions would
be allocated in a list with the property last in, first
out.
So, stack is the data structure to manage function
call mechanism.
When a function call is made:
• the activation record is pushed into the stack, and
• when the function is finished, the activation record
for the function is popped off.
D calls itself
Activation record for
the main program M
Stack frames(运行栈): each column shows the stack contents at a given time:
• A function may call itself. There is no difference
between this recursive call and other function
calls.
• For each recursive call, an activation record for
the instance of the call is pushed into the stack.
L1:
L2:
L3:
L4:
int main() {
move(2,1,3,2);
L0:
: ….;
Stack frames for move(2, 1, 3, 2)
Activation record:
(address, n, start, finish, tempt)
L2, 0, 1, 3, 2
L2, 1, 1, 2, 3
L0, 2, 1, 3, 2
Return, Pop(), goto L2
L2, 0, 1, 2, 3
L2, 1, 1, 2, 3
L0, 2, 1, 3, 2
Starting from L2,
move disk 1 from 1
to 2
L2, 1, 1, 2, 3
L0, 2, 1, 3, 2
Recursive call at L3, so push()
L4, 0, 3, 2, 1
L2, 1, 1, 2, 3
L0, 2, 1, 3, 2
Return, pop(), then
goto L4
L2, 1, 1, 2, 3
L0, 2, 1, 3, 2
Starting at L4, so pop() again,
and goto L2
L2, 1, 1, 2, 3
L0, 2, 1, 3, 2
Do L2: move disk 2
from 1 to 3
L0, 2, 1, 3, 2
Recursive
Recursivecall
call
at L1
at L3
L2, 0, 2, 1, 3
L4, 1, 2, 3, 1
L0, 2, 1, 3, 2
Pop(), goto L2
L2, 0, 2, 1, 3
L4, 1, 2, 3, 1
L0, 2, 1, 3, 2
Moving disk 1 from 2 to 3
L4, 1, 2, 3, 1
L0, 2, 1, 3, 2
Recursive call at L3, push()
L4, 0, 1, 3, 2
L4, 1, 2, 3, 1
L0, 2, 1, 3, 2
L4, 0, 1, 3, 2
L4, 1, 2, 3, 1
L0, 2, 1, 3, 2
Pop(), goto L4, so pop() again, goto L4
pop() again
Final state when
move(2, 1,3,2) is done
Recursion (递归)
1. Introduction to Recursion
-- Solving problems using recursion
-- Recursion trees
2. How recursion is implemented on the machine
-- Stacks and recursion
3. Analyzing Recursion
-- when to use recursion
4. Backtracking (回溯法)– a method to try all
possibilities using recursion
Analyzing space and time complexity
What correspond to time
complexity?
3 time units +
A’s and D’s time units
3
The time
complexity is
proportional to
the number of
nodes.
3
1
time complexity: count the
total time units in the
function call tree.
2
2
1
Complexity of factorial
空间复杂度:变量占用空间+
递归工作栈占用空间 = O(n)
时间复杂度:
T(0) = 1
T(n)= 1 + T(n-1) = O(n)
(recurrence relation 递推关系)
Complexity of Hanoi
空间复杂度:
变量占用空间+
递归工作栈占用空间
= O(n)
时间复杂度:
T(n)= T(n-1) + 1+T(n-1)
T(0) = 0
T(n) = O(2n)
Complexity and recursive trees
根据递归调用树可以估算递归算法的空间复
杂度和时间复杂度:
• 空间复杂度与递归树的高度成正比;
• 时间复杂度与函数的调用次数有关,与递
归树的结点总数正比。
What we learn about recursion
• A recursive algorithm must have a base case, which gives a
direct solution, and a method which reduces general cases to
simpler cases.
• Recursion is done by using stacks:
– When a call is made, the activation record is pushed into the
stack and control is passed to the called function;
– When the call is finished, the activation record is popped off
the stack and execution starts from the address in the popped
record.
• The recursive tree of a recursive algorithm can be used to
analyze the algorithm:
– The space complexity is proportional to the height of the
recursive tree;
– The time complexity is proportional to the number of nodes.
Recursive or Nonrecursive
Factorials
Recursive version
Nonrecursive
version
The recursive version is
more expensive in space
and time
Recursion tree for
F7
F3 is repeated 5 times
F5 is repeated
Nonrecursive
version
Using Iteration
(迭代)
• Space complexity for recursive version is linear in n.
• Time complexity for the recursive version is
exponential with n (>= 2n/2).
• Time complexity for the nonrecursive version is linear
in n.
• The space complexity for the nonrecursive version: it
uses only 4 variables.
Hanoi with tail recursion
Last executed statement is a
recursive call
Hanoi without tail recursion
When to use recursion
• If time and space is not an issue, then
recursive version is much easier to understand
and preferred.
• If duplicate tasks are involved, then data
structure other than stacks will be appropriate.
• Recursion can always be replaced by iteration
and stacks. But no point to do it if not
necessary.
Recursion (递归)
1. Introduction to Recursion
-- Solving problems using recursion
-- Recursion trees
2. How recursion is implemented on the machine
-- Stacks and recursion
3. Analyzing Recursion
-- when to use recursion
4. Backtracking (回溯法)– a method to try all
possibilities using recursion
Backtracking(回溯)
A method to try all possibilities using recursion.
When there are several possibilities,
• take one and go on;
• go back to the most recent choice, and try another
possibility when a dead end is reached.
Eight Queens Puzzle
Four Queens Solution
Take 1 of the 4 legitimate positions
guarded
backtrack
Dead end
backtrack
backtrack
Backtracking
• Backtracking is a recursive method that goes back to
the most recent choice and tries another possibility.
• In (b), we go back to row 2 and put the queen in
column 4 instead.
• In (c) , there is no other possibilities in row two. We go
back to row 1 and put the queen in column 2.
Outline of the solution
• Imagining using a class called Queens to represent a
partial configuration(格局, 状态) of queens;
• Add a queen in the configuration and continue the
search from the new configuration;
• If this does not lead to a solution, then remove the
newly added queens and try another position and
continue the search until a solution is reached.
Exit: a solution is
found
Exit: dead end
When it is returned here, the
queen added at p is removed
and try next possible position.
The main program
The Queen Class
(count, col) denotes a
possible position
The position is
empty
count is modified
after insertion.
Occupied
It is not necessary to check the lower
part. (?)
The Backtracking Function
Guarded positions are
not investigated
Effectively pruning
a recursion tree
Dead end
Part of the recursion tree for eight queens problem
Analysis of Backtracking
• Effectiveness of backtracking: positions that are
discovered impossible prevent the later
investigation of fruitless paths.
• Number of configurations that there is only one
queen in each row: 88
• Number of configurations that there is only one
queen in each column: 8!
• Backtracking cut this number further: it does not
investigate impossible paths. See the recursion
tree.
Lower Bounds
However, the amount of work done by backtracking
still grows rapidly with n.
To place a queen on the first n/4 rows, the minimum
positions to investigate:
n(n-3)(n-6)…(n-3n/4) > (n/4)n/4 > 2n
The queen guards at most three
positions in the next row
Task: Improvement
• The loops in function unguarded takes
considerable time when checking if a position is
guarded in three directions.
• An improvement is to keep track which columns
are guarded using an one dimension bool array,
which diagonals are guarded using two bool
arrays. This makes the function unguarded much
simpler.
Upward_free:
row+col:0--6
Downward_free:
board_size-1+row-col:0--6
New Queens
Number of
diagonals
Modify the column and two
diagonals that are guarded
now.
bool Queens::unguarded(int col)const
/*Post: Return true if the position (count, col)
is unguarded, false else. */
{
return col_free(col)
&& upward_free[count+col]
&& downward_free[count-col+board_size-1];
}
Summary
• Recursion: concept and recursion trees
• Principles of designing recursive algorithms;
• Divide and conquer, a method to solve problems
using recursion;
• How recursion is implemented in machine;
• Complexity analysis using recursion trees.
Download