Data Structures and Algorithms for Information Processing Some Notes on Recursion 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Recursion • We’ve seen several examples of the use of recursion • We’ll take a closer look at recursion as a style of programming • Lends itself to analytical methods; proving program properties 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Verifying Program Properties • How can we be sure that a program is correct? – Debug – Test cases – Make sure output matches another program – ... • None of these give absolute assurance 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Imperative Programming • The “usual” style in Java, using commands • Programs are written by create data (“state”) and storing it in variables • Flow of control insures that correct sequence of assignments is executed 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Applicative Programming • No references to other objects • No side effects (assignments, output...) • Some advantages: – Functions only return values – No need for loops – Easier to prove properties • A different programming style, and a different way to think about programming 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Recursion General pattern: recursive_fn(params) { if (…) return some_value; else ... recursive_fn(new_params) ... } A recursive function call is made somewhere in the body of the function 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Tail Recursion General pattern: tail_recursive_fn(params) { if (…) return some_value; else return tail_recursive_fn(new_params) } Tail recursive: the function does no work after the recursive call 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Tail Recursion “Usual” recursive factorial // Precondition: n >= 0 static int fact1(int n) { if (n==0) return 1; else return n*fact1(n-1); } 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Tail Recursion Tail recursive factorial static int fact2(int n) { // Precondition: n >= 0 return fact2_aux(n, 1); } static int fact2_aux(int n, int accum) { if (n==0) return accum; else return fact2_aux(n-1, n*accum); } 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Execution Trace fact1(5) 5*fact1(4) 5*4*fact1(3) 5*4*3*fact1(2) 5*4*3*2*fact1(1) 5*4*3*2*1*fact1(0) 5*4*3*2*1*1 => 120 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Execution Trace fact2(5) fact2_aux(5,1) fact2_aux(4,5) fact2_aux(3,20) fact2_aux(2,60) fact2_aux(1,120) fact2_aux(0,120) => 120 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Example of Non-Tail Recursion // Precondition: y > 0 static int mult (int x, int y) { if (y==1) return x; else return x + mult(x, y-1); } • Addition operation carried out after the recursive call 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Tail Recursion (cont) • Tail recursive functions can be more efficient • Often easier to prove properties of tail recursive functions – correctness, termination, cost – technique: induction 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Example: fact2 Want to prove using induction that fact2(n) => n! • We do this by proving an appropriate property of the auxiliary function: fact2_aux(n, p) => n! * p 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Example: fact2 (cont) • Base case: n=0 – for all p fact2_aux(0, p) => p = 0! * p • Inductive step: n > 0: – Assume true for n-1 – For all p fact2_aux(n-1, p) =>(n-1)! * p 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Example: fact2 (cont) • Inductive step: n > 0: – Assume true for n-1 – For all p fact2_aux(n-1, p) =>(n-1)! * p – So: fact2_aux(n, p) => fact2_aux(n-1, n*p) => (n-1)! * (n*p) = (n*(n-1)!)*p = n!*p 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Example: fact2 (cont) • Proving termination by induction: – Base case: fact2_aux(0, p) => return p – Inductive case: terminates if operator * terminates 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Tail recursive reverse • We can easily get an O(n) implementation using tail recursion: List rev2_aux(List x, List y) { if (x==null) return y; else return rev2_aux(x.next(), new List(x.value(), y)) } List reverse2(x) { return rev2_aux(x, null); } 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Cost of rev2_aux • Let Cost[n,m] be the number of operations for rev2_aux(x,y) with x.length()=n and y.length() = m • Cost[0,m] = A (constant) • n>0, Cost[n,m] = Cost[n-1,m+1] + B • Thus, Cost[n,m] = A + nB = O(n) 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Fibonacci Numbers • Want fib(0)=1, fib(1)=1, fib(n) = fib(n-1) + fib(n-2) if n>1 • Simple recursive implementation: // Precondition: n>=0 int fib(int n) { if (n < 2) return 1; else return fib(n-1)+fib(n-2); } 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Fibonacci Numbers • Cost is the same as the Fibonacci numbers themselves! • fib(n) rises exponentially with n: – fib(n) > (1.618)^n / 2 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Imperative version int i=1; int a=1, b=1; while (i<n) { int c = a+b; // fib(i+1) a = b; b = c; i++; } 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Recursive Version • Define an auxiliary function fib_aux • Use two accumulator variables, one set to fib(i-1), the other to fib(i) 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Recursive Version int fib_aux (int n, int i, int x, int y) { if (i==n) return y; else return fib_aux(n, i+1, y, x+y); } int fib(int n) { if (n==0) return 1; else return fib_aux(n, 1, 1, 1); } 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Backtracking Search General pattern: • Test if current position satisfies goal • If not, mark current position as visited and make a recursive call to search procedure on neighboring points • Exhaustive search, terminates as soon as goal is found 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Backtracking search: simple example The “bear game”: • Start with initial number of bears • Need to reach goal number within a certain number of steps • Possible moves: – Add incr number of bears – If even divide current number in half • Suppose initial=10, goal=180, incr=50,n=5 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Backtracking search: simple example Public static boolean bears (int initial, int goal, int incr, int n) { if (initial == goal) return true; else if (n==0) return false; else if (bears(initial+incr, goal, incr, n-1)) return true; else if (initial % 2 == 0) return bears(initial/2, goal, incr, n-1); else return false; } 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. Backtracking search: simple example • Why does this program terminate? • What if we remove the restriction on the number of steps? 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved. General Backtracking boolean solve(Configuration conf) { if(no more choices) return (conf is goal) for (each possible choice) { make choice to conf if(solve(conf)) return true Try with: N-Queens unmake choice of conf Sudoku } : return false Etc. } 90-723: Data Structures and Algorithms for Information Processing From Stanford Video on Program Abstractions Lecture 11 Copyright © 1999, Carnegie Mellon. All Rights Reserved. Benefits of Recursion • Recursion can often be implemented efficiently • Requires less work (programming and computation) • Tail recursive versions require less stack space • The code is typically much cleaner than imperative versions • Sometimes easier to prove program properties 90-723: Data Structures and Algorithms for Information Processing Some Notes on Recursion Copyright © 1999, Carnegie Mellon. All Rights Reserved.