Presentation is loading. Please wait.

Presentation is loading. Please wait.

1 Recursion. Objectives To define the concept of recursion as a programming strategy distinct from other forms of algorithmic decomposition. To recognize.

Similar presentations


Presentation on theme: "1 Recursion. Objectives To define the concept of recursion as a programming strategy distinct from other forms of algorithmic decomposition. To recognize."— Presentation transcript:

1 1 Recursion

2 Objectives To define the concept of recursion as a programming strategy distinct from other forms of algorithmic decomposition. To recognize the paradigmatic form of a recursive function. To understand the internal implementation of recursive calls. To appreciate the importance of the recursive leap of faith. To understand the concept of wrapper functions in writing recursive programs. To be able to write and debug simple recursive functions. 2

3 Recursion: One of the most important “Great Ideas” 3 Recursion is the process of solving a problem by dividing it into smaller sub-problems of the same form. The italicized phrase is the essential characteristic of recursion; without it, all you have is a description of stepwise refinement of the solution. Since the recursive decomposition generates sub-problems that have the same form as the original problem, we can use the same function or method to solve the generated sub-problems at different levels. In terms of the structure of the code, the defining characteristic of recursion is having functions that call themselves, directly or indirectly, as the decomposition process proceeds.

4 Recursive Paradigm: Writing a Recursive Function Finding a recursive solution is mostly a matter of figuring out how to break it down so that it fits the paradigm. For this, you must do two things: 1. Identify simple case(s) that can be solved without recursion. 2. Find a recursive decomposition that breaks each instance of the problem into simpler sub-problems of the same type, which you can then solve by applying the method recursively. 4 if (test for simple case) { Compute a simple solution without using recursion } else { Break the problem down into sub-problems of the same form. Solve each of the sub-problems by calling this function recursively. Reassemble the solutions to the sub-problems into a solution for the whole. }

5 The recursive formulation of Factorial n! = n x (n – 1)! Thus, 4! is 4 x 3!, 3! is 3 x 2!, and so on. To make sure that this process stops at some point, mathematicians define 0! to be 1. Thus, the conventional mathematical definition of the factorial looks like 5

6 Recursive vs. iterative implementation int Fact(int n) { if (n == 0) { return 1; } else { return n * Fact(n-1); } } int FactIteration(int n) { int product; product = 1; for (int i = 1; i <= n; i++) { product *= i; } return product; } 6

