350 likes | 443 Views
Recursion. Basic Recursions. Basic Recursions. Break a problem into smaller identical problems Each recursive call solves an identical but smaller problem. Stop the break-down process at a special case whose solution is obvious, (termed a base case )
E N D
Basic Recursions Basic Recursions • Break a problem into smaller identical problems • Each recursive call solves an identical but smaller problem. • Stop the break-down process at a special case whose solution is obvious, (termed a base case) • Each recursive call tests the base case to eventually stop. • Otherwise, we fall into an infinite recursion.
Basic Recursions Example 1: The Sum of the First NPositive Integers • Definition: sum(n)= n + (n-1) + (n-2) + … + 1 for any integer n > 0 • Recursive relation; sum(n)= n + [(n-1) + (n-2) + … + 1] = n +sum(n-1) Looks so nice, but how about n == 1? sum(1) = 1+sum(0), but the argument to sum( ) must be positive • Final recursive definition: sum(n) = 1 if n = 1 (Base case) = n + sum(n-1) if n > 1 (Recursive call)
Basic Recursions Recursive Definition of sum(n) intsum(int n) { if (n == 1) // base case return 1; else return n +sum(n-1); // recursive call A } How should I test this function’s correctness? Use the box method: A box has each function’s local environment ex. cout << sum(3); 1. Arguments 2. Local variables 3. Place holder for the value returned by a called function 4. A return value n = 3 A: sum(n-1) = ? return ?
Basic Recursions n = 2 A: sum(n-1)=? return ? n = 1 return 1 3 1 6 3 Box trace of sum(3) cout << sum(3); n = 3 A: sum(n-1)=? return ? n = 3 A: sum(n-1)= ? return ? n = 2 A: sum(n-1)=? return ? n = 1 return 1 Each box corresponds to a function’s activation record or stack.
Basic Recursions Example 2: The Factorial of n • Definition: factorial(n) = n * (n-1) * (n-2) * … * 1 for any integer n > 0 factorial(0) = 1 • Recursive relation; factorial(n) = n * [(n-1) * (n-2) * … * 1] = n * factorial(n-1) Looks so nice, but how about n = 1? factorial(1) = 1 * factorial(0) but the argument to factorial( ) must be positive • Final recursive definition: factorial(n) = 1 if n = 1 = n *factorial(n-1) if n > 1
Basic Recursions Recursive Definition of facotrial(n) int fact(int n) { if (n == 1) // base case return 1; else return n * fact(n-1); // recursive call A } Trace its execution using the box method: ex. cout << fact(3); n = 3 A: fact(n-1) = ? return ?
Basic Recursions n = 2 A: fact(n-1)=? return ? n = 1 return 1 2 1 6 2 Box trace of fact(3) cout << fact(3); n = 3 A: fact(n-1)=? return ? n = 3 A: fact(n-1)= ? return ? n = 2 A: fact(n-1)=? return ? n = 1 return 1
Basic Recursions Precondition of fact(n) • Precondition: n >= 1 • If the precondition is violated, what happen? • Fact(0) calls fact(-1), which calls fact(-2), …. • For robustness, given a negative number or 0, fact should stop a recursive call. Always correct to recursions int fact(int n) { if (n <=1) // base case for n = 1 or bad args. return 1; else return n * fact(n-1); // recursive call A }
Basic Recursions Example 3: Printing Numbers in Any Base How to convert a decimal to a hexadecimal Dividend Remainder 16) 1234567890(10)……..… 16) 7716049(10)………6 16) 482253(10)………1 16) 30140(10)…13(D) 16) 1883(10)…12(C) 16) 117(10)…11(B) 16) 7(10)……....5 16) 0(10)……....7 2. Print each reminder from the bottom: 75BCD16(16) 1. Divide by a base(16)
Basic Recursions Recursive Definition of printNum(n,base) voidprintNum(int n, int base) { static string DIGIT_TABLE = “0123456789abcdef”; if (n >=base) // recursive call A printNum( n / base, base ); // else n < base base case cout << DIGIT_TALBE[ n % base ]; } Trace its execution using the box method: ex. cout << print(123456789, 16); n = 123456789, base = 16 A: fact(n-1) returns nothing Print: n % base = ? return nothing
Basic Recursions Example 4: Binary Search An entire sorted list First half Second half • The search divides a list into two small sub-lists till a sub-list is no more divisible. • Each step needs the top/tail indexes and a target item. First half Second half First half Can we call binarySearch( ) recursively?
Basic Recursions Binary Search (Recursive Version) int binarySearch( const int array[], int top, int tail, int target ) { int mid = (top + tail)/2; if ( target == array[mid] ) return mid; else if ( target < array[mid] ) return binarySearch( array, top, mid-1, target ); else if ( target > array[mid] ) return binarySearch( array, mid+1, tail, target ); } Question: if it does not find the target, how can we stop this program?
Basic Recursions Binary Search Revisited i i+1 35 47 top tail mid = (top + tail)/2 = (i + i+1)/2 = i top = mid + 1 = i + 1 In the next recursive call, top = i + 1; tail = i; thus top > tail!! Base cases: (1) if (target == array[mid]) return mid; (2) if top > tail return –1;
Basic Recursions Binary Search (Correct Recursive Version) int binarySearch( const int array[], int top, int tail, int target ) { int mid = (top + tail)/2; if ( target == array[mid] ) return mid; // find it else if ( top > tail ) return -1; // cannot find it else if ( target < array[mid] ) return binarySearch( array, top, mid-1, target ); else if ( target > array[mid] ) return binarySearch( array, mid+1, tail, target ); }
Basic Recursions Example 5: Multiplying Mice Month 1 Month 2 Quite difficult to keep track of the population explosion. Month 3 Month 4 Month 5 Month 6 Babies Month 7 Recursive relation: #babies in Month 7= #mice in Month 5 #adults in Month 7 = #mice in Month 6
Basic Recursions The Fibonacci Sequence • mice(n) = mice(n-1) + mice(n-2) • Then, what are base cases? • Recursive definition: mice(1) = mice(0) + mice(-1) can’t happen. Thus, mice(1) = 1; mice(2) = mice(1) + mice(0) can’t happen, either. Thus, mice(2) = 1; mice(n) = 1 if n = 1 or 2 mice(n-1) + mice(n-2) if n > 2
Basic Recursions m(5) Return m(4)+m(3) m(4) Return m(3)+m(2) m(4) Return m(3)+m(2) m(3) Return m(2)+m(1) m(3) Return m(2)+m(1) m(2) Return 1 m(3) Return m(2)+m(1) m(2) Return 1 m(2) Return 1 m(1) Return 1 m(2) Return 1 m(1) Return 1 m(2) Return 1 m(1) Return 1 Tracing mice(n) m(6) Return m(5)+m(4) 8 5 3 3 2 2 1 2 1 1 1 1 1 1 1
Recursive Applications Recursive Applications • Discrete Mathematics (Combinatorics, Puzzles, Coding Theory) • Tower of Hanoi and Gray Code • Divide and Conquer • Mergesort, Convex Hall, and Fast Fourier Transform • Backtrack • 8 Queens, Maze and Classic Chess Program • Fractal Figures • Koch, Sierpinski Allowhead, Gosper, Hilbert, and Dragon curves
Recursive Applications Towers of Hanoi A B C Find how to move dishes from A to B using C. Restrictions:
Recursive Applications A B C A B C A B C A B C Solution In order to move n dishes from A to B via C, move(n, A, B, C) if we could move n-1 dishes from A to C via B, move(n-1, A, C, B) we could move a dish, (i.e., the last one) from A to B (via C), move(1, A, B, C) and thereafter, we would move n-1 Dishes from C to B via A! move(n-1, C, B, A)
Example Code #include <iostream> #include <stdlib.h> using namespace std; void move( int n, char orig, char dest, char temp ) { if ( n == 1 ) cout << "move disk 1 from " << orig << " to " << dest << endl; else { move( n - 1, orig, temp, dest ); cout << "move disk " << n << " from " << orig << " to " << dest << endl; move( n - 1, temp, dest, orig ); } } int main( int argc, char* argv[] ) { int nDishes = atoi( argv[1] ); move( nDishes, 'A', 'B', 'C' ); cout << "completed" << endl; return 0; }
Recursive Applications Tracing move(n, A, B, C) move(3, A, B, C) 4 move(2, A, C, B) move(1, A, B, C) move(2, C, B, A) Locally, A is A, C is B, B is C. Locally C is A, B is B, A is C. 1 5 move(1, C, A, B) move(1, A, B, C) 2 6 move(1, C, B, A) move(1, A, C, B) 7 3 move(1, A, B, C) move(1, B, C, A)
Recursive Applications Divide-and-Conquer AlgorithmsComputational Geometry – Finding a Convex Hall • Divide: • Divide a problem into two sub-problems recursively so that the sub-problems are calculated by themselves. • Conquer: • Form a new solution from the solutions answered by the sub-problems The convex hull problem Trivial algorithm: O(n2) Divide & conquer: O(nlogn)
Recursive Applications Q Q Q Q Q Q Q Q BacktrackTrace every combination until encountering a solution • The eight queens problem • Place 8 queens on a 8 * 8 chessboard so that no queen can attack any other queen. • Place a queen from the column 0 to 7 as checking if the queens placed on the former columns can attack the current queen.
Recursive Applications BacktrackThe Eight Queen Problem • A Naïve Solution Q ?? for ( int col = 0; col < 7; col++ ) { for ( int row = 0; row < 7; row++ ) { if ( safeLocation( table, row, col ) ) { table[row][col] = true; break; } } // oops! No safe row to place a new queen // I got to go back to the previous column! } Q Q Q Q Q • Then, how can I go back to the previous column? Just col--?
Recursive Applications BacktrackThe Eight Queen Problem • A Recursive Solution: [0,0] -> [0,1] NO [1,1] NO [2,1] -> [0,2] NO [1,2] NO [2,2] NO [3,2] NO [4,2] -> [0,3] NO [1,3] -> [0,4] NO [1,4] NO [2,4] NO [3,4] -> [0,5] NO [1,5] NO [2,5] NO [3,5] NO [4,5] NO [5,5] NO [6,5] NO [7,5] NO [4,4] NO [5,4] NO [6,4] NO [7,4] -> [0,5] Q ?? Q Q Q Q Q
Recursive Applications BacktrackThe Eight Queen Problem bool addQueen( bool t[SIZE][SIZE], int col ) { if ( col >= SIZE ) return true; // all cols have been examined for ( int row = 0; row < SIZE; row++ ) { if ( safeLocation( t, row, col ) ) { // this row may be a candidate t[row][col] = true; // place a new queen; if ( addQueen( t, col + 1 ) ) return true; // all the following cols were filled else t[row][col] = false; // A wrong position. Try the next row } } return false; // all rows examined, but no candidates } int main( ) { bool table[SIZE][SIZE]; // there are no matrix templates in STL init( table ); // all table[i][j] = false; if ( addQueen( table, 0 ) ) print( table ); else cout << "no solution" << endl; }
Recursive Applications Fractal CurvesDrawing a Ruler // java code to draw a ruler void drawRuler( Graphics g, int left, int right, int level ) { if ( level < 1 ) return; int mid = ( left + right ) / 2; g.drawLine( mid, 80, mid, 80 – level * 5 ); drawRuler( g, left, mid – 1, level – 1 ); drawRuler( g, mid + 1, right, level – 1 ); } mid at level 1 mid at level 1 mid at level 1 mid at level 1 Initial right Initial left mid at level 2 mid at level 2 mid at level 3
Recursive Applications Fractal CurvesDrawing a Koch Curve using a Turtle class Turtle { public: Turtle( float initX=0.0, float initY=0.0, float initAngle=0.0 ) { ~Turtle( ); void draw( float d ); // draw a line from the current position for distance d void move( float d ); // simply move from the current position for distance d void turn( float a ); // turn left by angle a private: float angle; // presented in degree but not in radian oftream out; }; int main( ) { Turtle t; t.draw( 10 ); t.turn( 45 ); t.move( 10 ); t.draw( 10 ); }
Recursive Applications Fractal CurvesDrawing a Koch Curve using a Turtle class Koch : Turtle { public: Koch( float initX=0.0, float initY=0.0, float initAngle=0.0) : Turtle( initX, initY, initAngle ) { ) ~Koch( ); curve( int level, float length ) { if ( level > 1 ) { curve( level – 1, length ); turn( 60 ); curve( level – 1, length ); turn( -120 ); curve( level – 1, length ); turn( 60 ); curve( level – 1, length ); } draw( length ); } }; int main( ) { Koch k(100, 100, 0); k.curve( 3, 10 ); } Draw the level-3 Koch curve here
Efficiency Recursion and Efficiency • Programmability • Capable of breaking down a complex problem • Based on mathematical induction • Efficiency • The overhead associated with function calls (Bookkeeping the current stack and creating a new one) • The inherent inefficiency of some algorithm (Repeating the same computation)
Efficiency m(5) Return m(4)+m(3) m(4) Return m(3)+m(2) m(4) Return m(3)+m(2) m(3) Return m(2)+m(1) m(3) Return m(2)+m(1) m(2) Return 1 m(3) Return m(2)+m(1) m(2) Return 1 m(2) Return 1 m(1) Return 1 m(2) Return 1 m(1) Return 1 m(2) Return 1 m(1) Return 1 Efficiency of mice(n) O(2n) m(6) Return m(5)+m(4)
Efficiency Iterative Solution of mice(n) int iterativeMice(int n) { int previous = 1; int current = 1; int next = 1; for (int i = 3; i <= n; ++i) { next = current + previous; previous = current; current = next; } return next; } Efficiency of mice(n): O(n-2)=O(n)
Efficiency Recursion versus Iteration Problems Recursion Iteration Fibonacci sequence O(2n) O(n) Sum/Fact/printNum O(n) O(n) function call overhead Binary search O(log n) O(log n) Tower of Hanoi O(2n) Fractal figures (Koch) O(4n) Can you program it? N queens O(Nn) • Discover a recursive solution if it is easier. • Convert it to iteration if an iterative solution is more efficient. • When a recursion incurs more iterations • When each iteration includes a few computation