1 / 41

Recursion: Writing and Testing Recursive Programs

Learn about recursion, a technique for solving problems by solving smaller versions of the same problem. Understand how to implement recursive functions and test them for correctness. Explore the advantages and limitations of recursion compared to iteration.

tguenther
Download Presentation

Recursion: Writing and Testing Recursive Programs

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Recursion

  2. Big Picture • Our objective is to write a recursive program and convince ourselves it is correct with the minimum amount of effort. • We will put some faith in the principles of mathematical induction as we write the program • We will then check the program with a small, non-trivial example • Anything that can be done with a loop, you can do recursively, but it’s certainly not appropriate in many cases. • Does it cause more or less work?

  3. What is recursion? • Sometimes, the best way to solve a problem is by solving a smaller version of the exact same problem first • Recursion is a technique that solves a problem by solving a smaller problem of the same type

  4. When you turn this into a program, you end up with functions that call themselves (recursive functions) int f(int x) { int y; if(x==0) return 1; else { y = 2 * f(x-1); return y+1; } }

  5. All Odd Numbers are Prime • How a physicist determines that all odd numbers are prime • 3, 5, 7 are all prime, 9 – experimental error, 11, 13 are prime… they must all be prime • How a business major determines that all odd numbers are prime • 3, 5, 7, 9, 11, are all prime – they must all be prime.

  6. The Danger With Testing • It takes a long time to test a recursive program, and it’s easy to make mistakes along the way //This code doesn’t work!! bool isPrime(int x) { if (x == 3) { return true; } else if (isPrime(x-2)) { return true; } else { return false; } }

  7. Problems defined recursively • There are many problems whose solution can be defined recursively • Example: n! (n factorial) 1 if n = 0 n!= (recursive solution) (n-1)!*n if n > 0 1 if n = 0 n!= (closed form solution) 1*2*3*…*(n-1)*n if n > 0

  8. Coding the factorial function • Recursive implementation int Factorial(int n) { if (n==0) // base case return 1; else return n * Factorial(n-1); }

  9. Coding the factorial function (cont.) • Iterative implementation int Factorial(int n) { int fact = 1; for(int count = 2; count <= n; count++) fact = fact * count; return fact; }

  10. Divide and Conquer • Recursion works because mathematical induction works. Have faith. Or…. • Imagine that I have lots of very similar functions int fact(int n) { if (n < 2) { return 1; } else { return n * fact2(n-1); } } int fact2(int n) { if (n < 2) { return 1; } else { return n * fact3(n-1); } } // etc, etc.

  11. The Machine is Dumb • The computer does not realize that the function is calling itself! • The computer cannot distinguish between fact calling fact, and fact calling fact2 • After all, the machine code for fact and the machine code for fact2 are identical • So, if we could just become as dumb as the computer, recursion would not be confusing.

  12. How is recursion implemented? • What happens when a function gets called? int a(int w) { return w+w; } int b(int x) { int z,y;  ……………… // other statements z = a(x) + y; return z; }

  13. What happens when a function is called? (cont.) • An activation record is stored into a stack (run-time stack) • The computer has to stop executing function b and starts executing functiona • Since it needs to come back to functionb later, it needs to store everything about functionb that is going to need (x, y, z, and the place to start executing upon return) • Then,x from a is bounded tow from b • Control is transferred to function a

  14. What happens when a function is called? (cont.) • After function a is executed, the activation record is popped out of the run-time stack • All the old values of the parameters and variables in functionb are restored and the return value of functiona replaces a(x) in the assignment statement

  15. What happens when a recursive function is called? • Except the fact that the calling and called functions have the same name, there is really no difference between recursive and nonrecursive calls int f(int x) { int y; if(x==0) return 1; else { y = 2 * f(x-1); return y+1; } }

  16. 2*f(2) 2*f(1) 2*f(1) =f(0) =f(1) =f(2) =f(3)

  17. Another example: n choose k (combinations) • Given n things, how many different sets of size k can be chosen? n n-1 n-1 = + , 1 < k < n (recursive solution) k k k-1 n n! = , 1 < k < n (closed-form solution) k k!(n-k)! with base cases: n n = n (k = 1), = 1 (k = n) 1 n

  18. n choose k (combinations) int Combinations(int n, int k) { if(k == 1) // base case 1 return n; else if (n == k) // base case 2 return 1; else return(Combinations(n-1, k) + Combinations(n-1, k-1)); }

  19. Recursion vs. iteration • Iteration can be used in place of recursion • An iterative algorithm uses a looping construct • A recursive algorithm uses a branching structure • Recursive solutions are often less efficient, in terms of both time and space, than iterative solutions • Recursion can simplify the solution of a problem, often resulting in shorter, more easily understood source code

  20. Termination, Stack Depth and Efficiency • We should always check to ensure our recursive programs terminate. • We should avoid programs that have lots of activation records on the stack at the same time (the stack depth) • The time required for most recursive programs is proportional to the number of recursive calls

  21. How do I write a recursive function? • Determine the size factor • Determine the base case(s) (the one for which you know the answer) • Determine the general case(s) (the one where the problem is expressed as a smaller version of itself) • Verify the algorithm (use the "Three-Question-Method")

  22. Three-Question Verification Method • The Base-Case Question: Is there a nonrecursive way out of the function, and does the routine work correctly for this "base" case?  • The Smaller-Caller Question: Does each recursive call to the function involve a smaller case of the original problem, leading inescapably to the base case?  • The General-Case Question: Assuming that the recursive call(s) work correctly, does the whole function work correctly?

  23. Step 1: Identify Your “Base Case” • A base case is an input value (or set of values) for which: • Determining the result of our function is trivially easy • All other values can ultimately be reduced to (one of) our base case(s) through a finite sequence of “decompositions”

  24. Base Case Examples • Factorial and Fibonacci: n < 2 • Sorting: n < 2 (either array has 1 element or it has no elements at all) • Towers of Hanoi: n == 1 (exactly one ring) • Making Change: The amount of change is exactly the same monetary value as a single coin

  25. Step 2: Pick a Decomposition • It may take us a couple of tries to get the decomposition right, but we have to start somewhere • The decomposition must: • Reduce the problem to a “smaller problem” • Result in eventually reaching the base case • Somehow be useful to solving the original problem

  26. Example Decompositions • Subtract one from n (and compute n-1 factorial) • Divide the array in half (and sort each half) • Skip the first number (and compute the mean of the remaining elements)???

  27. Recursive binary search • Non-recursive implementation template<class ItemType> void SortedType<ItemType>::RetrieveItem(ItemType& item, bool& found) { int midPoint; int first = 0; int last = length - 1; found = false; while( (first <= last) && !found) { midPoint = (first + last) / 2; if (item < info[midPoint]) last = midPoint - 1; else if(item > info[midPoint]) first = midPoint + 1; else { found = true; item = info[midPoint]; } } }

  28. Recursive binary search (cont’d) • What is the size factor? The number of elements in (info[first] ... info[last]) • What is the base case(s)? (1) If first > last, return false (2) If item==info[midPoint], return true • What is the general case? if item < info[midPoint] search the first half if item > info[midPoint], search the second half

  29. Recursive binary search (cont’d) • template<class ItemType> bool BinarySearch(ItemType info[], ItemType& item, int first, int last) { int midPoint; if(first > last) // base case 1 return false; else { midPoint = (first + last)/2; if(item < info[midPoint]) return BinarySearch(info, item, first, midPoint-1); else if (item == info[midPoint]) { // base case 2 item = info[midPoint]; return true; } else return BinarySearch(info, item, midPoint+1, last); } }

  30. Recursive binary search (cont’d) • template<class ItemType> void SortedType<ItemType>::RetrieveItem (ItemType& item, bool& found) { found = BinarySearch(info, item, 0, length-1); }

  31. Recursive InsertItem (sorted list) location location location location

  32. Recursive InsertItem (sorted list) • What is the size factor? The number of elements in the current list What is the base case(s)? • If the list is empty, insert item into the empty list • If item < location->info, insert item as the first node in the current list • What is the general case? Insert(location->next, item)

  33. Recursive InsertItem (sorted list) template <class ItemType> void Insert(NodeType<ItemType>* &location, ItemType item) { if(location == NULL) || (item < location->info)) { // base cases NodeType<ItemType>* tempPtr = location; location = new NodeType<ItemType>; location->info = item; location->next = tempPtr; } else Insert(location->next, newItem); // general case } template <class ItemType> void SortedType<ItemType>::InsertItem(ItemType newItem) { Insert(listData, newItem); }

  34. - No "predLoc" pointer is needed for insertion location

  35. Recursive DeleteItem (sorted list) location location

  36. Recursive DeleteItem (sorted list)(cont.) • What is the size factor? The number of elements in the list • What is the base case(s)? If item == location->info, delete node pointed by location • What is the general case?  Delete(location->next, item)

  37. Recursive DeleteItem (sorted list)(cont.) template <class ItemType> void Delete(NodeType<ItemType>* &location, ItemType item) { if(item == location->info)) { NodeType<ItemType>* tempPtr = location; location = location->next; delete tempPtr; } else Delete(location->next, item); } template <class ItemType> void SortedType<ItemType>::DeleteItem(ItemType item) { Delete(listData, item); }

  38. Recursion can be very inefficient is some cases 15

  39. Deciding whether to use a recursive solution • When the depth of recursive calls is relatively "shallow" • The recursive version does about the same amount of work as the nonrecursive version • The recursive version is shorter and simpler than the nonrecursive solution

More Related