240 likes | 445 Views
Carrano : Chapter 2. Recursion Topics: Definition of Recursion Numeric Recursion Non-Numeric Recursion Inefficiency of Recursion. What is Recursion?. Recursion is a programming technique where a function calls itself.
E N D
Carrano: Chapter 2 Recursion • Topics: • Definition of Recursion • Numeric Recursion • Non-Numeric Recursion • Inefficiency of Recursion
What is Recursion? • Recursion is a programming technique where a function calls itself. Of course, you can’t leave it like this, or the calls won’t stop correctly CS 150
What is Recursion? • Recursive functions must communicate with themselves to stop the recursion. Now that we can stop the recursion, we can control how the function calls itself. CS 150
Program B’s Run-Time Stack (Room for B’s growth) Program B’s Data Heap Program B Program A’s Run-Time Stack (Room for A’s growth) Program A’s Data Heap Program A Operating System How Recursion Works. Program Memory Allocation Each program being executed on a computer is allocated extra space which it could use for its stack of function calls, and/or for “dynamically” expanding its use of memory for data. The memory used for data is known as a “heap”. CS 150
int yippy(int &yoo, int boo) { inti; for (i = 1; i < boo; i++) yoo += i; return boo + yoo; } yoo: boo: 5 i: 5 int snippy(intsnoo) { inthep = yippy(snoo,5); if (hep > 0) return hep; else return 0; } snoo: 7 hep: ??? void main() { int snap = 7; int yap = 10; intblap = 100; blap = snippy(snap); yippy(yap, blap); } snap: 7 yap: 10 blap: 100 How Recursion Works. A Program’s Run-Time Stack Program XYZ: void main() { int snap = 7; int yap = 10; intblap = 100; blap = snippy(snap); yippy(yap, blap); zippy(yap); } int snippy(intsnoo) { inthep = yippy(snoo,5); if (hep > 0) return hep; else return 0; } void yippy(int &yoo, int boo) { inti; for (i = 1; i < boo; i++) yoo += i; return; } CS 150
void rex(int x) • { • if(x >= 100) • return; • rex(x + 1); • } x = 98 void main() { rex(98); } How Recursion Works. A Recursive Program’s Run-Time Stack • void rex(int x) • { • if(x >= 100) • return; • rex(x + 1); • } x = 100 Program PDQ: • void rex(int x) • { • if(x >= 100) • return; • rex(x + 1); • } • void main() • { • rex(98); • } • void rex(intx) • { • if(x >= 100) • return; • rex(x + 1); • } x = 99 CS 150
Recursive Solutions • Facts about a recursive solution • A recursive function calls itself • Each recursive call solves an identical, but smaller, problem • The solution to at least one smaller problem— the base case—is known • Eventually, one of the smaller problems must be the base case; reaching the base case enables the recursive calls to stop CS 150
Recursive Solutions • Four questions for constructing recursive solutions • How can you define the problem in terms of a smaller problem of the same type? • How does each recursive call diminish the size of the problem? • What instance of the problem can serve as the base case? • As the problem size diminishes, will you reach this base case? CS 150
Recursion Example #1: Factorial //////////////////////////////////////////////////////////////////////////// // Program file: factorial.cpp // // This program queries the user for a non-negative integer and then uses // // a recursive function to calculate the factorial of the input value. // //////////////////////////////////////////////////////////////////////////// #include <iostream> #include <iomanip> using namespace std; int factorial(int n); //////////////////////////////////////////////////////////// // The main function asks the user for a value, calls the // // recursive factorial function, and outputs the result. // //////////////////////////////////////////////////////////// void main() { int number, result; cout << "Enter a non-negative integer for factorial computation: "; cin >> number; while (number < 0) { cout << "I said, enter a NON-NEGATIVE integer: "; cin >> number; } cout << endl; result = factorial(number); cout << "FINAL RESULT: " << number << "! = " << result << endl << endl; } CS 150
Factorial (Continued) ///////////////////////////////////////////////////////////////////////////// // Function factorial recursively calculates the factorial of parameter n, // // outputting entering and exiting messages to demonstrate to the user the // // recursive function calls which make the final calculation possible. // ///////////////////////////////////////////////////////////////////////////// int factorial(int n) { static int indent = 0; int result; cout << setw(indent) << "" << "ENTERING FACTORIAL ROUTINE WITH N = ” << n << endl << endl; if ((n == 1) || (n == 0)) { cout << setw(indent) << "" << "EXITING FACTORIAL ROUTINE WITH N = " << n << ", " << n << "! = " << 1 << endl << endl; indent -= 4; return 1; } else { indent += 4; result = n * factorial(n - 1); cout << setw(indent) << "" << "EXITING FACTORIAL ROUTINE WITH N = " << n << ", " << n << "! = " << result << endl << endl; indent -= 4; return result; } } CS 150
Factorial (Continued) CS 150
int factorial (int n) { if ((n == 1) || (n == 0)) return 1; else : } int factorial (int n) { if ((n == 1) || (n == 0)) return 1; else { result = n * factorial(n-1); return result; } } n: 1 result: ? n: 4 result: 4*? int factorial (int n) { if ((n == 1) || (n == 0)) return 1; else { result = n * factorial(n-1); return result; } } n: 2 result: 2*? int factorial (int n) { if ((n == 1) || (n == 0)) return 1; else { result = n * factorial(n-1); return result; } } n: 5 result: 5*? int factorial (int n) { if ((n == 1) || (n == 0)) return 1; else { result = n * factorial(n-1); return result; } } n: 3 result: 3*? void main() { : result = factorial(number); : } number: 5 result: ? Tracing Through Factorial’s Run-Time Stack CS 150
Given n objects, how many different sets of size k can be selected? This problem can be solved by solving two slightly smaller problems and summing the results (sounds pretty recursive!): Problem 1: Assuming that the first object is selected, how many size k-1 sets can be selected from the remaining n-1 objects? Problem 2: Assuming that the first object is not selected, how many size k sets can be selected from the remaining n-1 objects? Recursion Example #2: Combination CS 150
Combination (Continued) ///////////////////////////////////////////////////////////////////// // Program file: combination.cpp // // This program queries the user for a set size and a subset size, // // and then recursively computes the number of different subsets // // of the appropriate size that could be selected from the set. // ///////////////////////////////////////////////////////////////////// #include <iostream> using namespace std; intcomputeCombo(intsetSize, intsubsetSize); // Main asks the user for the set & subset sizes, calls the // // function producing the combination, and outputs the result. // void main() { intsizeOfCollection; intsizeOfSelection; cout << "How many objects in the master collection? "; cin >> sizeOfCollection; cout << "How many objects in a single selection? "; cin >> sizeOfSelection; cout << endl << endl; cout << "FOR " << sizeOfCollection << " FANCY OBJECTS COLLECTED" << endl << "AND " << sizeOfSelection << " LUCKY CHOICES SELECTED," << endl << "THERE\'S " << computeCombo(sizeOfCollection, sizeOfSelection) << " DIFFERENT SUBSETS" << endl << "BUT EACH HAS " << sizeOfCollection-sizeOfSelection << " ANGRY UPSETS" << endl << "\'CAUSE THE REST OF THE SAPS ARE REJECTED!" << endl << endl; } CS 150
Combination (Continued) ///////////////////////////////////////////////////////////// // Function computeCombo recursively calculates the number // // of different combinations of size subsetSize that can // // be obtained from a collection of size setSize. // ///////////////////////////////////////////////////////////// intcomputeCombo(intsetSize, intsubsetSize) { if (subsetSize > setSize) return 0; if (subsetSize == setSize)) return 1; if (subsetSize == 1)) return setSize; return (computeCombo(setSize-1, subsetSize-1) + computeCombo(setSize-1, subsetSize)); } CS 150
Non-Numeric Recursion: Backtracking • Recursion allows the use of a technique called “Backtracking” where attempts at a solution are made by trying one of several possibilities and, when a ‘dead end’ is reached, the attempts backtrack to a former decision point and try another possibility. • Examples include: • Flood Filling algorithms • Path Finding algorithms CS 150
Non-Numeric Recursion: Flood Fill /////////////////////////////////////////////////////////////////// // This function uses recursion to fill an area of a character // // grid by filling each non-filled point and then checking that // // point’s neighbors. // ////////////////////////////////////////////////////////////////// void pixelFiller(int row, intcol, char canvas[MAX_HEIGHT][MAX_WIDTH]) { //check the base cases for point canvas[row][col] if((row < 0)||(row >= MAX_HEIGHT)) // too high/low return; if((col < 0)||(col>= MAX_WIDTH)) // too left/right return; if(canvas[row][col] == FILL_CHAR) // already filled return; // fill current location canvas[row][col] = FILL_CHAR; //make recursive function calls to check neighbors pixelFiller(row-1,col, canvas); // up pixelFiller(row+1, col, canvas); // down pixelFiller(row, col-1, canvas); // left pixelFiller(row,col+1, canvas); // right // all neighboring pixels checked, so we’re done! return; } CS 150
Non-Numeric Recursion: Maze Solving /////////////////////////////////////////////////////////////////////////////// // This program uses recursion to traverse a maze in the form of a character // // grid, finding the path through the maze, and marking it. // /////////////////////////////////////////////////////////////////////////////// #include <iostream> #include <fstream> using namespace std; const int MAZE_SIDE = 21; const char START_CHAR = 'S'; const char FINISH_CHAR = 'F'; const char BLANK_CHAR = ' '; const char PATH_CHAR = 'o'; void getMaze(char matrix[MAZE_SIDE][MAZE_SIDE], int &startRow, int &startCol, int &destRow, int &destCol); bool solveMaze(char matrix[MAZE_SIDE][MAZE_SIDE], intcurrRow, intcurrCol, intdestRow, intdestCol); void printMaze(char matrix[MAZE_SIDE][MAZE_SIDE]); // Function main retrieves, solves, outputs the maze. // void main() { char maze[MAZE_SIDE][MAZE_SIDE]; intstartRow, startCol; intdestRow, destCol; getMaze(maze, startRow, startCol, destRow, destCol); solveMaze(maze, startRow, startCol, destRow, destCol); printMaze(maze); } CS 150
Maze Solving (Continued) ///////////////////////////////////////////////////////////////////////////////// // Function getMaze retrieves the maze from a specific input file, assuming // // that it is size MAZE_SIDE x MAZE_SIDE, and that it is stored as MAZE_SIDE // // lines of MAZE_SIDE characters. The four function parameters will be // // assigned the row and column numbers of the start and finish of the maze. // ///////////////////////////////////////////////////////////////////////////////// void getMaze(char matrix[MAZE_SIDE][MAZE_SIDE], int &startRow, int &startCol, int &destRow, int &destCol) { ifstreamsourceFile; char nextChar; sourceFile.open("maze.txt"); for (inti = 0; i < MAZE_SIDE; i++) for (int j = 0; j < MAZE_SIDE; j++) { sourceFile.get(nextChar); while(nextChar == '\n') sourceFile.get(nextChar); matrix[i][j] = nextChar; if(nextChar == START_CHAR) { startRow = i; startCol = j; } else if(nextChar == FINISH_CHAR) { destRow = i; destCol = j; } } } CS 150
Maze Solving (Continued) /////////////////////////////////////////////////////////////////////////////// // Function solveMaze will recursively find and mark a path through the maze.// //////////////////////////////////////////////////////////////////////////////// bool solveMaze(char maze[MAZE_SIDE][MAZE_SIDE], intcurrRow, intcurrCol, intdestRow, intdestCol) { //system("cls"); printMatrix(maze); if((currRow < 0) || (currRow >= MAZE_SIDE) || (currCol < 0) || (currCol >= MAZE_SIDE)) return false; if(maze[currRow][currCol] == FINISH_CHAR) return true; if((maze[currRow][currCol] != BLANK_CHAR) && (maze[currRow][currCol] != START_CHAR)) return false; if(maze[currRow][currCol] != START_CHAR) maze[currRow][currCol] = PATH_CHAR; if(solveMaze(maze, currRow-1, currCol, destRow, destCol)) return true; else if(solveMaze(maze, currRow+1, currCol, destRow, destCol)) return true; else if(solveMaze(maze, currRow, currCol-1, destRow, destCol)) return true; else if(solveMaze(maze, currRow, currCol+1, destRow, destCol)) return true; if(maze[currRow][currCol] != START_CHAR) maze[currRow][currCol] = BLANK_CHAR; return false; } CS 150
Maze Solving (Continued) ///////////////////////////////////////////////////////// // Function printMaze outputs the entire maze grid. // ///////////////////////////////////////////////////////// void printMaze(char maze[MAZE_SIDE][MAZE_SIDE]) { for (int row = 0; row < MAZE_SIDE; row++) { for (int col = 0; col < MAZE_SIDE; col++) cout << maze[row][col]; cout << endl; } cout << endl; return; CS 150
Maze Solving (Continued) Sample maze maze.txt +-+-+-+-+-+-+-+-+-+-+ | | | | | | + +-+-+ + + + +-+ + + | | | | | | + +-+ + +-+-+-+-+ +-+ | | +-+-+ +-+-+-+-+-+-+ + | | | | | + +-+ +-+-+ + + +-+ + | | | | | | +-+ +-+-+ +-+-+ +-+-+ | | | | | | | + + + +-+-+ + + +-+ + | | | | +-+-+-+-+ +-+-+ +-+-+ | | | | | | + + +-+ +-+ + + + +-+ | | | | | | + +-+ + +-+ + +-+ + + | |S | | F| | | +-+-+-+-+-+-+-+-+-+-+ Results of running program on sample maze: CS 150
Recursion vs. Iteration Note that many problems that can be solved recursively can just as easily be solved via iteration (i.e., looping). In fact, the use of recursion when an iterative solution can be found is rather wasteful, forcing the operating system to do extra work and taking the risk that a stack overflow will occur. When To Use Recursion Instead Of Iteration When a straightforward iterative solution cannot be found. CS 150
The Inefficiency of Recursion One of the big drawbacks to using recursion is the inherent inefficiency of many recursive algorithms. For instance, our maze solving routine frequently revisits coordinates that have already been seen and eliminated as possible steps in the path! On the other hand, some recursive algorithms are vast improvements over alternative solutions and are implemented with very simple code. How To Measure The Efficiency Of A Recursive Algorithm Study Math 224 real hard, and take CS 240 and 340! CS 150