290 likes | 411 Views
Data Structures and Algorithms for Information Processing. Some Notes on Recursion. 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.
E N D
Data Structures and Algorithms for Information Processing Some Notes on Recursion Some Notes on Recursion
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 Some Notes on Recursion
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 Some Notes on Recursion
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 Some Notes on Recursion
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 Some Notes on Recursion
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 Some Notes on Recursion
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 Some Notes on Recursion
Tail Recursion • “Usual” recursive factorial // Precondition: n >= 0 static int fact1(int n) { if (n==0) return 1; else return n*fact1(n-1); } Some Notes on Recursion
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); } Some Notes on Recursion
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 Some Notes on Recursion
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 Some Notes on Recursion
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 Some Notes on Recursion
Tail Recursion (cont) • Tail recursive functions can be more efficient • Often easier to prove properties of tail recursive functions • correctness, termination, cost • technique: induction Some Notes on Recursion
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 Some Notes on Recursion
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 Some Notes on Recursion
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 Some Notes on Recursion
Example: fact2 (cont) • Proving termination by induction: • Base case: fact2_aux(0, p) => return p • Inductive case: terminates if operator * terminates Some Notes on Recursion
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); } Some Notes on Recursion
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) Some Notes on Recursion
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); } Some Notes on Recursion
Fibonacci Numbers • Cost is the same as the Fibonacci numbers themselves! • fib(n) rises exponentially with n: • fib(n) > (1.618)^n / 2 Some Notes on Recursion
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++; } Some Notes on Recursion
Recursive Version • Define an auxiliary function fib_aux • Use two accumulator variables, one set to fib(i-1), the other to fib(i) Some Notes on Recursion
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); } Some Notes on Recursion
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 Some Notes on Recursion
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=100, incr=50,n=5 Some Notes on Recursion
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; } Some Notes on Recursion
Backtracking search: simple example • Why does this program terminate? • What if we remove the restriction on the number of steps? Some Notes on Recursion
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 Some Notes on Recursion