SubsetSum(i,T): // Does any subset of X[1..i] sum to T?
if T = 0
return 1
else if T < 0 or i = 0
return 0
else
with <- SubsetSum(i-1, T − X[i])
without <- SubsetSum(i-1, T)
return (with + without)
SubsetSum(k,T)
returns the number of subsets of X[1..k]
summing to \(T\), for \(k<i\).X[i]
and not using X[i]
, so the total number of ways is the sum of these two.Break a string into words: BOTHEARTHANDSATURNSPIN
// A[1..n] is a string to test to see if it is splittable. (n is constant)
// IsWord(i,j) returns true iff A[i..j] is a word.
Splittable(i): // Is A[i..n] splittable?
if i > n
return TRUE
for j <- i to n
if IsWord(i, j)
if Splittable(j+1)
return TRUE
return FALSE
// Call this function as: Splittable(1)
// A[1..n] is a string (n is constant)
// IsWord(i,j) returns true iff A[i..j] is a word.
PartitionCount(i): // how many ways can A[i..n] split into words?
if i > n
return 1
total <- 0
for j <- i to n
if IsWord(i, j)
total <- total + PartitionCount(j+1)
return total
PartitionCount(k)
returns the number of splits of A[k..n]
for \(k > i\).A[i..n]
for a word. For each that is found, increment total
by the number of splits of the remainder, which is correctly determined, by inductive hypothesis.Consider the recursion tree for TOPARTISTOIL
.
Solution: Save the subproblem calculations (usually in an array), and refer to the array instead of making a recursive call.
\[ F(n) = \left\{\begin{array}{ll} 1 & \mbox{if } n = 0 \mbox{ or } n = 1 \\ F(n-1) + F(n-1) & \mbox{if } n>1 \end{array} \right. \] Brute force recursion (exponential time):
RFib(n):
if n = 0
return 0
else if n = 1
return 1
else
return RFib(n − 1) + RFib(n − 2)
Top-down evaluation wastes a lot of effort:
RFib(7) = RFib(6) + RFib(5)
= RFib(5) + RFib(4) + RFib(4) + RFib(3)
= RFib(4) + RFib(3) + RFib(3) + RFib(2) + RFib(3) + RFib(2) + RFib(2) + RFib(1)
= etc...
Memoization: Avoid repeated recursive calls by storing every result in an array. Instead of making a recursive call, just use the array value (if defined).
MemFibo(n):
if n = 0
return 0
else if n = 1
return 1
else
if F[n] is undefined
F[n] <- MemFibo(n − 1) + MemFibo(n − 2)
return F[n]
Notice:
IterFibo(n):
F[0] <- 0
F[1] <- 1
for i <- 2 to n
F[i] <- F[i − 1] + F[i − 2]
return F[n]
Specify the problem as a recursive function, and solve it recursively (with an algorithm or formula).
Build the solutions from the bottom up:
// A[1..n] is a string (n is constant)
// IsWord(i,j) returns true iff A[i..j] is a word.
PartitionCount(i): // how many ways can A[i..n] split into words?
if i > n
return 1
total <- 0
for j <- i to n
if IsWord(i, j)
total <- total + PartitionCount(j+1)
return total
// Call this function as PartitionCount(1)
Time: \(O(2^n)\)
Table #1 | Table #2 | Table #3 | Table #4 | Table #5 | Table #6 | Table #7 |
---|---|---|---|---|---|---|
Isaac | Trevor | James | Kristen | John | Bri | Logan |
Jack | Blake | Grace | Nathan | Jordan | Andrew | Josiah |
Talia | Ethan | Graham | Kevin | Claire | Levi | Drake |
Identify the subproblems. In order for PartitionCount(i)
to return a value, which subproblems need to return values? (i.e., what does the Recursion Fairy need to take care of?)
Find an evaluation order. Let PC
be an array of numbers for filling in the solutions, so our job is to put the return value of PartitionCount(i)
in PC[i]
, for all i
. What is the first value of PC
that you can fill in? Based on that first value, what’s the second value you can fill in? Third?
Write an iterative algorithm to fill in the elements of PC
.
What are the space and time of your algorithm?