Data Structures and Algorithms for Information Processing
-
Upload
latifah-pugh -
Category
Documents
-
view
45 -
download
1
description
Transcript of Data Structures and Algorithms for Information Processing
90-723: Data Structuresand Algorithms for
Information ProcessingLecture 8: Recursion
Copyright © 1999, Carnegie Mellon. All Rights Reserved.
Data Structures and Algorithms for Information
Processing
Lecture 8: Recursion
90-723: Data Structuresand Algorithms for
Information ProcessingLecture 8: 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 Structuresand Algorithms for
Information ProcessingLecture 8: 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 Structuresand Algorithms for
Information ProcessingLecture 8: 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 Structuresand Algorithms for
Information ProcessingLecture 8: 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 Structuresand Algorithms for
Information ProcessingLecture 8: Recursion
Copyright © 1999, Carnegie Mellon. All Rights Reserved.
RecursionRecursion 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 Structuresand Algorithms for
Information ProcessingLecture 8: Recursion
Copyright © 1999, Carnegie Mellon. All Rights Reserved.
Tail RecursionTail 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 Structuresand Algorithms for
Information ProcessingLecture 8: Recursion
Copyright © 1999, Carnegie Mellon. All Rights Reserved.
Tail RecursionTail 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 Structuresand Algorithms for
Information ProcessingLecture 8: Recursion
Copyright © 1999, Carnegie Mellon. All Rights Reserved.
Tail RecursionTail 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);
}
90-723: Data Structuresand Algorithms for
Information ProcessingLecture 8: 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 Structuresand Algorithms for
Information ProcessingLecture 8: 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 Structuresand Algorithms for
Information ProcessingLecture 8: Recursion
Copyright © 1999, Carnegie Mellon. All Rights Reserved.
Example of Non-Tail Recursion
// Precondition: y > 0static 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 Structuresand Algorithms for
Information ProcessingLecture 8: 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 Structuresand Algorithms for
Information ProcessingLecture 8: 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 Structuresand Algorithms for
Information ProcessingLecture 8: 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 Structuresand Algorithms for
Information ProcessingLecture 8: 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 Structuresand Algorithms for
Information ProcessingLecture 8: 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 Structuresand Algorithms for
Information ProcessingLecture 8: Recursion
Copyright © 1999, Carnegie Mellon. All Rights Reserved.
Proving Properties of Programs that use ListsProving Properties of
Programs that use Lists
• List concatenation: A^B
10
7
nullhead
15
-1
null
5head
90-723: Data Structuresand Algorithms for
Information ProcessingLecture 8: Recursion
Copyright © 1999, Carnegie Mellon. All Rights Reserved.
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));}
90-723: Data Structuresand Algorithms for
Information ProcessingLecture 8: Recursion
Copyright © 1999, Carnegie Mellon. All Rights Reserved.
Append (cont)
• Want to prove:
– Terminates
– Returns x^y
– Cost is O(n)
90-723: Data Structuresand Algorithms for
Information ProcessingLecture 8: Recursion
Copyright © 1999, Carnegie Mellon. All Rights Reserved.
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
90-723: Data Structuresand Algorithms for
Information ProcessingLecture 8: Recursion
Copyright © 1999, Carnegie Mellon. All Rights Reserved.
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
90-723: Data Structuresand Algorithms for
Information ProcessingLecture 8: Recursion
Copyright © 1999, Carnegie Mellon. All Rights Reserved.
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)
90-723: Data Structuresand Algorithms for
Information ProcessingLecture 8: Recursion
Copyright © 1999, Carnegie Mellon. All Rights Reserved.
Reversing ListsReversing Lists
• List reversal: Rev[x]
10
7
nullhead
15
7 10
nullhead
15
90-723: Data Structuresand Algorithms for
Information ProcessingLecture 8: Recursion
Copyright © 1999, Carnegie Mellon. All Rights Reserved.
Reversing Lists: first implementation
List reverse1 (List l) { if (l == null) return null; else { return append(reverse(l.next()), new List(l.value())); }}
90-723: Data Structuresand Algorithms for
Information ProcessingLecture 8: Recursion
Copyright © 1999, Carnegie Mellon. All Rights Reserved.
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)
90-723: Data Structuresand Algorithms for
Information ProcessingLecture 8: 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 Structuresand Algorithms for
Information ProcessingLecture 8: 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 Structuresand Algorithms for
Information ProcessingLecture 8: 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>=0int fib(int n) { if (n < 2) return 1; else return fib(n-1)+fib(n-2);}
90-723: Data Structuresand Algorithms for
Information ProcessingLecture 8: 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 Structuresand Algorithms for
Information ProcessingLecture 8: 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 Structuresand Algorithms for
Information ProcessingLecture 8: 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 Structuresand Algorithms for
Information ProcessingLecture 8: 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 Structuresand Algorithms for
Information ProcessingLecture 8: 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 Structuresand Algorithms for
Information ProcessingLecture 8: 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– Divide current number in half
90-723: Data Structuresand Algorithms for
Information ProcessingLecture 8: 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 Structuresand Algorithms for
Information ProcessingLecture 8: 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 Structuresand Algorithms for
Information ProcessingLecture 8: Recursion
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