Factorial Recursion stack Binary Search Towers of Hanoi Session 05: C# Recursion Factorial Recursion stack Binary Search Towers of Hanoi FEN 2013-03-02 AK IT: Softwarekonstruktion
AK IT: Softwarekonstruktion Recursion In programming languages recursion is when a method is calling it self (recursion may be indirectly) Recursion often comes around when a problem can be solved using a divide and conquer strategy: If the problem is small, we can apply some simple (non-recursive) solution. (called “base case”) Otherwise the problem is divided into smaller sub problems of the same kind. The sub problems are then solved by applying the solution recursively to the sub problems. Eventually the sub problems become so small that the simple solution can be applied, and recursion terminates. When the sub problems are solved, the solutions to the sub problems are combined into a solution to the original problem. FEN 2013-03-02 AK IT: Softwarekonstruktion
Recursion –Divide and Conquer If the problem is small, we can apply some simple (non-recursive) solution (called “base case”). Otherwise the problem is divided into smaller sub problems of the same kind. The sub problems are then solved by applying the solution recursively to the sub problems. Eventually the sub problems become so small that the simple solution can be applied, and recursion terminates. Note the similarities to loops Conquer: Simple sub problems are solved using the non-recursive solution When the sub problems are solved, the solutions to the sub problems are combined into a solution to the original problem. FEN 2013-03-02 AK IT: Softwarekonstruktion
Example n! = n * (n-1)! for n > 0 n! = 1 for n = 0 Many mathematical functions may be defined recursively. For instance, computing the factorial of an number n (n!): n! = n * (n-1)! for n > 0 n! = 1 for n = 0 The base case (simple problem) is when n=0 If n>0 the problem is divided into the subproblem of computing the factorial of n-1 The combine part is multiplying the result of (n-1)! by n. Note: There must be a base case, so recursion eventually stops. FEN 2013-03-02 AK IT: Softwarekonstruktion
Recursive Factorial in C# public static long Fac(long n) { calls++; if (n == 0) return 1; else return n * Fac(n - 1); } FEN 2013-03-02 AK IT: Softwarekonstruktion
Behind the Scenes: The Recursion Stack When a recursive method executes many activations of the method exist at the same time. The virtual machine is handling this by storing the different states of the method on a stack: Value of parameters and local variables Return address When a method is called, an activation is created and pushed onto the stack When a method returns, its activation is popped from the stack The current activation of the method is always the topmost on the stack, and it is the values of parameters, local variables etc. stored that is used. A stack is a list, where insert (push) and remove (pop) are done at the front of the list (stack top). Stack: top FEN 2013-03-02 AK IT: Softwarekonstruktion
AK IT: Softwarekonstruktion Iterative Factorial Actual it is not very smart to compute the factorial of n recursively – a loop can do the job much more efficiently: public long FacIter(int n) { long fac = 1; for (int i = 2; i <= n; i++) fac = fac * i; return fac; } But is it more simple? public long Fac(int n) if (n == 0 || n == 1) return 1; else return n * Fac(n-1); } FEN 2013-03-02 AK IT: Softwarekonstruktion
AK IT: Softwarekonstruktion Exercise Write a recursive method that computes the sum of the first n non-negative numbers. Hint: the sum may be define by: sum(n) = n + sum(n-1) for n > 0 sum(n) = 0 for n = 0 Draw pictures of the recursion stack when sum(3) is computed FEN 2013-03-02 AK IT: Softwarekonstruktion
Binary Search In a sorted random access (array based) data set it is possible to use binary search. Binary search can be formulated very simply using recursion: Strategy: If - the search set is empty Then - the searched element, s, is not in the set Else - inspect the middle element, m - If - m == s - Then - the element is found and we are done - Else - If - m > s - Then - search in the lower half of the set - Else - search in the upper half of the set Note the recursive nature of this solution! FEN 2013-03-02 AK IT: Softwarekonstruktion
AK IT: Softwarekonstruktion In C#… public static bool BinSearch(int l, int h, int x, int[] T) { //PRE T is sorted //Looking for x between l and h in T calls++; int m; if (l > h) return false; else m = (l + h) / 2; if (x == T[m]) return true; if (x > T[m]) return BinSearch(m + 1, h, x, T); return BinSearch(l, m - 1, x, T); } FEN 2013-03-02 AK IT: Softwarekonstruktion
AK IT: Softwarekonstruktion The Towers of Hanoi Three pins: source, S target, T help, H 64 discs with decreasing diameters are to be moved from S to T observing the following rules: A larger disc can not be placed upon a smaller Only one disc can be moved at the time Algorithm? FEN 2013-03-02 AK IT: Softwarekonstruktion
The Towers of Hanoi This yields a recursive solution Solution: Move 63 discs from S to H using T as help Move one disc from S to T Move 63 discs from H to T using S as help Now the problem is how to move the 63 discs. That must be easier: Move 62 discs… Move one disc This yields a recursive solution FEN 2013-03-02 AK IT: Softwarekonstruktion
AK IT: Softwarekonstruktion The Algorithm in C# public static void Hanoi(int n, char s, char h, char d) { //Moves n disks from S(ource) to D(estination) using H(elp) //Do not try this for n much larger 16 calls++; if (n == 1) //System.Console.WriteLine("Move one disk from: " + s + " to:" + d); } else Hanoi(n - 1, s, d, h); Hanoi(n - 1, h, s, d); FEN 2013-03-02 AK IT: Softwarekonstruktion
Efficiency of Recursive Algorithms Overhead in time due to call-return Overhead in space due to the recursion stack The Recursion Tree tells about complexity: The depth tells about memory space The number of nodes tells about running time FEN 2013-03-02 AK IT: Softwarekonstruktion
AK IT: Softwarekonstruktion Examples Draw the recursion trees: Binary Search: O(log n) (time and space) Factorial of n: O(n) (time and space) Towers of Hanoi: O(2n) (time) O(n) (space) FEN 2013-03-02 AK IT: Softwarekonstruktion
Recursive vs. Iterative Algorithms It is always possible to rewrite a recursive algorithm to an equivalent iterative which may be more efficient. But the iterative version is often more complicated, since one has to handle the recursion stack oneself. In some cases it is possible to find iterative algorithms that are more efficient and as simple as the equivalent recursive: Tail recursion: If the recursive call is the last statement in the algorithm, then there is no need for remembering the state of other active calls. The recursion can the be converted to a loop and there is no need for the recursion stack. Hence we get rid of the overhead to call-return and the overhead in space as well. FEN 2013-03-02 AK IT: Softwarekonstruktion