// Let price[1..N] be a constant array of prices.
// So price[i] is the cost of a churro of length i.
CutChurro(n):
if n = 0
return 0
maxTotPrice <- -1
for i <- 1 to n
totPriceTry <- price[i] + CutChurro(n-i)
if totPriceTry > maxTotPrice
maxTotPrice <- totPriceTry
return maxTotPrice
// Let price[1..N] be a constant array of prices.
// So price[i] is the cost of a churro of length i.
CutChurro(n):
if n = 0
return 0
maxTotPrice <- -1
for i <- 1 to n
totPriceTry <- price[i] + CutChurro(n-i)
if totPriceTry > maxTotPrice
maxTotPrice <- totPriceTry
return maxTotPrice
CutChurro(n)
depends on the Recursion Fairy taking care of CutChurro(n-1)
, CutChurro(n-2)
, …, CutChurro(0)
.CC[0..n]
CC[0]
is the first thing to fill in, then CC[1]
, CC[2]
, … etc, in that order.FastCutChurro(n):
CC[0] <- 0
for i <- 1 to n
maxTotPrice <- 0
for j <- 1 to i
totPriceTry <- price[j] + CC[i-j]
if maxTotPrice < totPriceTry
maxTotPrice <- totPriceTry
CC[i] <- maxTotPrice
return CC[n]
FastCutChurro(n):
CC[0] <- 0
for i <- 1 to n
maxTotPrice <- 0
for j <- 1 to i
totPriceTry <- price[j] + CC[i-j]
if maxTotPrice < totPriceTry
maxTotPrice <- totPriceTry
CC[i] <- maxTotPrice
return CC[n]
FastCutChurro(n):
CC[0] <- 0
for i <- 1 to n
maxTotPrice <- 0
for j <- 1 to i
totPriceTry <- price[j] + CC[i-j]
if maxTotPrice < totPriceTry
maxTotPrice <- totPriceTry
CC[i] <- maxTotPrice
return CC[n]
> system.time(CutChurro(20))
user system elapsed
0.736 0.002 0.738
> system.time(FastCutChurro(20))
user system elapsed
0 0 0
Dynamic programming keeps track of the subproblem solutions, and iteratively builds an array/table of solutions.
Specify the problem as a recursive function, and solve it recursively (with an algorithm or formula).
Build the solutions from the bottom up:
How many operations does it take to transform one string (e.g., DNA sequence) to another? The operations are:
SATURDAY -mutate-> SUTURDAY -mutate-> SUNURDAY -delete-> SUNRDAY -delete-> SUNDAY
SATURDAY -delete-> STURDAY -delete-> SURDAY -mutate-> SUNDAY
KITTEN -mutate-> SITTEN -mutate-> SITTIN -insert-> SITTING
Alignment diagrams: |-bars represent 1 unit of cost.
SATURDAY SATURDAY KITTEN ALGOR I THM
|||| || | | | | || | | ||
SUN DAY S UNDAY SITTING AL TRUISTIC
SATURDAY SATURDAY KITTEN ALGOR I THM
|||| || | | | | || | | ||
SUN DAY S UNDAY SITTING AL TRUISTIC
The rightmost element in an alignment diagram can be:
Try all four, then have the Recursion Fairy optimize the rest.
Table #1 | Table #2 | Table #3 | Table #4 | Table #5 | Table #6 | Table #7 |
---|---|---|---|---|---|---|
Graham | Kristen | John | James | Bri | Trevor | Blake |
Isaac | Josiah | Drake | Andrew | Jack | Talia | Logan |
Levi | Kevin | Claire | Jordan | Grace | Ethan | Nathan |
Suppose A[1..m]
and B[1..n]
are two strings. Suppose also that you have a function EditDistance(i,j)
that returns the minimum edit distance from A[1..i]
to B[1..j]
, as long as \(i<m\) or \(j<n\).
Suppose A[m] = B[n]
(the last characters of the strings match). If the rightmost element of the alignment diagram is a match \(\substack{x \\ | \\ x}\), what is the minimum edit distance from A[1..m]
to B[1..n]
? (Use the EditDistance
function.)
Suppose A[m] != B[n]
(the last characters of the strings don’t match). If the rightmost element of the alignment diagram is a mutation \(\substack{x \\ | \\ y}\), what is the minimum edit distance?
If the rightmost element of the alignment diagram is an insertion \(\substack{- \\ | \\ x}\), what is the minimum edit distance?
If the rightmost element of the alignment diagram is a deletion \(\substack{x \\ | \\ -}\), what is the minimum edit distance?
Let ED[1..m, 1..n]
be an array in which we will store the minimum edit distance from A[1..i]
to B[1..j]
.
ED[i,j]
will be the minimum of the following:
ED[i-1, j-1]
(if A[i] = B[j]
)
ED[i-1, j-1] + 1
(if A[i] != B[j]
)
ED[i, j-1] + 1
ED[i-1, j] + 1
ED[i,j]
is the minimum of the following:
ED[i-1, j-1]
(if A[i] = B[j]
)ED[i-1, j-1] + 1
(if A[i] != B[j]
)ED[i, j-1] + 1
ED[i-1, j] + 1
So the evaluation order must obey the arrows:
\[ \begin{array}{cccccc} \cdots & \mathtt{ED[i-2, j-2]} & \rightarrow & \mathtt{ED[i-2, j-1]} & \rightarrow &\mathtt{ED[i-2, j]} \\ & \downarrow & \searrow & \downarrow & \searrow & \downarrow \\ \cdots & \mathtt{ED[i-1, j-2]} & \rightarrow & \mathtt{ED[i-1, j-1]} & \rightarrow &\mathtt{ED[i-1, j]} \\ & \downarrow & \searrow & \downarrow & \searrow & \downarrow \\ \cdots & \mathtt{ED[i, j-2]} & \rightarrow & \mathtt{ED[i, j-1]} & \rightarrow & \mathtt{ED[i, j]} \\ \end{array} \]
\[ \begin{array}{cccccc} \cdots & \mathtt{ED[i-2, j-2]} & \rightarrow & \mathtt{ED[i-2, j-1]} & \rightarrow &\mathtt{ED[i-2, j]} \\ & \downarrow & \searrow & \downarrow & \searrow & \downarrow \\ \cdots & \mathtt{ED[i-1, j-2]} & \rightarrow & \mathtt{ED[i-1, j-1]} & \rightarrow &\mathtt{ED[i-1, j]} \\ & \downarrow & \searrow & \downarrow & \searrow & \downarrow \\ \cdots & \mathtt{ED[i, j-2]} & \rightarrow & \mathtt{ED[i, j-1]} & \rightarrow & \mathtt{ED[i, j]} \\ \end{array} \]
for i from 1 to m
for j from 1 to n
TODO: populate E[i,j]
ED[1..m, 1..n]
, so space is \(O(mn)\).ED[i,j]
is the minimum of the following:
ED[i-1, j-1]
(if A[i] = B[j]
)ED[i-1, j-1] + 1
(if A[i] != B[j]
)ED[i, j-1] + 1
ED[i-1, j] + 1
for i from 1 to m
for j from 1 to n
TODO: populate E[i,j]
populate
step takes constant time, so time is \(mn \cdot O(1) = O(mn)\)Finally, we can write the algorithm. (Note we add a row and a column of 0’s as a base case.)
EditDistance(A[1 .. m], B[1 .. n]):
Initialize ED[0, *] and ED[*, 0] to *, for *=0,1,2,...
for i <- 1 to m
for j <- 1 to n
insert <- ED[i, j − 1] + 1
delete <- ED[i − 1, j] + 1
if A[i] = B[j]
mutate <- ED[i − 1, j − 1]
else
mutate <- ED[i − 1, j − 1] + 1
ED[i, j] <- min {insert, delete, mutate}
return ED[m, n]
Check out the memoization table.