7 Tracing Factorial Function skip simulation Fact Enter n: 3 3! = 6 int main() { printf("Enter n: “); int n = GetInteger(); printf(“%d! = %d\n”,n, Fact(n) ); return 0; } n 3 6 int Fact(int n) { if (n == 0) { return 1; } else { return n * Fact(n - 1); } n 3 2 int Fact(int n) { if (n == 0) { return 1; } else { return n * Fact(n - 1); } n 2 1 int Fact(int n) { if (n == 0) { return 1; } else { return n * Fact(n - 1); } n 1 1 int Fact(int n) { if (n == 0) { return 1; } else { return n * Fact(n - 1); } n 0 Local variables and return addresses are stored in a stack.

8 Simulating the Fact Function skip simulation Fact Enter n: 5 5! = 120 int main() { cout << "Enter n: "; int n = GetInteger(); cout << n << "! = " << Fact(n) << endl; return 0; } n 5 120 int Fact(int n) { if (n == 0) { return 1; } else { return n * Fact(n - 1); } n 5 24 int Fact(int n) { if (n == 0) { return 1; } else { return n * Fact(n - 1); } n 4 6 int Fact(int n) { if (n == 0) { return 1; } else { return n * Fact(n - 1); } n 3 2 int Fact(int n) { if (n == 0) { return 1; } else { return n * Fact(n - 1); } n 2 1 int Fact(int n) { if (n == 0) { return 1; } else { return n * Fact(n - 1); } n 1 1 int Fact(int n) { if (n == 0) { return 1; } else { return n * Fact(n - 1); } n 0 int Fact(int n) { if (n == 0) { return 1; } else { return n * Fact(n - 1); } n 0

9 The Recursive “Leap of Faith” 9 The purpose of going through the complete decomposition of factorial is to convince you that the process works and that recursive calls are in fact no different from other method calls, at least in their internal operation. The danger with going through these details is that it might encourage you to do the same when you write your own recursive programs. As it happens, tracing through the details of a recursive program almost always makes such programs harder to write. Writing recursive programs becomes natural only after you have enough confidence in the process that you don’t need to trace them fully. As you write a recursive program, it is important to believe that any recursive call will return the correct answer as long as the arguments define a simpler sub-problem. Believing that to be true—even before you have completed the code—is called the recursive leap of faith.

10 The Fibonacci function int Fib(int n) { if (n < 2) { return n; } else { return Fib(n - 1) + Fib(n - 2); } } 10 t0 t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 t11 t12 0 1 1 2 3 5 8 13 21 34 55 89 144 How about int FibIteration(int n) { … // dynamic programming }

11 Steps in the calculation of Fib(5) 11 Recursive leap of faith

12 Efficiency of the Recursive implementation of Fib Can you implement an iterative version of Fib, say int IterativeFib(int n) ? Which one will be faster Recursive or Iterative? Look at the details of Fib(5) in previous slide: you will see that it is extremely inefficient the same Fib term is computed many times (redundant calls to Fib()) Should we blame Recursion! Can we fix this? 12

13 Wrapper function and a subsidiary function for the more general case Suppose we have the following function int AdditiveSequence(int n, int t0, int t1) { if (n == 0) return t0; if (n == 1) return t1; return AdditiveSequence(n-1, t1, t0+t1); } Then we can simply implement Fib(n) as int Fib(int n) { return AdditiveSequence(n, 0, 1); } 13 subsidiary function for the more general case Wrapper function

14 Trace and Efficiency of Fib Fib(5) = AdditiveSequence(5, 0, 1) = AdditiveSequence(4, 1, 1) = AdditiveSequence(3, 1, 2) = AdditiveSequence(2, 2, 3) = AdditiveSequence(1, 3, 5) = 5 The new implementation is entirely recursive, and it is comparable in efficiency to the standard iterative version of the Fib() function. 14

15 Common Errors Recursive function may not terminate if the stopping case is not correct or is incomplete stack overflow: run-time error Make sure that each recursive step leads to a situation that is closer to a stopping case. (problem size gets smaller and smaller and smaller and smaller ) 15

16 Iteration vs. Recursion In general, an iterative version of a program will execute more efficiently in terms of time and space than a recursive version. Why? This is because the overhead involved in entering and exiting a function is avoided in iterative version. However, a recursive solution can be sometimes the most natural and logical way of solving a problem (tree traversal). Conflict: machine efficiency vs. programmer efficiency It is always true that recursion can be replaced with iteration and a stack data structure. 16

17 Mutual Recursion So far, the recursive functions have called themselves directly But, the definition is broader: To be recursive, a function must call itself at some point during its evaluation. For example, if a function ƒ calls a function g, which in turn calls ƒ, the function calls are still considered to be recursive. The recursive call is actually occurring at a deeper level of nesting. 17

18 Mutual Recursion Example 18 typedef int bool; bool IsEven(unsigned int n) { if (n == 0) { return true; } else { return IsOdd(n - 1); } bool IsOdd(unsigned int n) { return !IsEven(n); }

19 Other examples A palindrome is a string that reads identically backward and forward, such as "level” or "noon". it is easy to check whether a string is a palindrome by iterating through its characters, Palindromes can also be defined recursively. Insight: any palindrome longer than a single character must contain a shorter palindrome in its interior. For example, "level" consists of the palindrome "eve" with an "l" at each end. Binary Search 19 bool IsPalindrome(string str) { int len = strlen(str); if (len <= 1) { return true; } else { return (str[0] == str[len – 1] && IsPalindrome(SubString(str,1, len - 2))); }

20 A Recursive GCD Function int GCD(int x, int y) { int r = x % y; while (r != 0) { x = y; y = r; r = x % y; } return y; } One of the oldest known algorithms that is worthy of the title is Euclid’s algorithm for computing the greatest common divisor (GCD) of two integers, x and y. Euclid’s algorithm is usually implemented iteratively using code that looks like this:

21 A Recursive gcd Function int gcd(int x, int y) { if (y == 0) { return x; } else { return gcd(y, x % y); }

22 Generating Permutations All possible arrangements of a particular set Write a function ListPermutations(s) that displays all permutations of the string s. For example, ListPermutations(s); should display the following six arrangements when s is "ABC": ABC ACB BAC BCA CBA CAB 22 How would you implement the ListPermutations function?  Iterative control structures: finding a general solution that works for strings of any length is difficult.  Thinking about the problem recursively, on the other hand, leads to a relatively straightforward solution.

23 The recursive insight for Generating Permutations The permutations of the five-character string "ABCDE" consist of the following strings: The character 'A' followed by every possible permutation of "BCDE“ The character 'B' followed by every possible permutation of "ACDE“ The character 'C' followed by every possible permutation of "ABDE“ The character 'D' followed by every possible permutation of "ABCE“ The character 'E' followed by every possible permutation of "ABCD" More generally, take each of the n characters in turn and display that character followed by every possible permutation of the remaining n – 1 characters. 23

24 Difficulty in previous strategy The recursive sub-problem does not have exactly the same form as the original. The original problem requires you to display all permutations of a string. The sub-problem requires you to display a character from a string followed by all permutations of the remaining letters. Moreover, as the recursion proceeds, the character in front will become two characters, then three, and so forth. So the general sub-problem is to generate all permutations of a string with some characters at the beginning of the string already fixed in their positions. 24 asymmetry

25 Solving asymmetry between the original problem and its recursive sub-problems RecursivePermute(str, k) generates all permutations of a string whose first k letters are fixed. When k=0, all letters are free to change As k increases, the problem becomes smaller When k=length of string, there is no more interchange, print the string Define ListPermutations as a simple wrapper function that calls a subsidiary function to solve the more general case. 25 void ListPermutations(char *str) { RecursivePermute(str, 0); }

26 RecursivePermute: Pseudocode form 26 void RecursivePermute(string str, int k) { if (k is equal to the length of the string) { Display the string. } else { For each character position i from k to the end of string { Exchange the characters in positions i and k Use recursion to generate permutations with the first k+1 characters fixed Restore the string by exchanging the characters in positions i and k }

27 RecursivePermute: Implementation in C 27 void RecursivePermute(char *str, int k) { int i; if (k == strlen(str)) { printf(“%s\n”, str); } else { for(i=k; i < strlen(str); i++) { ExchangeCharacters(str, k, i); RecursivePermute(str, k+1); ExchangeCharacters(str, k, i); } void ExchangeCharacters(char *str, int k, int i) { char tmp; tmp = str[i]; str[i] = str[k]; str[k] = tmp; } Can you think of any improvement? What is the complexity?

28 Exercise: Trace ListPermutations char str[4]="abc"; ListPermutations(str); abc acb bac bca cba cab 28

29 29 Binary Search of a Sorted Array Strategy Find the midpoint of the array Is target equal to the item at midpoint? If smaller, look in the first half If larger, look in second half firstmidlast

30 30 Binary Search (non-recursive) int binSearch ( array[], target, n) { int first = 0; int last = n-1; while ( first <= last ) { mid = (first + last) / 2; if ( target == array[mid] ) return mid; // found it else if ( target < array[mid] ) // must be in 1 st half last = mid -1; else // must be in 2 nd half first = mid + 1 } return -1; // only got here if not found above }

31 31 Binary Search (recursive) int binSearch ( array[], first, last, target) { if ( first <= last ) { mid = (first + last) / 2; if ( target == array[mid] ) // found it! return mid; else if ( target < array[mid] ) // must be in 1 st half return binSearch( array, first, mid-1, target); else // must be in 2 nd half return binSearch(array, mid+1, last, target); } return -1; } No loop! Recursive calls takes its place But don’t think about that if it confuses you! Base cases checked first? (Why? Zero items? One item?)


Download ppt "1 Recursion. Objectives To define the concept of recursion as a programming strategy distinct from other forms of algorithmic decomposition. To recognize."

Similar presentations


Ads by Google