Cuts: Price:
1111 4
112 7
121 7
13 9
211 7
22 10
31 9
4 9
CutChurro
is calculating by recursive brute force. (not clever)// 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
Count the original call as one call, so CutChurro(0)
makes 1 CutChurro
call:
\[\begin{align} C(0) &= 1\\ C(n) &= C(n-1) + C(n-2) + \cdots + C(1) + C(0) + 1 \end{align}\]
\[\begin{align} C(0) &= 1\\ C(n) &= C(n-1) + C(n-2) + \cdots + C(1) + C(0) + 1 \end{align}\]
Compute the first few terms and you get: \(1,2,4,8,\ldots\), so it looks like the closed-form formula is going to be \(C(n) = 2^n\). Can we prove this?
\[\begin{align} C(n) - C(n-1) &= [C(n-1) + C(n-2) + \cdots + C(1)+ C(0)+ 1] \\ &\phantom{=[C(n-1)} - [C(n-2) + \cdots + C(1) + C(0) + 1] \\ & = C(n-1) \end{align}\]
So we get \(C(n) = 2C(n-1)\), and it is easy to prove by induction that \(C(n) = 2^n\).
CutChurro(0)
makes one call.CutChurro(k)
makes \(2^k\) calls, for \(k<n\).CutChurro(n)
calls CutChurro(n-1)
, CutChurro(n-2)
, …, CutChurro(1)
, CutChurro(0)
, so by inductive hypothesis, it makes \(2^{n-1} + 2^{n-2} + \cdots + 2 + 1 + 1\) calls (counting the original call itself). By the same subtract-and-cancel trick, this equals \(2^n\).CutChurro(0)
is zero, which is the maximum price you can get for nothing at all.CutChurro(k)
returns the maximum price you can get for a \(k\)-inch churro by cutting it up, for \(k<n\).CutChurro(n)
computes the maximum price obtained by cutting a piece of size \(0,1,\ldots n-1\) and then, by inductive hypothesis, maximizing the total price of the other piece. Therefore CutChurro(n)
returns the maximum price.In an inductive proof, your inductive hypothesis should look like the final conclusion of your proof.
\[\begin{align} C(0) &= 0\\ C(n) &= n + \sum\limits_{i=1}^n C(n-i)\\ &= n + C(n-1) + \sum\limits_{i=1}^{n-1} C(n-1-i)\\ &= n + C(n-1) + C(n-1) - (n-1) \\ &= 1 + 2C(n-1) \end{align}\]
Closed-form solution: \(C(n) = 2^n - 1\)
num_inches <- 10:20
elapsed_time = sapply(num_inches,function(n){system.time(CutChurro(n))['elapsed']} )
rbind(num_inches, elapsed_time)
elapsed elapsed elapsed elapsed elapsed elapsed elapsed elapsed
num_inches 10 11.000 12.000 13.000 14.000 15.000 16.000 17.000
elapsed_time 0 0.003 0.005 0.009 0.013 0.023 0.044 0.087
elapsed elapsed elapsed
num_inches 18.000 19.000 20.000
elapsed_time 0.163 0.354 0.667
Backtracking algorithms tend to be slow.
Example:
Meta-Example:
IsWord(A[1..i])
that tells us if a string (i.e., array) is a word in our language.BOTHEARTHANDSATURNSPIN
BOT HEART HANDS AT URNS PIN
BOTH EARTH AND SATURN SPIN
If a string starts with a word, and the rest of the string is splittable into words, then whole string is splittable.
Splittable(A[1..n]):
if n = 0
return TRUE
for i <- 1 to n
if IsWord(A[1..i])
if Splittable(A[(i+1)..n])
return TRUE
return FALSE
Time? (Same recurrence as CutChurro: \(O(2^n)\)).
// 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)
Recursive Substructure:
Form the subproblems by removing some element of \(X\).
Table #1 | Table #2 | Table #3 | Table #4 | Table #5 | Table #6 | Table #7 |
---|---|---|---|---|---|---|
Drake | Trevor | Kevin | Levi | Claire | Jordan | Talia |
Isaac | John | Grace | Bri | James | Ethan | Josiah |
Graham | Blake | Nathan | Jack | Kristen | Andrew | Logan |
SubsetSum
. Form the subproblems by removing the last element of \(X\).// X[1..n] is an array of positive integers (n is constant)
SubsetSum(i, T) : // Does any subset of X[1..i] sum to T?
{
TODO
}
// Call this function as: SubsetSum(n, T)
SubsetSum
calls made by SubsetSum(n)
. Do you recognize it? What’s the closed-form solution?