SOFT1002 (Sem2, 2005) Today’s Lecture SOFT1902 Week 8a: Recursion Definition and explanation Recursive Sequences, and how to code them Functions, and how to code them Famous recursive algorithms Mechanism of recursion Discussion of usefulness of recursion School of Information Technologies Readings Kingston Text Book: Ch 18 “Recursion” Big Java (2nd ed): Ch 18 “Recursion” Further wonderful reading: “Gödel, Escher, Bach” by D. Hofstadter 1 SOFT1002 © University of Sydney, 2005 2 What is recursion? Recursion Something that allows you to define something in terms of itself, e.g., factorial n! = n (n – 1)! (a function) Fibonacci sequence Fn = Fn–1 + Fn–2 (a (noun. see “Recursion”) sequence of numbers) rooted binary tree (a definition) T = root {+ left subtree TL} {+ right subtree TR} where TL and TR are rooted binary trees 3 What else is recursion? SOFT1002 © University of Sydney, 2005 4 Any recursion ...has a base case and a recurrence rule. The base case is also known as the trivial or simplest case. allows a function to call itself (with different arguments!) describes relationships It gives an explicit answer for this case The recurrence rule governs how we break the problem into simpler ones until we hit the base case. my.ancestor = parent or parent.ancestor It shows how the answer in other cases is made up from answers of simpler cases No recursive definition or recursive method is complete without these two! SOFT1002 © University of Sydney, 2005 5 SOFT1002 © University of Sydney, 2005 6 1 SOFT1002 (Sem2, 2005) Important point Recursive functions & sequences Factorial Recursion can be very useful: 1, 2, 6, 24, 120, 720, 5040, ... n! = n( n–1)(n–2)...(2)(1) = n(n–1)! code is usually clean – easy to write/read/understand, Fibonacci some problems are much harder to solve in other ways. 1, 1, 2, 3, 5, 8, 13, 21, 34, ... But recursion is not always ideal: Fn = Fn–1 + Fn–2 LRRCC code may be very inefficient (and it can be hard to spot) (linear recurrence relation with constant coefficients) Recursion is never essential: fn = a1f n–1 + a2fn –2 + ... akfn –k with enough effort, it can be replaced by iteration (NB: LRRCCs can be solved exactly by factorizing a characteristic polynomial: see a mathematics text for details!) 7 SOFT1002 © University of Sydney, 2005 SOFT1002 © University of Sydney, 2005 Direct conversion of recursive sequence definition Any recursive sequence ...has a starting point (e.g. 0!, F0) and an expression of the form f(n) = a function of f(n–1), f(n–2), ... Note that recursion need not be on just one variable: e.g., binomial, multinomial coefficients recursive on n and k: 8 int fact(int n) { if (n==1) return 1; else return n*fact(n-1); } int fib(int n) { if ((n==1)||(n==2)) return 1; else return (fib(n-1)+fib(n-2)); } FIBONACCI FACTORIAL factn = 9 SOFT1002 © University of Sydney, 2005 Binomial recurrence 1 if n=1; n . factn-1 if n>1. n>1. fibn = 1 if n=1,2; fibn-1+fibn-2 if n>2. SOFT1002 © University of Sydney, 2005 10 Pascal's Triangle Base case: when k=n or when k=0, result is 1 The binomial coefficients appear in the expansion of SOFT1002 © University of Sydney, 2005 11 SOFT1002 © University of Sydney, 2005 12 2 SOFT1002 (Sem2, 2005) Pascal's Triangle SOFT1002 © University of Sydney, 2005 13 recursive binomial public static long binomial(int n, int k) { if (n==k) return 1; else if (k==0) return 1; else return (binomial(n-1,k) + binomial(n-1,k-1)); } SOFT1002 © University of Sydney, 2005 15 Recursive methods A sequence is a function with integer argument Eg fib(n) Recursion can also be used for methods with other arguments, provided there is some way of expressing the result in terms of the same method on argument(s) which are somehow simpler (binomial coefficients are quite normal...) SOFT1002 © University of Sydney, 2005 14 Pascal would be proud Printing Pascal's Triangle 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 1 5 10 10 5 1 1 6 15 20 15 6 1 1 7 21 35 35 21 7 1 1 8 28 56 70 56 28 8 1 1 9 36 84 126 126 84 36 9 1 1 10 45 120 210 252 210 120 45 10 1 SOFT1002 © University of Sydney, 2005 16 Recursive code In code, recursive methods often solve the base case first, then move to the sub-problem (smaller n) if it exists, using the recurrence rule. Eg result for a String in terms of result(s) for shorter Strings Eg result for a collection in terms of result(s) for smaller collections SOFT1002 © University of Sydney, 2005 17 SOFT1002 © University of Sydney, 2005 18 3 SOFT1002 (Sem2, 2005) Using Recursion to Reverse a Sentence (P18.1 from Big Java) reverse(“1234”) = “4321” public class Sentence{ private String phrase; public Sentence(String phrase) { this.phrase = phrase; } public String reverse() {???} public String toString() { return phrase; } } SOFT1002 © University of Sydney, 2005 reverse(“1234”) = reverse(“234”) + “1” reverse(“”) = “” Remember: base case is first in code! 19 A (non-recursive) recursion checklist Do I need to return anything? if (phrase.equals("")) return ""; An empty string. When the string is not empty. What is the “thing” that gets closer and closer to the base case? How can this be done? The variable: phrase Removing one character at a time. What should be returned by the recursive case? Base Case When the original string comes to an end. What is the condition for the recursive case? A string. What is the information returned by the base case? Remember to return char c; String rest; What is the condition for the base case? Using Recursion to Reverse a Sentence public String reverse() { Yes. Making the sentence smaller each time } c = phrase.charAt(0); rest = phrase.substring(1); Sentence tailSentence = new Sentence(rest); return tailSentence.reverse() + c; The returned string from the recursive call + current character. SOFT1002 © University of Sydney, 2005 Recursive Case 21 Towers of Hanoi At a remote temple somewhere in Asia, a group of monks is working to move 64 disks from the leftmost peg (peg A) to the rightmost peg (peg C). After all 64 disks have been moved, the universe will dissolve! When will this happen if each disk takes 1 second to move? Rules of the puzzle: Only one topmost disk may be moved at a time. Larger disks may not be placed on top of smaller disks. SOFT1002 © University of Sydney, 2005 20 SOFT1002 © University of Sydney, 2005 What is to return? Reversing a string 23 Call reverse again But a different target Return with concatenated info SOFT1002 © University of Sydney, 2005 22 Towers of Hanoi For n=64, number of moves = 264-1 1.844 1019. Astronomers estimate the present age of the universe to be 20 billion (0.2 1011) years. Recall that one move takes one second. Time left in universe = 1.844 1.844 1019 seconds = 5.85 5.85 1011 years (585 billion years). SOFT1002 © University of Sydney, 2005 24 4 SOFT1002 (Sem2, 2005) Tower of Hanoi (Non-Recursive) At the beginning of the subroutine, code is inserted which declares stacks associated with each formal parameter, each local variable, and the return address for each recursive call. Initially all stacks are empty. The label 1 is attached to the first executable program statement. If the subroutine is a function, i.e., returns some value, then we must replace all return statements with assignment statements, i.e., we introduce a fresh variable, say z, which has the same type as that of the function, and replace each return e statement with a z=e statement. Now, each recursive call is replaced by a set of instructions which do the following: Store the values of all parameters and local variables in their respective stacks. The stack pointer is the same for all stacks. Create the i-the label, i, and store i in the address stack. The value i of this label will be used as the return address. This label is placed in the subroutine as described in rule 8. Evaluate the arguments of this call (they may be expressions) and assign these values to the appropriate formal parameters. Insert an unconditional branch to the beginning of the subroutine. If this is a procedure, add the label created above to the statement immediately following the unconditional branch. If this is a function then follow the unconditional branch by code to use the value of the variable z in the same way a return statement was handled earlier. The first statement of this code is given the label that was created above. These steps are sufficient to remove all recursive calls from a subroutine. Finally, we need to append code just after the last executable statement to do the following: If the recursion stacks are empty, then return the value of z, i.e., return z, in case this is a function, or else simply return. If the stacks are not empty, then restore the value of all parameters and of all local variables. These are at the top of the each stack. Use the return label from the corresponding stack and execute a branch to this label. This can be done using a switch statement. SOFT1002 © University of Sydney, 2005 1. Move N-1 top disks from source to aux using dest 2. Move (bottom) disk from source to dest 3. Move the N-1 disks from aux to dest using source 1 2 25 Recursive Algorithm for Hanoi Towers Hanoi(disks, source, dest, aux): move top disks from source to dest using aux Hanoi(disks, source, dest, aux) If disks is zero All done, return Else Hanoi(disks - 1, source, aux, dest) Move the single disk from source to dest Hanoi(disks - 1, aux, dest, source) SOFT1002 © University of Sydney, 2005 Towers of Hanoi recursively N disks to be moved from source to dest using aux for temporary moves 3 SOFT1002 © University of Sydney, 2005 26 Recursive algorithms “Divide and conquer” is a widely-used way to solve algorithmic problems To solve the problem in one case X find some smaller cases (X1, X2 , etc) of the same problem solve them combine the solutions somehow to get a solution of the problem for X 27 Recursive binary search To search for a value in a segment of a sorted array, look at the middle element, and then search only half the segment. If array[middle] < x, we know that x can’t occur in part of array before middle, so search in half-segment above middle; if array[middle] > x, search in half-segment below middle. SOFT1002 © University of Sydney, 2005 28 Recursive Binary Search code // return an index between left and right inclusive where // the sortedarray has value x // return -1 if there is no such index int binsearch (int x, int[] sortedArray, int left, int right) { if (left > right) return -1; // empty segment of the array if (left == right) { // array segment with one element if (sortedarray[left] == x) return left; else return -1; } else { // segment of the array has 2 or more elements int middle = (left + right) / 2; if (x == sortedArray[middle]) return middle; else if (x < sortedArray[middle]) return binsearch (x, sortedArray, left, middle-1); else if (x > sortedArray[middle]) return binsearch (x, sortedArray, middle+1, right); } } Warning: this algorithm is easy to code wrongly! Always think about boundary cases carefully. SOFT1002 © University of Sydney, 2005 29 SOFT1002 © University of Sydney, 2005 30 5 SOFT1002 (Sem2, 2005) Mechanism for recursion When writing recursive code, we should not think about how it executes Mechanics of Method Calls When any method is called: Just make sure base case and recurrence calculation are done properly However, it is good to know how the code will execute Hand trace (very small examples) as desk Parameters are assigned the values given in the call Local variables are created return value of called method can be used in calling expression local storage for called method is no longer accessible The method body is executed After body completes, execution resumes in the caller check or during debugging SOFT1002 © University of Sydney, 2005 Fresh space (memory) is set aside for the method’s parameters and local variables Recursive methods are no different. 31 The program stack Each time a recursive method calls itself, current information is pushed onto the program stack. The maximum depth of the recurrence affects how much memory we need. The number of calls affects how long it will take! 32 SOFT1002 © University of Sydney, 2005 Executing factorial(2) public static void main(String [ ] args) { System.out.println(factorial(2)); } public int factorial(int n) { if(n==0) return 1; else return n * factorial(n-1); } Memory public static void main(String [ ] args) { System.out.println(factorial(2)); } public int factorial(int n) 2) { if(n==0) if(2 ==0)return return1;1; else return 2 n * factorial(2 factorial(n-1); -1); } public int factorial(int n) 1) { if(n==0) if(1 ==0)return return1;1; else return 1 n * factorial(1 factorial(n-1); -1); } 1 public int factorial(int n) 0) { if(0 if(n==0) ==0)return return1;1; else return 0 n * factorial(0 factorial(n-1); -1); } SOFT1002 © University of Sydney, 2005 33 SOFT1002 © University of Sydney, 2005 Executing factorial(2) public static void main(String [ ] args) { System.out.println(factorial(2)); } public int factorial(int n) { if(n==0) return 1; else return n * factorial(n-1); } Memory public static void main(String [ ] args) { System.out.println(factorial(2)); } public int factorial(int n) 1) { if(n==0) if(1 ==0)return return1;1; else return 1 n * factorial(1 factorial(n-1); 1 -1); } SOFT1002 © University of Sydney, 2005 Executing factorial(2) public static void main(String [ ] args) { System.out.println(factorial(2)); } public int factorial(int n) { if(n==0) return 1; else return n * factorial(n-1); } public int factorial(int n) 2) { if(n==0) if(2 ==0)return return1;1; else return 2 n * factorial(2 factorial(n-1); -1); } 34 Memory public static void main(String [ ] args) { System.out.println(factorial(2)); } public int factorial(int n) 2) { if(n==0) if(2 ==0)return return1;1; else return 2 n * factorial(2 factorial(n-1); -1); 1 } 2 1 35 SOFT1002 © University of Sydney, 2005 36 6 SOFT1002 (Sem2, 2005) Executing factorial(2) public static void main(String [ ] args) { System.out.println(factorial(2)); } Memory public static void main(String [ ] args) { System.out.println( 2 ); } Tracing the stack We now trace the method calls for a Fibonacci number... public int factorial(int n) { if(n==0) return 1; else return n * factorial(n-1); } SOFT1002 © University of Sydney, 2005 37 SOFT1002 © University of Sydney, 2005 The recursion tree for F7 38 Stack analysis For Fibonacci sequence, what is the maximum depth of our code? Fn calls Fn –1, Fn–2, right down to F1: depth n How long will it take? Suppose Fn calls the Fibonacci method g(n) times. Then g(n) = g(n–1) + g(n–2), and g(1) = g(0) = 1 so g(n) = Fn – yuck! This recursive method for Fibonacci takes AGES (there is a faster way, using iteration). SOFT1002 © University of Sydney, 2005 39 Conquering the stack The stack analysis shows that if a recursive function for f(n) has f(n–1) in it, the depth will be O(n). Recursive methods that reduce a problem's size by a constant amount will always suffer this way. Methods that divide a problem into parts which are much smaller usually run faster and can require less space. SOFT1002 © University of Sydney, 2005 41 SOFT1002 © University of Sydney, 2005 40 Recursion and Scalability Bad recursion may lead to exponential running times (example: fibonacci) Often much worse than the obvious iterative code Sensible recursive solutions may have cost comparable to iterative solutions. Recursion is often easy to code But also easy to code wrongly Often recursive code is easier to understand; it may offer a neat answer. SOFT1002 © University of Sydney, 2005 42 7 SOFT1002 (Sem2, 2005) Recursion for general programming Recursion vs Iteration /** * Recursive Solution */ public int factorial(int n) { if(n==0) return 1; else return n * factorial(n-1); } Some “functional” programming languages don’t use iteration at all, but instead use recursion for these purposes /** * Iterative Solution */ public int factorial(int n) { int result = 1; for(int i=n; i>0; i - -) result = result * i; return result; } eg Haskell, ML, Scheme, LISP They treat a loop as “do one turn, then do rest of loop”. Weird but true. As a general rule, use recursion over iteration when recursion provides a significantly more elegant solution. 43 SOFT1002 © University of Sydney, 2005 SOFT1002 © University of Sydney, 2005 Exercise Recursive definitions Write a program that checks whether a word is a palindrome Examples: tattarrattat – the longest palindrome in the Oxford English Dictionary, coined by James Joyce in Ulysses for a knock on the door aibohphobia – a joke word meaning "fear of palindromes", deliberately constructed so as to be one SOFT1002 © University of Sydney, 2005 45 Lists, recursively A list of objects: a list also a list Recursive definition: list is or 44 A rooted tree is a node called the root whose child nodes, if they exist, are also rooted trees. A list is empty, or an object followed by another list SOFT1002 © University of Sydney, 2005 46 Data structure recursion Many complex structured objects can be understood as built up from sub-objects. Therefore calculating functions on these objects can easily be coded recursively: sometimes code as a method of another class, with a parameter which is the data structure (recursive calls will have substructures as parameters); sometimes code as a method of the data structure class, so the target object is the data structure <> (empty) (recursive calls will have substructures as targets) The base case is for empty or trivial structure. Recursive calls reduce the effective size of the structure. list SOFT1002 © University of Sydney, 2005 47 SOFT1002 © University of Sydney, 2005 48 8 SOFT1002 (Sem2, 2005) Any recursive structure What have we done? Recursion concept recursively defined sequences And recursive code to calculate them Recursive methods And how to code them Famous recursive Algorithms ...has a simplest case (e.g., empty structure), and a way of composing the structure from similarly defined substructures. Mechanism for recursive execution For next week: read Kingston ch 19 on Trees SOFT1002 © University of Sydney, 2005 49 SOFT1002 © University of Sydney, 2005 50 Recursion and trees Later we will see how recursion is a crucial part of functions on trees, and parsing. Bear this in mind! SOFT1002 © University of Sydney, 2005 51 9
© Copyright 2024