Pseudocode and English: It is clear what it’s doing and why it works:
solve(n):
If n=1,
flip(1)
If n=2
flip(2)
flip(1)
else (when n>2)
solve(n-2) on the rightmost n-2 bits
flip(n), the nth bit << legal, b/c there are n-2 0's at the end >>
solve(n-2) in reverse on the rightmost n-2 bits << makes Puzzle look like 0111...111 >>
solve(n-1) on rightmost (n-1) bits
Psuedocode only. It’s correct, but it doesn’t communicate as well:
B(int n){
if(n == 0){
print nothing
}
else if (n==1){
print 1
}
else {
B(n-2)
print n
B(n-2), but printed backwards
B(n-1)
}
}
The recurrence relation should match the algorithm:
solve(n):
If n=1,
flip(1)
If n=2
flip(2)
flip(1)
else (when n>2)
solve(n-2) on the rightmost n-2 bits
flip(n), the nth bit << legal, b/c there are n-2 0's at the end >>
solve(n-2) in reverse on the rightmost n-2 bits << makes Puzzle look like 0111...111 >>
solve(n-1) on rightmost (n-1) bits
\[\begin{equation} M(n)=\begin{cases} 1 & \text{if $n=1$}.\\ 2 & \text{if $n=2$}.\\ 2M(n-2)+M(n-1)+1 & \text{if $n > 2$}. \end{cases} \end{equation}\]
If the algorithm is different, the recurrence is different.
B(int n){
if(n == 0){
print nothing
}
else if (n==1){
print 1
}
else {
B(n-2)
print n
B(n-2), but printed backwards
B(n-1)
}
}
\[\begin{equation} M(n)=\begin{cases} 0 & \text{if $n=0$}.\\ 1 & \text{if $n=1$}.\\ 2M(n-2)+M(n-1)+1 & \text{if $n > 2$}. \end{cases} \end{equation}\]
At least two options:
\[ 2^{n+1}/3 - 1/2 - (-1)^n/6 \] or
\[ M\left(n\right) = \left\lceil2\cdot\frac{2^n-1}{3}\right\rceil \] The former is easier; the latter requires cases in the induction argument.
Base case: \(M(1)=1=\frac{2^{2}}{3}-\frac{1}{2}-\frac{(-1)^{1}}{6}\) and \(M(2)=2=\frac{2^{3}}{3}-\frac{1}{2}-\frac{(-1)^{2}}{6}\).
Suppose as inductive hypothesis that \(M(k)=\frac{2^{k+1}}{3}-1/2-\frac{(-1)^{k}}{6}\) for all \(k<n\).
(When in doubt, always use strong induction.)
\[\begin{align} M(n)&=2M(n-2)+M(n-1)+1\\ &=2\left(\frac{2^{n-1}}{3}-\frac{1}{2}-\frac{(-1)^{n-2}}{6} \right) + \left(\frac{2^{n}}{3}-\frac{1}{2}-\frac{(-1)^{n-1}}{6}\right)+1\\ &=\frac{2^{n}}{3}-1-\frac{2(-1)^{n-2}}{6}+\frac{2^{n}}{3}-\frac{1}{2}-\frac{(-1)^{n-1}}{6}+1\\ &=\frac{2^{n}}{3}+\frac{2^{n}}{3}-1+1-\frac{1}{2}-\frac{2(-1)^{n-2}}{6}-\frac{(-1)^{n-1}}{6}\\ &=\frac{2^{n+1}}{3}-\frac{1}{2}+\frac{2(-1)^{n-1}}{6}-\frac{(-1)^{n-1}}{6}\\ &=\frac{2^{n+1}}{3}-\frac{1}{2}+\frac{(-1)^{n-1}}{6}\\ &=\frac{2^{n+1}}{3}-\frac{1}{2}-\frac{(-1)^{n}}{6} \end{align}\]
Your only task is to simplify the original problem, or to solve it directly when simplification is either unnecessary or impossible; the Recursion Fairy will solve all the simpler subproblems for you, using Methods That Are None Of Your Business…
Consider the usual Towers of Hanoi puzzle (pp. 24ff) with \(n\) disks and 3 pegs, numbered 0, 1, and 2. Now suppose you are forbidden to move any disk directly between peg 1 and peg 2; every move must involve peg 0.
Describe a recursive algorithm to solve this puzzle.
Explain why (prove that) your algorithm never moves a disk between peg 1 and peg 2. (Use a strong induction hypothesis.)
Table #1 | Table #2 | Table #3 | Table #4 | Table #5 | Table #6 |
---|---|---|---|---|---|
Jack | Bri | Graham | Blake | James | Ethan |
Nathan | Grace | Logan | Levi | Drake | John |
Josiah | Kristen | Claire | Andrew | Timothy | Talia |
Kevin | Isaac | Jordan | Trevor |
Merge(A[1..n], m): MergeSort(A[1..n]):
i <- 1; j <- m + 1 if n > 1
for k <- 1 to n m <- floor(n/2)
if j > n MergeSort(A[1..m])
B[k] <- A[i]; i <- i + 1 MergeSort(A[(m+1)]..m)
else if i > m Merge(A[1..n],m)
B[k] <- A[ j]; j <- j + 1
else if A[i] < A[j]
B[k] <- A[i]; i <- i + 1
else
B[k] <- A[j]; j <- j + 1
for k <- 1 to n
A[k] <- B[k]
Merge(A[1..n], m): # O(n) comparisons MergeSort(A[1..n]): # C(n) comparisons
i <- 1; j <- m + 1 if n > 1
for k <- 1 to n m <- floor(n/2)
if j > n MergeSort(A[1..m]) # C(n/2) comparisons (approx)
B[k] <- A[i]; i <- i + 1 MergeSort(A[(m+1)]..m) # C(n/2) comparisons (approx)
else if i > m Merge(A[1..n],m) # O(n) comparisons
B[k] <- A[ j]; j <- j + 1
else if A[i] < A[j]
B[k] <- A[i]; i <- i + 1
else
B[k] <- A[j]; j <- j + 1
for k <- 1 to n
A[k] <- B[k]
\[ C(n) = \left\{\begin{array}{ll} 0 & \mbox{if } n=1 \\ 2C(n/2) + O(n) & \mbox{if } n>1 \end{array} \right. \]
\[ C(n) = \left\{\begin{array}{ll} 0 & \mbox{if } n=1 \\ 2C(n/2) + O(n) & \mbox{if } n>1 \end{array} \right. \]
As a tree, we can model \(C(n)\) as:
O(n)
/ \
C(n/2) C(n/2)
Expanding recursively, we get the following recursion tree:
n
/ \
n/2 n/2
/ \ / \
n/4 n/4 n/4 n/4
/ \ / \ / \ / \
n/8 n/8 n/8 n/8 n/8 n/8 n/8 n/8
... and so on
\[ C(n) = \left\{\begin{array}{ll} 0 & \mbox{if } n=1 \\ 2C(n/2) + O(n) & \mbox{if } n>1 \end{array} \right. \]
Total up each level in the recursion tree:
level total
n n
/ \
n/2 n/2 n
/ \ / \
n/4 n/4 n/4 n/4 n
/ \ / \ / \ / \
n/8 n/8 n/8 n/8 n/8 n/8 n/8 n/8 n
... and so on
There are \(\log n\) levels in this tree, so \(C(n) \in O(n \log n)\).
(More to come.)