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.