Sept 23, 2022
Computational problems can be recursive too!
439! =439â‹…438!
Recursive Case: n!=n⋅(n−1)!
Base Case: 1!=1
def factorial(n: Int): Long =
if(n <= 1){ 1 }
else { n * factorial(n-1) }
if(n <= 1){ 1 }
Recursive Case:
else { n * factorial(n-1) }
1, 1, 2, 3, 5, 8, 13, 21,
Base Cases: fib(1)=1, fib(2)=1
Recursive Cases: fib(n)=fib(n−1)+fib(n−2)
def fib(n: Int): Long =
if(n < 2){ 1 }
else { fib(n-1) + fib(n-2) }
if(n < 2){ 1 }
Recursive Case:
else { fib(n-1) + fib(n-2) }
Live demo!
Task: Move n blocks from A to C
Base Case (n=1)
Recursive Case (n≥2)
var towers = Array(new Stack(), new Stack(), new Stack())
def move(fromTower: Int, toTower: Int, numDisks: Int): Unit =
{
val otherTower = (Set(0, 1, 2) - fromTower - toTower).head
if(numDisks == 1){
moveOne(from = fromTower, to = toTower)
} else {
move(fromTower, otherTower, numDisks-1)
moveOne(from = fromTower, to = toTower)
move(otherTower, toTower, numDisks-1)
}
}
How do we get the complexity of recursive algorithms?
What's the complexity? (in terms of n)
def factorial(n: Int): Long =
if(n <= 1){ 1 }
else { n * factorial(n-1) }
Idea: Write down a recursive runtime growth function
What's the complexity? (in terms of n)
def factorial(n: Int): Long =
if(n <= 1){ 1 }
else { n * factorial(n-1) }
Base Case (n≤0) Θ(1)
Recursive Case (n>0) T(n−1)+Θ(1)
Solve for T(n)
Solve for T(n)
Approach:
Approach: Write out the rule for increasing n
Θ(1), 2Θ(1), 3Θ(1), 4Θ(1), 5Θ(1), 6Θ(1), 7Θ(1), …
What's the pattern?
Hypothesis: T(n)∈O(n)
(there is some c>0 such that T(n)≤c⋅n)
Let's make the constants explicit
T(n)={c0if n≤0T(n−1)+c1otherwiseSolve for T(n)
T(1)≤c⋅1
T(1)≤c
c0≤c
True for any c≥c0
T(2)≤c⋅2
T(1)+c1≤2c
c0+c1≤2c
We know there's a c≥c0, so... c1≤c
True for any c≥c1
T(3)≤c⋅3
T(2)+c1≤3c
We know there's a c s.t. T(2)≤2c,
... so if we show that 2c+c1≤3c, then T(2)+c1≤2c+c1≤3c
c1≤c
True for any c≥c1
T(4)≤c⋅4
T(3)+c1≤4c
We know there's a c s.t. T(3)≤3c,
...so if we show that 3c+c1≤4c, then T(3)+c1≤3c+c1≤4c
c1≤c
True for any c≥c1
Hey... this looks like a pattern!
Approach: Assume the hypothesis is true for any n′<n;
use that to prove for n
Assume: There is a c>0 s.t. T(n−1)≤c⋅(n−1)
Prove: There is a c>0 s.t. T(n)≤c⋅n
T(n−1)+c1≤c⋅n
By the inductive assumption, there is a c s.t. T(n−1)≤(n−1)c, so if we show that (n−1)c+c1≤nc, then T(n−1)+c1≤(n−1)c+c1≤nc
(n−1)c+c1−(n−1)c≤nc−(n−1)c
c1≤c
True for any c≥c1
def factorial(n: Int): Long =
if(n <= 1){ 1 }
else { n * factorial(n-1) }
How much space is used?
def factorial(n: Int): Long =
if(n <= 1){ 1 }
else { n * factorial(n-1) }
↳ the compiler can (sometimes) figure this out on its own ↴
def factorial(n: Int): Long =
{
var total = 1l
for(i <- 1 until n){ total *= i }
return total
}
If the last action in the function is a recursive call, the compiler will turn it into a loop.
Scala: Add @tailrec to a function to get the compiler to yell at you if it can't convert the function.
Time permitting...
What's the complexity? (in terms of n)
def fib(n: Int): Long =
if(n < 2){ 1 }
else { fib(n-1) + fib(n-2) }
Solve for T(n)
Divide and Conquer
Recursion Trees