Exercises · Recursion¶
Exercises · Chapter 14 · each solution opens with a click · Chapter 14: Recursion · Exercises, volume 5 (PDF)
In the exercises of this chapter the steps of a recursive algorithm are counted with the conventions of the chapters «Running time» and «Recursion», and the recurrences are solved by simple unrolling.
-
Every execution of a numbered line of pseudo-code is one step; a line that contains a recursive call counts as one step plus all the steps executed by that call.
-
\(C(n)\) denotes the number of calls (nodes of the tree of recursive calls) and \(S(n)\) the number of steps; the depth of the recursion is the maximum number of nodes on a path from the root to a leaf of the tree, i.e., the maximum number of stack frames simultaneously in memory.
-
Strong induction: if \(P(n_0)\) is true and, for every \(n>n_0\), the truth of \(P(k)\) for every \(k\) with \(n_0\le k<n\) implies the truth of \(P(n)\), then \(P(n)\) is true for every \(n\ge n_0\).
-
Unrolling a recurrence \(S(n)=S(n-1)+c\) with constant \(c\):
\[ S(n)=S(n-1)+c=S(n-2)+2c=\cdots=S(n-k)+k\,c, \]and the base case is reached for the value of \(k\) that makes \(n-k\) a base case.
Exercise 14.1: Recursion (E1)
Consider the following recursive algorithm.
Algorithm: Mystery
Questions
-
Draw the tree of recursive calls of
Mystery\((6)\), writing next to each call the value it returns. -
What does the algorithm compute? Prove your claim by strong induction on \(n\).
-
Let \(C(n)\) be the number of calls executed by
Mystery\((n)\). Write a recurrence for \(C(n)\) and prove that \(C(n) = 2n - 1\) for every \(n \ge 1\). Check the formula on the tree of question 1. -
What is the depth of the recursion of
Mystery\((6)\), i.e., the maximum number of stack frames simultaneously in memory?
Solution
-
We have \(\lfloor 6/2 \rfloor = \lceil 6/2 \rceil = 3\), \(\lfloor 3/2 \rfloor = 1\), \(\lceil 3/2 \rceil = 2\) and \(\lfloor 2/2 \rfloor = \lceil 2/2 \rceil = 1\). The tree of recursive calls (returned values in red) is:
Mystery\((2)\) returns \(1+1 = 2\),Mystery\((3)\) returns \(1 + 2 = 3\) andMystery\((6)\) returns \(3 + 3 = 6\). -
The algorithm returns \(n\) itself (it computes \(n\) as a sum of \(n\) ones).
Proof by strong induction on \(n \ge 1\).
-
Base case (\(n=1\)): the algorithm returns \(1\) at line 3.
-
Inductive step (\(n \ge 2\)): assume that
Mystery\((k)\) returns \(k\) for every \(1 \le k < n\). For \(n \ge 2\) we have \(1 \le \lfloor n/2 \rfloor \le \lceil n/2 \rceil < n\), so by the inductive hypothesis line 5 returns\[ \lfloor n/2 \rfloor + \lceil n/2 \rceil = n. \]
Strong induction is necessary because the recursive calls are on \(\lfloor n/2 \rfloor\) and \(\lceil n/2 \rceil\), not on \(n-1\).
-
-
Each call with \(n \ge 2\) makes two recursive calls, hence
\[ C(n) = \begin{cases} 1 & \text{if } n = 1 \\ 1 + C(\lfloor n/2 \rfloor) + C(\lceil n/2 \rceil) & \text{if } n \ge 2 \end{cases} \]Proof of \(C(n) = 2n - 1\) by strong induction on \(n\).
-
Base case (\(n=1\)): \(C(1) = 1 = 2 \cdot 1 - 1\).
-
Inductive step (\(n \ge 2\)): by the inductive hypothesis,
\[ C(n) = 1 + \big(2\lfloor n/2 \rfloor - 1\big) + \big(2\lceil n/2 \rceil - 1\big) = 2\big(\lfloor n/2 \rfloor + \lceil n/2 \rceil\big) - 1 = 2n - 1. \]
For \(n = 6\): \(C(6) = 11\), and indeed the tree of question 1 has \(11\) nodes. (The tree has \(n = 6\) leaves, one for each \(1\) in the sum, and \(n - 1 = 5\) internal nodes, one for each addition.)
-
-
The longest paths from the root to a leaf are, e.g.,
Mystery\((6)\),Mystery\((3)\),Mystery\((2)\),Mystery\((1)\). Hence the depth of the recursion is \(4\): at most \(4\) stack frames are simultaneously in memory, although \(11\) calls are executed. (In general the depth is \(\lceil \log_2 n \rceil + 1\).)
Exercise 14.2: Recursion (E2)
Given an array \(a[1..n]\) of \(n\) integer numbers, consider the problems of computing
-
the sum \(a[1] + a[2] + \dots + a[n]\) (equal to \(0\) if \(n = 0\));
-
the maximum value \(\max\{a[1], a[2], \dots, a[n]\}\) (for \(n \ge 1\)).
Use the array \(a = [3, 8, 1, 6]\) for the numerical questions.
Questions
-
Write a recursive algorithm
RecSum\((a, n)\) that computes the sum of \(a[1..n]\) by reducing the problem to the sub-array \(a[1..n-1]\) (decrease by one). -
Write a recursive algorithm
RecMax\((a, n)\) that computes the maximum of \(a[1..n]\) by reducing the problem to the sub-array \(a[1..n-1]\). -
Show the sequence of calls and returns of
RecSum\((a, 4)\) and ofRecMax\((a, 4)\) for \(a = [3, 8, 1, 6]\). -
Prove the correctness of
RecMaxby induction on \(n\). -
Write the recurrences of the number of steps \(S(n)\) of the two algorithms, solve them by unrolling, and give the depth of the recursion.
Solution
-
The sum of \(a[1..n]\) is the sum of \(a[1..n-1]\) plus \(a[n]\), and the sum of an empty array is \(0\):
Algorithm: RecSum
Input: An array \(a[1..n]\) of \(n \ge 0\) integer numbersOutput: The sum \(a[1] + \dots + a[n]\)1function RecSum(a, n)2if \(n = 0\) then3return \(0\)4end5return RecSum\((a, n-1) + a[n]\)6end -
The maximum of \(a[1..n]\) is the larger between the maximum of \(a[1..n-1]\) and \(a[n]\); the maximum of a single element \(a[1]\) is \(a[1]\):
Algorithm: RecMax
Input: An array \(a[1..n]\) of \(n \ge 1\) integer numbersOutput: The maximum value in \(a[1..n]\)1function RecMax(a, n)2if \(n = 1\) then3return \(a[1]\)4end5\(m :=\) RecMax\((a, n-1)\)6if \(a[n] > m\) then7return \(a[n]\)8end9return \(m\)10end -
For \(a = [3, 8, 1, 6]\):
RecSum\((a,4)\): calls
\[ \texttt{RecSum}(a,4) \to \texttt{RecSum}(a,3) \to \texttt{RecSum}(a,2) \to \texttt{RecSum}(a,1) \to \texttt{RecSum}(a,0), \]then the returns:
call returns RecSum\((a,0)\)\(0\) (base case) RecSum\((a,1)\)\(0 + a[1] = 0 + 3 = 3\) RecSum\((a,2)\)\(3 + a[2] = 3 + 8 = 11\) RecSum\((a,3)\)\(11 + a[3] = 11 + 1 = 12\) RecSum\((a,4)\)\(12 + a[4] = 12 + 6 = 18\) RecMax\((a,4)\): calls
RecMax\((a,4) \to\)RecMax\((a,3) \to\)RecMax\((a,2) \to\)RecMax\((a,1)\), then the returns:call comparison returns RecMax\((a,1)\)– (base case) \(a[1] = 3\) RecMax\((a,2)\)\(m = 3\), \(a[2] = 8 > 3\) \(8\) RecMax\((a,3)\)\(m = 8\), \(a[3] = 1 \le 8\) \(8\) RecMax\((a,4)\)\(m = 8\), \(a[4] = 6 \le 8\) \(8\) The sum is \(18\) and the maximum is \(8\).
-
Claim: for every \(n \ge 1\),
RecMax\((a,n)\) returns \(\max\{a[1], \dots, a[n]\}\).-
Base case (\(n=1\)): the algorithm returns \(a[1] = \max\{a[1]\}\) at line 3.
-
Inductive step (\(n \ge 2\)): by the inductive hypothesis, line 5 assigns
\[ m = \max\{a[1],\dots,a[n-1]\}. \]If \(a[n] > m\), then \(a[n]\) is larger than all the elements of \(a[1..n-1]\) and the algorithm returns \(a[n] = \max\{a[1],\dots,a[n]\}\) (line 7). Otherwise \(a[n] \le m\) and the algorithm returns \(m = \max\{a[1],\dots,a[n]\}\) (line 9).
-
-
RecSum. \(S(0) = 2\) (lines 2–3) and, for \(n \ge 1\), lines 2 and 5 plus the recursive call:
\[ S(n) = S(n-1) + 2 = S(n-2) + 2 \cdot 2 = \dots = S(0) + 2n = 2n + 2 = \Theta(n). \]RecMax. \(S(1) = 2\) (lines 2–3) and, for \(n \ge 2\), lines 2, 5, 6 and one of the lines 7, 9, plus the recursive call:
\[ S(n) = S(n-1) + 4 = S(n-2) + 2 \cdot 4 = \dots = S(1) + 4(n-1) = 4n - 2 = \Theta(n). \]For \(n = 4\): \(S(4) = 10\) for
RecSumand \(S(4) = 14\) forRecMax.The depth of the recursion is \(n+1\) for
RecSum(calls on \(n, n-1, \dots, 0\)) and \(n\) forRecMax(calls on \(n, \dots, 1\)): both use \(\Theta(n)\) memory for the call stack, while the iterative versions (asFindMaximumin the chapter «Running time») use constant memory.If all the entries use a constant number of bits (\(|I| = \Theta(n)\)), the worst-case running time of both
RecSumandRecMaxis \(T(|I|) = \Theta(|I|)\).
Exercise 14.3: Recursion (E3)
An array \(a[1..n]\) is a palindrome if it reads the same from left to right and from right to left, i.e., \(a[i] = a[n+1-i]\) for every \(i \in \{1, 2, \dots, n\}\). Consider the following recursive algorithm, called on the whole array as IsPalindrome\((a, 1, n)\).
Algorithm: IsPalindrome
Questions
-
Show the sequence of calls of
IsPalindrome\((a,1,5)\) for the arrays \(a = [1, 2, 3, 2, 1]\) and \(a = [1, 2, 3, 3, 1]\). -
Prove by induction on the length \(k = r - l + 1\) of the sub-array that the algorithm is correct.
-
Determine the worst-case number of steps \(S(n)\) of
IsPalindrome\((a,1,n)\) and its depth of recursion. -
Is the algorithm tail recursive? If yes, write the equivalent iterative algorithm.
-
Write a recursive algorithm
Reverse\((a, l, r)\) that reverses the sub-array \(a[l..r]\) in place, and show its execution on \(a = [5, 7, 2, 9, 4, 6]\) with \(l = 1\), \(r = 6\).
Solution
-
For \(a = [1, 2, 3, 2, 1]\):
call \(a[l]\) \(a[r]\) action IsPalindrome\((a,1,5)\)\(1\) \(1\) equal \(\Rightarrow\) calls IsPalindrome\((a,2,4)\)IsPalindrome\((a,2,4)\)\(2\) \(2\) equal \(\Rightarrow\) calls IsPalindrome\((a,3,3)\)IsPalindrome\((a,3,3)\)– – \(l \ge r\) \(\Rightarrow\) returns true The value true is returned unchanged by all the calls (\(3\) calls).
For \(a = [1, 2, 3, 3, 1]\):
call \(a[l]\) \(a[r]\) action IsPalindrome\((a,1,5)\)\(1\) \(1\) equal \(\Rightarrow\) calls IsPalindrome\((a,2,4)\)IsPalindrome\((a,2,4)\)\(2\) \(3\) different \(\Rightarrow\) returns false The value false is returned (\(2\) calls).
-
Let \(k = r - l + 1\) be the length of \(a[l..r]\) (\(k \le 0\) means an empty sub-array). We prove by strong induction on \(k\) that the algorithm returns true if and only if \(a[l..r]\) is a palindrome.
-
Base cases (\(k \le 1\), i.e., \(l \ge r\)): an empty array or an array with one element is a palindrome, and the algorithm returns true at line 3.
-
Inductive step (\(k \ge 2\)): \(a[l..r]\) is a palindrome if and only if \(a[l] = a[r]\) and the inner sub-array \(a[l+1..r-1]\) is a palindrome. If \(a[l] \ne a[r]\), the algorithm correctly returns false (line 6). Otherwise it returns the result of the call on \(a[l+1..r-1]\), whose length \(k-2 < k\); by the inductive hypothesis, this result is true if and only if \(a[l+1..r-1]\) is a palindrome, hence if and only if \(a[l..r]\) is a palindrome.
The length decreases by \(2\) at each call, which is why both \(k=0\) and \(k=1\) must be base cases.
-
-
The worst case is when the array is a palindrome (the recursion is never stopped at line 6). Every call with \(l < r\) executes lines 2, 5 and 8 (\(3\) steps), and the final call executes lines 2–3 (\(2\) steps). In terms of the length \(k\):
\[ S(k) = S(k-2) + 3 \quad (k \ge 2), \qquad S(0) = S(1) = 2. \]Unrolling, after \(\lfloor k/2 \rfloor\) steps we reach \(S(0)\) or \(S(1)\):
\[ S(n) = 3\lfloor n/2 \rfloor + 2 = \Theta(n). \]The algorithm makes \(\lfloor n/2 \rfloor + 1\) calls, which is also the depth of the recursion (the tree is a path). For \(n = 5\): \(3\) calls, as in question 1, and \(S(5) = 8\).
-
Yes: the recursive call at line 8 is the last operation and its result is returned unchanged. Replacing the tail call by the update \((l, r) \leftarrow (l+1, r-1)\) we obtain:
Algorithm: IterativeIsPalindrome
Input: An array \(a[1..n]\)Output: true if \(a\) is a palindrome, false otherwise1function IterativeIsPalindrome(a, n)2\(l := 1\)3\(r := n\)4while \(l < r\) do5if \(a[l] \ne a[r]\) then6return false7end8\(l \leftarrow l + 1\)9\(r \leftarrow r - 1\)10end11return true12endIt uses constant memory instead of a stack of depth \(\lfloor n/2 \rfloor + 1\).
-
To reverse \(a[l..r]\): swap the two extreme elements, then reverse the inner sub-array \(a[l+1..r-1]\). A sub-array with at most one element is already reversed (base case, nothing to do).
Algorithm: Reverse
Input: An array \(a\) and two indices \(l, r\)Output: The sub-array \(a[l..r]\) reversed in place1function Reverse(a, l, r)2if \(l < r\) then3swap \(a[l]\) and \(a[r]\)4Reverse\((a, l+1, r-1)\)5end6endExecution on \(a = [5, 7, 2, 9, 4, 6]\):
call action array after the call's swap Reverse\((a,1,6)\)swap \(a[1]=5\), \(a[6]=6\) \([6, 7, 2, 9, 4, 5]\) Reverse\((a,2,5)\)swap \(a[2]=7\), \(a[5]=4\) \([6, 4, 2, 9, 7, 5]\) Reverse\((a,3,4)\)swap \(a[3]=2\), \(a[4]=9\) \([6, 4, 9, 2, 7, 5]\) Reverse\((a,4,3)\)\(l \ge r\): base case, nothing to do \([6, 4, 9, 2, 7, 5]\) The final array is \([6, 4, 9, 2, 7, 5]\), after \(\lfloor 6/2 \rfloor + 1 = 4\) calls and \(3\) swaps. The correctness follows by induction on \(k = r - l + 1\) exactly as in question 2.
Exercise 14.4: Recursion (E4)
Consider the naive recursive algorithm for the Fibonacci numbers (\(F(0) = 0\), \(F(1) = 1\), \(F(n) = F(n-1) + F(n-2)\) for \(n \ge 2\)).
Algorithm: Fib
Questions
-
Draw the tree of recursive calls of
Fib\((6)\) and compute the value returned. -
Count the number of calls \(C(6)\) and check the formula \(C(n) = 2F(n+1) - 1\).
-
How many times is
Fib\((k)\) called, for each \(k = 0, 1, \dots, 6\), during the computation ofFib\((6)\)? What do you observe? -
Compare the number of steps of
Fib(\(S(n) = 4F(n+1) - 2\)) with the number of steps ofIterativeFib(\(S(n) = 5n + 1\)) for \(n = 6\) and for \(n = 20\). -
What is the depth of the recursion of
Fib\((6)\)?
Solution
-
The tree of recursive calls of
Fib\((6)\) (we write F\(k\) forFib\((k)\)):Returning from the leaves to the root:
Fib\((2) = 1 + 0 = 1\),Fib\((3) = 1 + 1 = 2\),Fib\((4) = 2 + 1 = 3\),Fib\((5) = 3 + 2 = 5\),Fib\((6) = 5 + 3 = 8\). Hence \(F(6) = 8\). -
Counting the nodes of the tree level by level: \(1 + 2 + 4 + 8 + 8 + 2 = 25\). The formula gives
\[ C(6) = 2F(7) - 1 = 2 \cdot 13 - 1 = 25. \]Equivalently, from the recurrence \(C(n) = C(n-1) + C(n-2) + 1\) with \(C(0) = C(1) = 1\): \(C(2) = 3\), \(C(3) = 5\), \(C(4) = 9\), \(C(5) = 15\), \(C(6) = 15 + 9 + 1 = 25\).
-
Counting the nodes with the same label:
\(k\) 6 5 4 3 2 1 0 total number of calls Fib\((k)\)1 1 2 3 5 8 5 25 The numbers of calls are themselves Fibonacci numbers:
Fib\((k)\) is called \(F(7-k)\) times for \(k \ge 1\) (andFib\((0)\) is called \(F(5)\) times, once for each callFib\((2)\)). The same subproblems are solved again and again: for example \(F(2)\) is computed \(5\) times, while it would be enough to compute it once and store it. This is the motivation of Dynamic Programming. -
The number of calls and of steps of the two algorithms are:
\(n\) calls \(C(n) = 2F(n+1)-1\) steps of Fib: \(4F(n+1) - 2\)steps of IterativeFib: \(5n+1\)\(6\) \(2 \cdot 13 - 1 = 25\) \(4 \cdot 13 - 2 = 50\) \(31\) \(20\) \(2 \cdot 10946 - 1 = 21891\) \(4 \cdot 10946 - 2 = 43782\) \(101\) For \(n = 6\) the two algorithms are comparable, but for \(n = 20\) the naive algorithm executes more than \(400\) times the number of steps of the iterative one: the first grows exponentially, the second linearly.
-
The longest path from the root to a leaf is
Fib\((6)\),Fib\((5)\),Fib\((4)\),Fib\((3)\),Fib\((2)\),Fib\((1)\): the depth of the recursion is \(6\). Although \(25\) calls are executed, at most \(6\) stack frames are simultaneously in memory.
Exercise 14.5: Recursion (E5)
Consider the Towers of Hanoi with \(n = 4\) disks, numbered \(1, 2, 3, 4\) from the smallest to the largest, initially on peg \(A\), to be moved to peg \(C\) using peg \(B\) as auxiliary peg, with the recursive algorithm:
Algorithm: Hanoi
Questions
-
List all the moves executed by
Hanoi\((4, A, C, B)\), in order. -
Check that the number of moves is \(M(4) = 2^4 - 1\).
-
How many times is each disk moved? Prove that, with \(n\) disks, disk \(k\) is moved exactly \(2^{n-k}\) times (\(\forall k \in \{1, 2, \dots, n\}\)), and check that \(\sum_{k=1}^{n} 2^{n-k} = 2^n - 1\).
-
How many calls of
Hanoiare executed for \(n = 4\), and what is the depth of the recursion? -
According to the legend, \(n = 64\) golden disks are moved at a speed of one move per second. How long does it take?
Solution
-
Hanoi\((4, A, C, B)\) executesHanoi\((3, A, B, C)\), then moves disk \(4\) from \(A\) to \(C\), then executesHanoi\((3, B, C, A)\). Each call with \(n = 3\) moves \(7\) disks (in the same pattern asHanoi\((3, A, C, B)\) in the chapter, with the pegs renamed):move disk from to move disk from to 1 1 A B 9 1 B C 2 2 A C 10 2 B A 3 1 B C 11 1 C A 4 3 A B 12 3 B C 5 1 C A 13 1 A B 6 2 C B 14 2 A C 7 1 A B 15 1 B C 8 4 A C Moves \(1\)–\(7\) transfer disks \(1,2,3\) from \(A\) to \(B\) (call
Hanoi\((3,A,B,C)\)), move \(8\) moves the largest disk, and moves \(9\)–\(15\) transfer disks \(1,2,3\) from \(B\) to \(C\) (callHanoi\((3,B,C,A)\)). -
There are \(15\) moves and \(2^4 - 1 = 15\). Equivalently, \(M(1) = 1\), \(M(2) = 2 \cdot 1 + 1 = 3\), \(M(3) = 2 \cdot 3 + 1 = 7\), \(M(4) = 2 \cdot 7 + 1 = 15\).
-
From the table: disk \(1\) is moved \(8\) times, disk \(2\) is moved \(4\) times, disk \(3\) is moved \(2\) times and disk \(4\) is moved once; these are \(2^{4-1}, 2^{4-2}, 2^{4-3}, 2^{4-4}\).
Proof by induction on \(n\) that, in
Hanoi\((n, \cdot, \cdot, \cdot)\), disk \(k\) is moved \(2^{n-k}\) times for every \(k \in \{1, 2, \dots, n\}\).-
Base case (\(n = 1\)): disk \(1\) is moved once \(= 2^{1-1}\).
-
Inductive step (\(n \ge 2\)): disk \(n\) is moved only by the call itself, once \(= 2^{n-n}\). A disk \(k \le n-1\) is moved only inside the two recursive calls with \(n-1\) disks, and by the inductive hypothesis it is moved \(2^{(n-1)-k}\) times in each of them: in total \(2 \cdot 2^{n-1-k} = 2^{n-k}\) times.
Summing over all the disks (geometric sum),
\[ \sum_{k=1}^{n} 2^{n-k} = 2^{n-1} + 2^{n-2} + \dots + 2 + 1 = 2^n - 1 = M(n). \]For \(n=4\): \(8 + 4 + 2 + 1 = 15\).
-
-
Every call executes exactly one move (the base case moves disk \(1\), the other calls move disk \(n\)), hence the number of calls is equal to the number of moves: \(15\) calls. The tree of recursive calls is a complete binary tree with \(4\) levels (\(1 + 2 + 4 + 8 = 15\) nodes), so the depth of the recursion is \(4\).
-
\(M(64) = 2^{64} - 1 = 18\,446\,744\,073\,709\,551\,615\) moves, i.e., the same number of seconds. One year has about \(365.25 \cdot 24 \cdot 3600 = 31\,557\,600\) seconds, so the time is
\[ \frac{2^{64}-1}{31\,557\,600} \approx 5.85 \cdot 10^{11} \text{ years}, \]about \(585\) billion years (more than \(40\) times the age of the universe). Since the number of moves of
Hanoiis optimal, no algorithm can do better.
Exercise 14.6: Recursion (E6)
Consider the following two recursive algorithms: FastPower computes \(x^n\) by squaring, ToBinary prints the binary representation of a natural number \(n\).
Algorithm
Questions
-
Show the sequence of calls of
FastPower\((x, 25)\) and count the number of multiplications. Compare with the naive algorithm, which executes \(n\) multiplications. -
Compute the number of multiplications and the number of steps \(S(n) = 4\lfloor \log_2 n \rfloor + 6\) of
FastPowerfor \(n = 31\), \(n = 32\) and \(n = 100\). -
A student replaces the line \(y :=\)
FastPower\((x, \lfloor n/2 \rfloor)\) and the following lines by\[ \textbf{return } \texttt{FastPower}(x, \lfloor n/2 \rfloor) \cdot \texttt{FastPower}(x, \lfloor n/2 \rfloor) \quad (\text{times } x \text{ if } n \text{ is odd}). \]Write the recurrence of the number of calls \(C(n)\) of this version, compute \(C(13)\) and \(C(16)\), and show that \(C(n) = \Theta(n)\).
-
Show the execution of
ToBinary\((37)\): calls, printed digits, and number of calls. Why are the digits printed in the correct order, while the algorithmChangeOfBase(chapter «Numerical algorithms») produces them from the least significant one? -
Prove by strong induction that
ToBinary\((n)\) prints the binary representation of \(n\), and that it executes \(\lfloor \log_2 n \rfloor + 1\) calls for \(n \ge 1\).
Solution
-
The exponents of the calls are \(25, 12, 6, 3, 1, 0\). Going back from the base case:
call \(y\) returns multiplications FastPower\((x, 0)\)– \(1\) (base case) \(0\) FastPower\((x, 1)\)\(1\) \(x \cdot 1 \cdot 1 = x\) (odd) \(2\) FastPower\((x, 3)\)\(x\) \(x \cdot x \cdot x = x^3\) (odd) \(2\) FastPower\((x, 6)\)\(x^3\) \(x^3 \cdot x^3 = x^6\) (even) \(1\) FastPower\((x, 12)\)\(x^6\) \(x^6 \cdot x^6 = x^{12}\) (even) \(1\) FastPower\((x, 25)\)\(x^{12}\) \(x \cdot x^{12} \cdot x^{12} = x^{25}\) (odd) \(2\) In total \(2 + 2 + 1 + 1 + 2 = 8\) multiplications, against \(25\) for the naive algorithm. Note that \(25 = (11001)_2\): \(5\) binary digits, three of which equal to \(1\), and \(5 + 3 = 8\).
-
The number of multiplications is the number of binary digits of \(n\) plus the number of digits equal to \(1\) (each call with \(n \ge 1\) executes \(1\) multiplication if \(n\) is even, \(2\) if \(n\) is odd, and the parity of the successive exponents gives the binary digits of \(n\)):
\(n\) binary multiplications \(S(n) = 4\lfloor \log_2 n \rfloor + 6\) naive \(31\) \(11111\) \(5 + 5 = 10\) \(4 \cdot 4 + 6 = 22\) \(31\) \(32\) \(100000\) \(6 + 1 = 7\) \(4 \cdot 5 + 6 = 26\) \(32\) \(100\) \(1100100\) \(7 + 3 = 10\) \(4 \cdot 6 + 6 = 30\) \(100\) Note that \(x^{32}\) requires fewer multiplications than \(x^{31}\), although \(32 > 31\).
-
Each call with \(n \ge 1\) now makes two recursive calls on \(\lfloor n/2 \rfloor\):
\[ C(n) = 2C(\lfloor n/2 \rfloor) + 1 \quad (n \ge 1), \qquad C(0) = 1. \]Hence \(C(1) = 3\), \(C(3) = 7\), \(C(6) = 15\), \(C(13) = 31\), and \(C(2) = 7\), \(C(4) = 15\), \(C(8) = 31\), \(C(16) = 63\).
The tree of recursive calls is now a complete binary tree. Its levels correspond to the exponents \(n, \lfloor n/2 \rfloor, \dots, 1, 0\), i.e., there are \(L = \lfloor \log_2 n \rfloor + 2\) levels, with \(1, 2, 4, \dots, 2^{L-1}\) nodes. Therefore
\[ C(n) = 1 + 2 + \dots + 2^{L-1} = 2^{L} - 1 = 4 \cdot 2^{\lfloor \log_2 n \rfloor} - 1. \](Check: \(C(13) = 4 \cdot 8 - 1 = 31\), \(C(16) = 4 \cdot 16 - 1 = 63\).) Since \(n/2 < 2^{\lfloor \log_2 n \rfloor} \le n\), we get \(2n - 1 < C(n) \le 4n - 1\), i.e., \(C(n) = \Theta(n)\): the version with two identical recursive calls is no better than the naive algorithm. Computing the value once and storing it in \(y\) is essential.
-
Execution of
ToBinary\((37)\):Calls:
ToBinary\((37) \to\)ToBinary\((18) \to\)ToBinary\((9) \to\)
ToBinary\((4) \to\)ToBinary\((2) \to\)ToBinary\((1)\)Prints (in the order in which the calls end):
call action printed so far ToBinary\((1)\)base case, prints \(1\) \(1\) ToBinary\((2)\)prints \(2 \bmod 2 = 0\) \(10\) ToBinary\((4)\)prints \(4 \bmod 2 = 0\) \(100\) ToBinary\((9)\)prints \(9 \bmod 2 = 1\) \(1001\) ToBinary\((18)\)prints \(18 \bmod 2 = 0\) \(10010\) ToBinary\((37)\)prints \(37 \bmod 2 = 1\) \(100101\) Hence \(37 = (100101)_2\) (indeed \(32 + 4 + 1 = 37\)), with \(6\) calls.
The remainders \(n \bmod 2\) are computed in the same order as in
ChangeOfBase(repeated division: \(1, 0, 1, 0, 0, 1\), from the least significant digit). However, each call prints its remainder after its recursive call has returned, i.e., after all the more significant digits have been printed: the call stack reverses the order of the digits, and no array is needed. -
Correctness, by strong induction on \(n\).
-
Base cases (\(n \in \{0,1\}\)): the binary representation of \(n\) is the single digit \(n\), which is printed.
-
Inductive step (\(n \ge 2\)): we have \(n = 2\lfloor n/2 \rfloor + (n \bmod 2)\) with \(1 \le \lfloor n/2 \rfloor < n\). If \(\lfloor n/2 \rfloor = (d_{k-1} \dots d_0)_2\), then
\[ n = 2\sum_{i=0}^{k-1} d_i 2^i + (n \bmod 2) = (d_{k-1} \dots d_0 \; (n \bmod 2))_2, \]i.e., the binary representation of \(n\) is that of \(\lfloor n/2 \rfloor\) followed by the digit \(n \bmod 2\). By the inductive hypothesis the recursive call prints \(d_{k-1} \dots d_0\), and then the algorithm prints \(n \bmod 2\).
Number of calls. Let \(C(n)\) be the number of calls for \(n \ge 1\): \(C(1) = 1\) and \(C(n) = C(\lfloor n/2 \rfloor) + 1\) for \(n \ge 2\). We prove \(C(n) = \lfloor \log_2 n \rfloor + 1\) by strong induction: \(C(1) = 1 = \lfloor \log_2 1 \rfloor + 1\); for \(n \ge 2\), using \(\lfloor \log_2 \lfloor n/2 \rfloor \rfloor = \lfloor \log_2 n \rfloor - 1\),
\[ C(n) = \big(\lfloor \log_2 n \rfloor - 1 + 1\big) + 1 = \lfloor \log_2 n \rfloor + 1. \]This is the number of binary digits of \(n\) (one digit printed per call). For \(n = 37\): \(\lfloor \log_2 37 \rfloor + 1 = 5 + 1 = 6\).
-