1 / 38

Data Structures and Algorithms for Information Processing

Data Structures and Algorithms for Information Processing. Lecture 8: 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.

Download Presentation

Data Structures and Algorithms for Information Processing

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Data Structures and Algorithms for Information Processing Lecture 8: Recursion Lecture 8: Recursion

  2. 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 Lecture 8: Recursion

  3. 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 Lecture 8: Recursion

  4. 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 Lecture 8: Recursion

  5. 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 Lecture 8: Recursion

  6. 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 Lecture 8: Recursion

  7. 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 Lecture 8: Recursion

  8. Tail Recursion • “Usual” recursive factorial // Precondition: n >= 0 static int fact1(int n) { if (n==0) return 1; else return n*fact1(n-1); } Lecture 8: Recursion

  9. Tail Recursion • Tail recursive factorial static int fact2(int n) { // Invariant: 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); } Lecture 8: Recursion

  10. 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 Lecture 8: Recursion

  11. 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 Lecture 8: Recursion

  12. 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 Lecture 8: Recursion

  13. Tail Recursion (cont) • Tail recursive functions can be more efficient • Often easier to prove properties of tail recursive functions • correctness, termination, cost • technique: induction Lecture 8: Recursion

  14. 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 Lecture 8: Recursion

  15. 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 Lecture 8: Recursion

  16. 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 Lecture 8: Recursion

  17. Example: fact2 (cont) • Proving termination by induction: • Base case: fact2_aux(0, p) => return p • Inductive case: terminates if operator * terminates Lecture 8: Recursion

  18. 7 head null 10 15 5 head -1 null Proving Properties of Programs that use Lists • List concatenation: A^B Lecture 8: Recursion

  19. Append • Input: Two lists x and y • Output: x ^ y List append (List x, List y) { if (x == null) return y; else return new List(x.value(), append(x.next(), y)); } Lecture 8: Recursion

  20. Append (cont) • Want to prove: • Terminates • Returns x^y • Cost is O(n) Lecture 8: Recursion

  21. Termination • Base case: x.length()=0 • append(x,y) => append(null, y) => y • Inductive case: x.length()=k-1>0 • append(x’,y) terminates for x’ with x’.length() < k • append(x.next(),y) terminates • constructor terminates Lecture 8: Recursion

  22. Correctness of append • Base case: x.length()=0 • append(x,y) => append(null, y) = empty list ^ y = x ^ y • Inductive case: x.length()=k-1>0 • assumption: append(x.next(), y) => (list referred to by x.next) ^ y • new List(x.value(), append(x.next(), y)) => x ^ y Lecture 8: Recursion

  23. Cost of append • Let Cost[n,m] be the number of operations for append(x,y) with x.length()=n and y.length() = m • Cost[0,m] = A (constant) • For n>0, Cost[n,m] = Cost[n-1,m] + B • Thus, Cost[n,m] = A + nB = O(n) Lecture 8: Recursion

  24. 7 head null 10 15 7 15 10 head null Reversing Lists • List reversal: Rev[x] Lecture 8: Recursion

  25. Reversing Lists: first implementation List reverse1 (List l) { if (l == null) return null; else { return append(reverse(l.next()), new List(l.value())); } } Lecture 8: Recursion

  26. Cost of reverse1 • Cost[n] is cost of reversing list with length n • Cost[0] = A (constant) • Cost[n] = B + Cost[n-1] + (n-1)C • Solving the recurrence gives Cost[n] = O(n^2) Lecture 8: Recursion

  27. 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); } Lecture 8: Recursion

  28. 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) Lecture 8: Recursion

  29. 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); } Lecture 8: Recursion

  30. Fibonacci Numbers • Cost is the same as the Fibonacci numbers themselves! • fib(n) rises exponentially with n: • fib(n) > (1.618)^n / 2 Lecture 8: Recursion

  31. 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++; } Lecture 8: Recursion

  32. Recursive Version • Define an auxiliary function fib_aux • Use two accumulator variables, one set to fib(i-1), the other to fib(i) Lecture 8: Recursion

  33. 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); } Lecture 8: Recursion

  34. 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 Lecture 8: Recursion

  35. 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 • Divide current number in half Lecture 8: Recursion

  36. 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; } Lecture 8: Recursion

  37. Backtracking search: simple example • Why does this program terminate? • What if we remove the restriction on the number of steps? Lecture 8: Recursion

  38. 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 Lecture 8: Recursion

More Related