Skip to content

Commit

Permalink
[list] uniformed the symboal in pow calculation
Browse files Browse the repository at this point in the history
  • Loading branch information
liuxinyu95 committed Mar 1, 2016
1 parent 3b93de3 commit a9a699b
Showing 1 changed file with 25 additions and 29 deletions.
54 changes: 25 additions & 29 deletions others/appendix/list/list-zh-cn.tex
Original file line number Diff line number Diff line change
Expand Up @@ -1190,8 +1190,7 @@ \subsubsection{尾递归}

作为本节的结尾,我们考虑一个有趣的题目,如何设计一个算法来高效地计算$b^n$?(参考\cite{SICP}中的1.16节。)

A naive brute-force solution is to repeatedly multiply $b$ for $n$ times from 1, which leads to a
linear $O(n)$ algorithm.
最直接的方法是从1开始重复乘以$b$$n$次,这是一个线性时间$O(n)$的算法。

\begin{algorithmic}[1]
\Function{Pow}{$b, n$}
Expand All @@ -1203,21 +1202,18 @@ \subsubsection{尾递归}
\EndFunction
\end{algorithmic}

Actually, the solution can be greatly improved. Consider we are trying to calculate $b^8$.
By the first 2 iterations in above naive algorithm, we got $x = b^2$. At this stage, we
needn't multiply $x$ with $b$ to get $b^3$, we can directly calculate $x^2$, which leads
to $b^4$. And if we do this again, we get $(b^4)^2 = b^8$. Thus we only need looping 3 times
but not 8 times.
我们考虑如何改进它。考虑计算$b^8$的过程,上述算法经过前两次迭代,可以得到$x = b^2$的结果。此时,我们无需再次用$x$乘以$b$得到$b^3$,可以直接再次乘以$b^2$,从而得到$b^4$。然后再次乘方,就可以得到$(b^4) = b^8$。这样总共只要循环3次,而不是8次。

An algorithm based on this idea to compute $b^n$ if $N = 2^M$ for some non-negative integer $m$ can be shown in
the following equation.

\[
pow(b, n) = \left \{
\begin{array}
{r@{\quad:\quad}l}
b & N = 1 \\
pow(b, \frac{N}{2})^2 & otherwise
b & n = 1 \\
pow(b, \frac{n}{2})^2 & otherwise
\end{array}
\right.
\]
Expand All @@ -1226,8 +1222,8 @@ \subsubsection{尾递归}

\begin{itemize}
\item For the trivial case, that $n$ is zero, the result is 1;
\item If $n$ is even number, we can halve $n$, and compute $b^{\frac{N}{2}}$ first. Then calculate the square number of this result.
\item Otherwise, $n$ is odd. Since $N-1$ is even, we can first recursively compute $b^{N-1}$, the multiply $b$ one more time to this result.
\item If $n$ is even number, we can halve $n$, and compute $b^{\frac{n}{2}}$ first. Then calculate the square number of this result.
\item Otherwise, $n$ is odd. Since $n-1$ is even, we can first recursively compute $b^{n-1}$, the multiply $b$ one more time to this result.
\end{itemize}

Below equation formalizes this description.
Expand All @@ -1236,9 +1232,9 @@ \subsubsection{尾递归}
pow(b, n) = \left \{
\begin{array}
{r@{\quad:\quad}l}
1 & N = 0 \\
pow(b, \frac{N}{2})^2 & 2 | N \\
b \times pow(b, N-1) & otherwise
1 & n = 0 \\
pow(b, \frac{n}{2})^2 & 2 | n \\
b \times pow(b, n-1) & otherwise
\end{array}
\right.
\ee
Expand All @@ -1250,30 +1246,30 @@ \subsubsection{尾递归}
pow(b, n) = \left \{
\begin{array}
{r@{\quad:\quad}l}
1 & N = 0 \\
pow(b^2, \frac{N}{2}) & 2 | N \\
b \times pow(b, N-1) & otherwise
1 & n = 0 \\
pow(b^2, \frac{n}{2}) & 2 | n \\
b \times pow(b, n-1) & otherwise
\end{array}
\right.
\ee

With this change, it's easy to get a tail-recursive algorithm as the following, so that $b^N = pow'(b, N, 1)$.
With this change, it's easy to get a tail-recursive algorithm as the following, so that $b^n = pow'(b, n, 1)$.

\be
pow'(b, N, A) = \left \{
pow'(b, n, A) = \left \{
\begin{array}
{r@{\quad:\quad}l}
A & N = 0 \\
pow'(b^2, \frac{N}{2}, A) & 2 | N \\
pow'(b, N-1, A \times b) & otherwise
A & n = 0 \\
pow'(b^2, \frac{n}{2}, A) & 2 | n \\
pow'(b, n-1, A \times b) & otherwise
\end{array}
\right.
\ee

Compare to the naive brute-force algorithm, we improved the performance to $O(\lg n)$.
Actually, this algorithm can be improved even one more step.

Observe that if we represent $n$ in binary format $N = (a_ma_{m-1}...a_1a_0)_2$, we clear know
Observe that if we represent $n$ in binary format $n = (a_ma_{m-1}...a_1a_0)_2$, we clear know
that the computation for $b^{2^i}$ is necessary if $a_i = 1$. This is quite similar to the
idea of Binomial heap (reader can refer to the chapter of binomial heap in this book). Thus
we can calculate the final result by multiplying all of them for bits with value 1.
Expand All @@ -1293,12 +1289,12 @@ \subsubsection{尾递归}
Summarize this idea, we can improve the algorithm as below.

\be
pow'(b, N, A) = \left \{
pow'(b, n, A) = \left \{
\begin{array}
{r@{\quad:\quad}l}
A & N = 0 \\
pow'(b^2, \frac{N}{2}, A) & 2 | N \\
pow'(b^2, \lfloor \frac{N}{2} \rfloor, A \times b) & otherwise
A & n = 0 \\
pow'(b^2, \frac{n}{2}, A) & 2 | n \\
pow'(b^2, \lfloor \frac{n}{2} \rfloor, A \times b) & otherwise
\end{array}
\right.
\ee
Expand All @@ -1307,8 +1303,8 @@ \subsubsection{尾递归}
which is the lowest bit) is 0, it means $n$ is even. It goes on computing the square of the base, without accumulating the
final product (Just like the 3rd step in above example); If the LSB is 1, it means $n$ is odd. It squares the base and
accumulates it to the product $A$; The edge case is when $n$ is zero, which means we exhaust all the bits in $n$, thus
the final result is the accumulator $A$. At any time, the updated base number $b'$, the shifted exponent number $N'$,
and the accumulator $A$ satisfy the invariant that $b^N = b'^{N'}A$.
the final result is the accumulator $A$. At any time, the updated base number $b'$, the shifted exponent number $n'$,
and the accumulator $A$ satisfy the invariant that $b^n = b'^{n'}A$.

This algorithm can be implemented in Haskell like the following.

Expand Down

0 comments on commit a9a699b

Please sign in to comment.