1 Recursion How to design an Object Oriented program Lecture #8.

download 1 Recursion How to design an Object Oriented program Lecture #8.

If you can't read please download the document

Transcript of 1 Recursion How to design an Object Oriented program Lecture #8.

  • Slide 1
  • 1 Recursion How to design an Object Oriented program Lecture #8
  • Slide 2
  • 2 Recursion! Building a SuDoKu solver! Even cracking codes and ciphers! Recursion is one of the most difficult topics in Computer Science But once you master it, you can solve all sorts of cool problems! Writing a computer program that can play chess or checkers!
  • Slide 3
  • 3 Idea Behind Is the problem trivially solved Break the problem into two or more simpler sub-problems Just return the answer Solve each sub-problem j Collect all the solution(s) to the sub-problems Use the sub-solutions to construct a solution to the complete problem Return the solution by calling some other function on the sub-problem j Yes No by calling ourself! SomeOtherFunction(sub-problem 1 ) Problem SolvingRecursion SomeOtherFunction(sub-problem n )... SolveAProblem(problem) SolveAProblem(sub-problem 1 ) SolveAProblem(sub-problem n )
  • Slide 4
  • 4 The Lazy Persons Sort Lets design a new sorting algorithm, called the lazy persons sort Lazy Persons Sort: Split the cards into two roughly-equal piles Hand one pile to nerdy student A and ask them to sort it Hand the other pile to nerdy student B and ask them to sort it Take the two sorted piles and merge them into a single sorted pile The input to this sort are a bunch of index cards with #s. 62217395146221739514
  • Slide 5
  • 5 The Lazy Persons Sort Lazy Persons Sort: Split the cards into two roughly-equal piles Hand one pile to nerdy student A and ask them to sort it Hand the other pile to nerdy student B and ask them to sort it Take the two sorted piles and merge them into a single sorted pile 9522171463 Pretty good! All I had to do was merge two piles of sorted cards! (My nerdy students did all the real work!) Well that worked so well, I think Ill have them sort the other six hundred! 1761492277499322 That sucks. Sorting 3 cards was OK but 300? I dont know where to start! Yeah right. Hey, youre kind of cute when youre angry! Thanks But what can we do? I think I have an idea. We can be lazy too, lets change Careys algorithm just a bit.
  • Slide 6
  • 6 The Lazy Persons Sort Lazy Persons Sort: Split the cards into two roughly-equal piles Hand one pile to nerdy student A and ask them to sort it Hand the other pile to nerdy student B and ask them to sort it Take the two sorted piles and merge them into a single sorted pile 1761492277499322 hot say do the Lazy Persons Sort Oh yeah One more thing. Youre a genius! So now all each person has to do is split their pile in two, hand each one to someone else and then finally merge the results! Correctamundo. And no one person has to do any complex sorting! Isnt this some kind of Pyramid scheme? Will it work? Well, it worked for Carey. Why cant we use exactly the same process he did with our piles? And the students we give each half of our piles to can do the same thing too! Then we can blow this joint and go play StarCraft! Very clever, students. But your approach has one flaw, can you see it? I think I see it The algorithm isnt complete What happens when a person ends up with just a single notecard. The algorithm breaks down. How can you split a single card into two equal piles?!!? Excellent, Mr. Smallberg. And do you have a solution? I think so. If you just rewrite it a bit
  • Slide 7
  • 7 Split the cards into two roughly-equal piles Hand one pile to nerdy student A and ask them to sort it Hand the other pile to nerdy student B and ask them to sort it Take the two sorted piles and merge them into a single sorted pile The Lazy Persons Sort Split the cards into two roughly-equal piles Hand one pile to nerdy student A and ask them to sort it Hand the other pile to nerdy student B and ask them to sort it Take the two sorted piles and merge them into a single sorted pile 1761492277499322 hot say do the Lazy Persons Sort If youre handed just one card, then just give it right back. Lazy Persons Sort: Oh yeah. And one more thing. studly Ah, I see. When a person at the bottom of the pyramid gets a single card, they cant split it in half Besides its just one card, so its technically already sorted... So they just hand it back to the guy above them and let them merge it. Correct! Amazing, huh? By having an algorithm use itself over and over, you can solve big problems!
  • Slide 8
  • 8 The Lazy Persons Sort Split the cards into two roughly-equal piles Hand one pile to person A and say do the Lazy Persons Sort Hand the other pile to person B and say do the Lazy Persons Sort Take the two sorted piles and merge them into a single sorted pile If youre handed just one card, then just give it right back. Lazy Persons Sort: The Lazy Persons Sort (also known as Merge Sort) is a perfect example of a recursive algorithm! Every time our MergeSort function is called, it breaks up its input into two smaller parts and calls itself to solve each sub-part. Its hard to believe it works! Ok but would you agree it definitely works if we change the algorithm like this? If we can rely upon OtherSort to somehow properly sort each sub-array, we agree that our udpated MergeSort will work. Correct? void MergeSort(an array) { if (arrays size == 1) return; // array has just 1 item, all done! MergeSort( first half of array ); // process the 1 st half of the array MergeSort( second half of array); // process the 2 nd half of the array Merge(the two array halves); // merge the two sorted halves // now the complete array is sorted } OtherSort Ok, well let me show you the way OtherSort works void OtherSort(an array) { // just call merge sort! MergeSort( array ); } When you write a recursive function Your job is to figure out how the function can use itself (on a subset of the problem) to get the complete problem solved. When you add the code to make a function call itself, you need to have faith that that call will work properly (on the subset of data). It takes some time to learn to think in this way, but once you get it, youll be a programming Ninja! If youre willing to assume that MergeSort actually works on a full array of data Then you should be willing to have faith that itll work on half an array of data too!
  • Slide 9
  • 9 The Two Rules of Recursion RULE ONE: The Stopping Condition (aka Base Case): Your recursive function must be able to solve the simplest, most basic problem without using recursion. Every recursive function must have a stopping condition! Remember: A recursive function calls itself. Therefore, every recursive function must have some mechanism to allow it to stop calling itself.
  • Slide 10
  • 10 void eatCandy(int layer) { if (layer == 0) { cout val ); int rest = 53 Step #6: Validating our Function int main() { } Again, start by testing your function with the simplest possible input. Node *head = nullptr; // empty list cout next == nullptr ) // the only node return cur->val; // so return its value biggest( cur->next ); {} {} biggest( )int Node *cur return max ( rest, cur->val ); int rest = 42 So our next simplest input is a list with a single node. Lets validate our function on such a list. Our next simplest input is a list with two nodes. Lets validate our function on such a list. And this is the correct result! CHECK! Each time our function runs, its supposed to return the biggest value of the list that was passed in to it. (Our function has no idea that its looking at the tail-end of a longer linked list it just sees the list starting at cur.) Since the list pointed to by cur only has one node, by definition, that one node must hold the biggest value in the list! So our func returns its value. 200 42 52 In this case, the first/top nodes value (52) is larger than the biggest value from the rest of the list (42). So our function returns the first nodes value as the biggest. In this case, cur->next == nullptr indicating that this is the only node in the linked list.
  • Slide 54
  • 54 main() { Node *head;... // create linked list cout next == nullptr) return(cur->val); int rest = biggest( cur->next ); return max( rest, cur-val ); } 1200 cur Biggest-in-List Trace-through int biggest(Node *cur) { if (cur->next == nullptr) return(cur->val); int rest = biggest( cur->next ); return max( rest, cur-val ); } cur 1300 int biggest(Node *cur) { if (cur->next == nullptr) return( cur->val); int rest = biggest( cur->next ); return max( rest, cur-val ); } 1400 cur 8 8 12 3
  • Slide 55
  • 55 Recursion Challenge #3 Write a recursive function called count that counts the number of times a number appears in an array. main() { const int size = 5; int arr[size] = {7, 9, 6, 7, 7}; cout next, val ); if (cur->value == val) return 0; // # found in top node int posInRestOfList = if (posInRestOfList == -1) return -1; // # was not in tail Write a function that finds and returns the earliest position of a number in a linked list. If the number is not in the list or the list is empty, your function should return -1 to indicate this. else return posInRestOfList + 1; void magicfindPos(Node *n, int v) {} findPos(cur->next, val); magic Node *head1 = nullptr; // empty cout
  • 63 Recursion: Binary Search Albert Brandy Carol David Eugene Frank Gordon Grendel Hank Wayne Yentle main() { string names[11] = {Albert,}; if (BS(names,0,10,David) != -1) cout bot) return (-1); // Value not found else { int Mid = (top + bot) / 2; if (f == A[Mid]) return(Mid); // found return where! else if (f < A[Mid]) return(BS(A,top,Mid - 1,f)); else if (f > A[Mid]) return(BS(A, Mid + 1,bot,f)); } As our binary search progresses, top will get bigger and bot will get smaller. If they ever pass each other, it means that our item was not in the array. (0 + 10) / 2 Which is 5 David == Frank 0 1 2 3 4 5 6 7 8 9 10 David < Frank top bot Mid
  • Slide 64
  • 64 Recursion: Binary Search Albert Brandy Carol David Eugene Frank Gordon Grendel Hank Wayne Yentle main() { string names[11] = {Albert,}; if (BS(names,0,10,David) != -1) cout bot) return (-1); // Value not found else { int Mid = (top + bot) / 2; if (f == A[Mid]) return(Mid); // found return where! else if (f < A[Mid]) return(BS(A,top,Mid - 1,f)); else if (f > A[Mid]) return(BS(A, Mid + 1,bot,f)); } int BS(string A[], int top, int bot, string f) { if (top > bot) return (-1); // Value not found else { int Mid = (top + bot) / 2; if (f == A[Mid]) return(Mid); // found return where! else if (f < A[Mid]) return(BS(A,top,Mid - 1,f)); else if (f > A[Mid]) return(BS(A, Mid + 1,bot,f)); } 4 0 bot Mid 0 > 4 (0 + 4) / 2 Which is 2 David == Carol David < Carol Mid David > Carol
  • Slide 65
  • 65 Recursion: Binary Search Albert Brandy Carol David Eugene Frank Gordon Grendel Hank Wayne Yentle main() { string names[11] = {Albert,}; if (BS(names,0,10,David) != -1) cout bot) return (-1); // Value not found else { int Mid = (top + bot) / 2; if (f == A[Mid]) return(Mid); // found return where! else if (f < A[Mid]) return(BS(A,top,Mid - 1,f)); else if (f > A[Mid]) return(BS(A, Mid + 1,bot,f)); } int BS(string A[], int top, int bot, string f) { if (top > bot) return (-1); // Value not found else { int Mid = (top + bot) / 2; if (f == A[Mid]) return(Mid); // found return where! else if (f < A[Mid]) return(BS(A,top,Mid - 1,f)); else if (f > A[Mid]) return(BS(A, Mid + 1,bot,f)); } int BS(string A[], int top, int bot, string f) { if (top > bot) return (-1); // Value not found else { int Mid = (top + bot) / 2; if (f == A[Mid]) return(Mid); // found return where! else if (f < A[Mid]) return(BS(A,top,Mid - 1,f)); else if (f > A[Mid]) return(BS(A, Mid + 1,bot,f)); } int BS(string A[], int top, int bot, string f) { if (top > bot) return (-1); // Value not found else { int Mid = (top + bot) / 2; if (f == A[Mid]) return(Mid); // found return where! else if (f < A[Mid]) return(BS(A,top,Mid - 1,f)); else if (f > A[Mid]) return(BS(A, Mid + 1,bot,f)); } 3 4 3 > 4 (3 + 4) / 2 Which is 3 David == David bot top Mid 3
  • Slide 66
  • 66 Recursion: Binary Search Albert Brandy Carol David Eugene Frank Gordon Grendel Hank Wayne Yentle main() { string names[11] = {Albert,}; if (BS(names,0,10,David) != -1) cout bot) return (-1); // Value not found else { int Mid = (top + bot) / 2; if (f == A[Mid]) return(Mid); // found return where! else if (f < A[Mid]) return(BS(A,top,Mid - 1,f)); else if (f > A[Mid]) return(BS(A, Mid + 1,bot,f)); } int BS(string A[], int top, int bot, string f) { if (top > bot) return (-1); // Value not found else { int Mid = (top + bot) / 2; if (f == A[Mid]) return(Mid); // found return where! else if (f < A[Mid]) return(BS(A,top,Mid - 1,f)); else if (f > A[Mid]) return(BS(A, Mid + 1,bot,f)); } bot top Mid 3
  • Slide 67
  • 67 Recursion: Binary Search Albert Brandy Carol David Eugene Frank Gordon Grendel Hank Wayne Yentle main() { string names[11] = {Albert,}; if (BS(names,0,10,David) != -1) cout bot) return (-1); // Value not found else { int Mid = (top + bot) / 2; if (f == A[Mid]) return(Mid); // found return where! else if (f < A[Mid]) return(BS(A,top,Mid - 1,f)); else if (f > A[Mid]) return(BS(A, Mid + 1,bot,f)); } 0 1 2 3 4 5 6 7 8 9 10 top bot Mid 3
  • Slide 68
  • 68 Recursion Helper Functions So we just saw a recursive version of Binary Search: Notice how many crazy parameters it takes? What is top? Whats bot? Thats going to be really confusing for the user! int BS(string A[], int top, int bot, string f) {... } Wouldnt it be nicer if we just provided our user with a simple function (with a few, obvious params) and then hid the complexity? int SimpleBinarySearch(string A[], int size, string findMe) { } This simple function can then call the complex recursive function to do the dirty work, without confusing the user. And then write a helper function to actually do the complex recursion with complex parameters return BS(A, 0, size-1, findMe); In these cases, youll want to define a function with a simple interface (earow-to-understand parameters) You can then call your helper function from your simple function
  • Slide 69
  • 69 Solving a Maze We can also use recursion to find a solution to a maze. In fact, the recursive solution works in the same basic way as the stack-based solution we saw earlier. The algorithm uses recursion to keep moving down paths until it hits a dead end. Once it hits a dead end, the function returns until it finds another path to try. This approach is called backtracking
  • Slide 70
  • 70 Solving a Maze void solve(int row, int col) { m[row][col] = #; // drop crumb if (row == drow && col == dcol) solvable = true; // done! if (m[row-1][col] == ' ) solve(row-1,col); if (m[row+1][col] == ' ) solve(row+1,col); if (m[row][col-1] == ' ) solve(row,col-1); if (m[row][col+1] == ' ) solve(row,col+1); } bool solvable; // globals int dcol, drow; char m[11][11] = { "**********", "* *", "* * * ** *", "* *** *", "* * * *", "* ***** *", "* * *", "********** }; main() { solvable = false; drow = dcol = 10; solve(1,1); if (solvable == true) cout
  • 92 Use Case #1 1. The user wants to add an appointment to their calendar. A.The user creates a new Appointment object and sets its values: Appointment *app = new Appointment; app->setStartTime(10am); app->setEndTime(11am); B.The user adds the Appointment object to the Calendar: Calendar c; c.addAppointment(app); It looks like were OK here. Although it might be nicer if we could set the Appointments values during construction Appointment bool setStartTime(Time &st) bool setEndTime(Time &st) bool addParticipant(string &user) bool setLocation(string &location) Appointment() ~Appointment() private: Time m_startTime; Time m_endTime; string m_participants[10]; string m_location; Time &start, Time &end, string loc, string parts[]) (10am,11am,Dodd,);
  • Slide 93
  • 93 Use Case #2 2. The user wants to determine if they have an appointment at 5pm with Joe. Hmm Can we do this with our classes? Calendar list getListOfAppts(void) bool addAppt(Appointment *addme) bool removeAppt(string &apptName) bool checkCalendars(Time &slot, Calendar others[]) bool login(string &pass) bool logout(void) Calendar() and ~Calendar() private: Appointment m_app[100]; String m_password; It doesnt look like we can find if we have an appointment at a particular time Lets add this! Appointment *checkTime(Time &t) Calendar c;... Appointment *appt; appt = c.checkTime(5pm); if (appt == NULL) cout isAttendee(Joe)) cout