diff --git a/chapter01.tex b/chapter01.tex index 42527b5..015aa88 100644 --- a/chapter01.tex +++ b/chapter01.tex @@ -778,7 +778,7 @@ n! & = & n \cdot (n-1)! \\ \index{Fibonacci number} -The \key{Fibonacci numbers} arise in many situations. +The \key{Fibonacci numbers}\footnote{Fibonacci (c. 1175--1250) was an Italian mathematician.} arise in many situations. They can be defined recursively as follows: \[ \begin{array}{lcl} @@ -790,7 +790,8 @@ f(n) & = & f(n-1)+f(n-2) \\ The first Fibonacci numbers are \[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, \ldots\] There is also a closed-form formula -for calculating Fibonacci numbers: +for calculating Fibonacci numbers\footnote{This formula is sometimes called +\index{Binet's formula} \key{Binet's formula}.}: \[f(n)=\frac{(1 + \sqrt{5})^n - (1-\sqrt{5})^n}{2^n \sqrt{5}}.\] \subsubsection{Logarithms} diff --git a/chapter02.tex b/chapter02.tex index f8ababa..7b84efa 100644 --- a/chapter02.tex +++ b/chapter02.tex @@ -281,7 +281,11 @@ Still, there are many important problems for which no polynomial algorithm is known, i.e., nobody knows how to solve them efficiently. \key{NP-hard} problems are an important set -of problems for which no polynomial algorithm is known \cite{gar79}. +of problems for which no polynomial algorithm +is known\footnote{A classic book on this topic is +M. R. Garey's and D. S. Johnson's +\emph{Computers and Intractability: A Guide to the Theory +of NP-Completeness} \cite{gar79}.}. \section{Estimating efficiency} @@ -352,7 +356,7 @@ time and even in $O(n)$ time. Given an array of $n$ integers $x_1,x_2,\ldots,x_n$, our task is to find the -\key{maximum subarray sum}\footnote{Bentley's +\key{maximum subarray sum}\footnote{J. Bentley's book \emph{Programming Pearls} \cite{ben86} made this problem popular.}, i.e., the largest possible sum of numbers in a contiguous region in the array. @@ -470,7 +474,9 @@ After this change, the time complexity is $O(n^2)$. \subsubsection{Algorithm 3} Surprisingly, it is possible to solve the problem -in $O(n)$ time, which means that we can remove +in $O(n)$ time\footnote{In \cite{ben86}, this linear algorithm +is attributed to J. B. Kadene, and the algorithm is sometimes +called \index{Kadene's algorithm} \key{Kadene's algorithm}.}, which means that we can remove one more loop. The idea is to calculate for each array position the maximum sum of a subarray that ends at that position. diff --git a/chapter03.tex b/chapter03.tex index 7d834ae..21349d4 100644 --- a/chapter03.tex +++ b/chapter03.tex @@ -326,7 +326,8 @@ of the algorithm is at least $O(n^2)$. It is possible to sort an array efficiently in $O(n \log n)$ time using algorithms that are not limited to swapping consecutive elements. -One such algorithm is \key{mergesort} +One such algorithm is \key{mergesort}\footnote{According to \cite{knu98}, +mergesort was invented by J. von Neumann in 1945.} that is based on recursion. Mergesort sorts a subarray \texttt{t}$[a,b]$ as follows: diff --git a/chapter04.tex b/chapter04.tex index bf576c0..b0edcd9 100644 --- a/chapter04.tex +++ b/chapter04.tex @@ -196,7 +196,7 @@ for (auto x : s) { } \end{lstlisting} -An important property of sets +An important property of sets is that all the elements are \emph{distinct}. Thus, the function \texttt{count} always returns either 0 (the element is not in the set) @@ -723,7 +723,7 @@ $5 \cdot 10^6$ & $10{,}0$ s & $2{,}3$ s & $0{,}9$ s \\ \end{tabular} \end{center} -Algorithm 1 and 2 are equal except that +Algorithms 1 and 2 are equal except that they use different set structures. In this problem, this choice has an important effect on the running time, because algorithm 2 diff --git a/chapter05.tex b/chapter05.tex index fce1d21..56e946e 100644 --- a/chapter05.tex +++ b/chapter05.tex @@ -436,18 +436,18 @@ the $4 \times 4$ board are numbered as follows: \end{tikzpicture} \end{center} +Let $q(n)$ denote the number of ways +to place $n$ queens to te $n \times n$ chessboard. The above backtracking -algorithm tells us that -there are 92 ways to place 8 -queens to the $8 \times 8$ chessboard. +algorithm tells us that $q(n)=92$. When $n$ increases, the search quickly becomes slow, because the number of the solutions increases exponentially. -For example, calculating the ways to -place 16 queens to the $16 \times 16$ -chessboard already takes about a minute -on a modern computer -(there are 14772512 solutions). +For example, calculating $q(16)=14772512$ +using the above algorithm already takes about a minute +on a modern computer\footnote{There is no known way to efficiently +calculate larger values of $q(n)$. The current record is +$q(27)=234907967154122528$, calculated in 2016 \cite{q27}.}. \section{Pruning the search} @@ -716,7 +716,8 @@ check if the sum of any of the subsets is $x$. The running time of such a solution is $O(2^n)$, because there are $2^n$ subsets. However, using the meet in the middle technique, -we can achieve a more efficient $O(2^{n/2})$ time solution. +we can achieve a more efficient $O(2^{n/2})$ time solution\footnote{This +technique was introduced in 1974 by E. Horowitz and S. Sahni \cite{hor74}.}. Note that $O(2^n)$ and $O(2^{n/2})$ are different complexities because $2^{n/2}$ equals $\sqrt{2^n}$. diff --git a/chapter06.tex b/chapter06.tex index e4cb004..489ab4e 100644 --- a/chapter06.tex +++ b/chapter06.tex @@ -530,7 +530,9 @@ the string \texttt{AB} or the string \texttt{C}. \subsubsection{Huffman coding} -\key{Huffman coding} \cite{huf52} is a greedy algorithm +\key{Huffman coding}\footnote{D. A. Huffman discovered this method +when solving a university course assignment +and published the algorithm in 1952 \cite{huf52}.} is a greedy algorithm that constructs an optimal code for compressing a given string. The algorithm builds a binary tree @@ -671,114 +673,4 @@ character & codeword \\ \texttt{C} & 10 \\ \texttt{D} & 111 \\ \end{tabular} -\end{center} - -% \subsubsection{Miksi algoritmi toimii?} -% -% Huffmanin koodaus on ahne algoritmi, koska se -% yhdistää aina kaksi solmua, joiden painot ovat -% pienimmät. -% Miksi on varmaa, että tämä menetelmä tuottaa -% aina optimaalisen koodin? -% -% Merkitään $c(x)$ merkin $x$ esiintymiskertojen -% määrää merkkijonossa sekä $s(x)$ -% merkkiä $x$ vastaavan koodisanan pituutta. -% Näitä merkintöjä käyttäen merkkijonon -% bittiesityksen pituus on -% \[\sum_x c(x) \cdot s(x),\] -% missä summa käy läpi kaikki merkkijonon merkit. -% Esimerkiksi äskeisessä esimerkissä -% bittiesityksen pituus on -% \[5 \cdot 1 + 1 \cdot 3 + 2 \cdot 2 + 1 \cdot 3 = 15.\] -% Hyödyllinen havainto on, että $s(x)$ on yhtä suuri kuin -% merkkiä $x$ vastaavan solmun \emph{syvyys} puussa -% eli matka puun huipulta solmuun. -% -% Perustellaan ensin, miksi optimaalista koodia vastaa -% aina binääripuu, jossa jokaisesta solmusta lähtee -% alaspäin joko kaksi haaraa tai ei yhtään haaraa. -% Tehdään vastaoletus, että jostain solmusta lähtisi -% alaspäin vain yksi haara. -% Esimerkiksi seuraavassa puussa tällainen tilanne on solmussa $a$: -% \begin{center} -% \begin{tikzpicture}[scale=0.9] -% \node[draw, circle, minimum size=20pt] (3) at (3,1) {\phantom{$a$}}; -% \node[draw, circle, minimum size=20pt] (2) at (4,0) {$b$}; -% \node[draw, circle, minimum size=20pt] (5) at (5,1) {$a$}; -% \node[draw, circle, minimum size=20pt] (6) at (4,2) {\phantom{$a$}}; -% -% \path[draw,thick,-] (2) -- (5); -% \path[draw,thick,-] (3) -- (6); -% \path[draw,thick,-] (5) -- (6); -% \end{tikzpicture} -% \end{center} -% Tällainen solmu $a$ on kuitenkin aina turha, koska se -% tuo vain yhden bitin lisää polkuihin, jotka kulkevat -% solmun kautta, eikä sen avulla voi erottaa kahta -% koodisanaa toisistaan. Niinpä kyseisen solmun voi poistaa -% puusta, minkä seurauksena syntyy parempi koodi, -% eli optimaalista koodia vastaavassa puussa ei voi olla -% solmua, josta lähtee vain yksi haara. -% -% Perustellaan sitten, miksi on joka vaiheessa optimaalista -% yhdistää kaksi solmua, joiden painot ovat pienimmät. -% Tehdään vastaoletus, että solmun $a$ paino on pienin, -% mutta sitä ei saisi yhdistää aluksi toiseen solmuun, -% vaan sen sijasta tulisi yhdistää solmu $b$ -% ja jokin toinen solmu: -% \begin{center} -% \begin{tikzpicture}[scale=0.9] -% \node[draw, circle, minimum size=20pt] (1) at (0,0) {\phantom{$a$}}; -% \node[draw, circle, minimum size=20pt] (2) at (-2,-1) {\phantom{$a$}}; -% \node[draw, circle, minimum size=20pt] (3) at (2,-1) {$a$}; -% \node[draw, circle, minimum size=20pt] (4) at (-3,-2) {\phantom{$a$}}; -% \node[draw, circle, minimum size=20pt] (5) at (-1,-2) {\phantom{$a$}}; -% \node[draw, circle, minimum size=20pt] (8) at (-2,-3) {$b$}; -% \node[draw, circle, minimum size=20pt] (9) at (0,-3) {\phantom{$a$}}; -% -% \path[draw,thick,-] (1) -- (2); -% \path[draw,thick,-] (1) -- (3); -% \path[draw,thick,-] (2) -- (4); -% \path[draw,thick,-] (2) -- (5); -% \path[draw,thick,-] (5) -- (8); -% \path[draw,thick,-] (5) -- (9); -% \end{tikzpicture} -% \end{center} -% Solmuille $a$ ja $b$ pätee -% $c(a) \le c(b)$ ja $s(a) \le s(b)$. -% Solmut aiheuttavat bittiesityksen pituuteen lisäyksen -% \[c(a) \cdot s(a) + c(b) \cdot s(b).\] -% Tarkastellaan sitten toista tilannetta, -% joka on muuten samanlainen kuin ennen, -% mutta solmut $a$ ja $b$ on vaihdettu keskenään: -% \begin{center} -% \begin{tikzpicture}[scale=0.9] -% \node[draw, circle, minimum size=20pt] (1) at (0,0) {\phantom{$a$}}; -% \node[draw, circle, minimum size=20pt] (2) at (-2,-1) {\phantom{$a$}}; -% \node[draw, circle, minimum size=20pt] (3) at (2,-1) {$b$}; -% \node[draw, circle, minimum size=20pt] (4) at (-3,-2) {\phantom{$a$}}; -% \node[draw, circle, minimum size=20pt] (5) at (-1,-2) {\phantom{$a$}}; -% \node[draw, circle, minimum size=20pt] (8) at (-2,-3) {$a$}; -% \node[draw, circle, minimum size=20pt] (9) at (0,-3) {\phantom{$a$}}; -% -% \path[draw,thick,-] (1) -- (2); -% \path[draw,thick,-] (1) -- (3); -% \path[draw,thick,-] (2) -- (4); -% \path[draw,thick,-] (2) -- (5); -% \path[draw,thick,-] (5) -- (8); -% \path[draw,thick,-] (5) -- (9); -% \end{tikzpicture} -% \end{center} -% Osoittautuu, että tätä puuta vastaava koodi on -% \emph{yhtä hyvä tai parempi} kuin alkuperäinen koodi, joten vastaoletus -% on väärin ja Huffmanin koodaus -% toimiikin oikein, jos se yhdistää aluksi solmun $a$ -% jonkin solmun kanssa. -% Tämän perustelee seuraava epäyhtälöketju: -% \[\begin{array}{rcl} -% c(b) & \ge & c(a) \\ -% c(b)\cdot(s(b)-s(a)) & \ge & c(a)\cdot (s(b)-s(a)) \\ -% c(b)\cdot s(b)-c(b)\cdot s(a) & \ge & c(a)\cdot s(b)-c(a)\cdot s(a) \\ -% c(a)\cdot s(a)+c(b)\cdot s(b) & \ge & c(a)\cdot s(b)+c(b)\cdot s(a) \\ -% \end{array}\] \ No newline at end of file +\end{center} \ No newline at end of file diff --git a/chapter07.tex b/chapter07.tex index 13de7b0..f8edc7c 100644 --- a/chapter07.tex +++ b/chapter07.tex @@ -708,7 +708,8 @@ depends on the values of the objects. \index{edit distance} \index{Levenshtein distance} -The \key{edit distance} or \key{Levenshtein distance} +The \key{edit distance} or \key{Levenshtein distance}\footnote{The distance +is named after V. I. Levenshtein who discussed it in connection with binary codes \cite{lev66}.} is the minimum number of editing operations needed to transform a string into another string. diff --git a/chapter09.tex b/chapter09.tex index 572e6aa..4a2fbf8 100644 --- a/chapter09.tex +++ b/chapter09.tex @@ -440,7 +440,8 @@ we can conclude that $\textrm{rmq}(2,7)=1$. \index{binary indexed tree} \index{Fenwick tree} -A \key{binary indexed tree} or \key{Fenwick tree} \cite{fen94} +A \key{binary indexed tree} or \key{Fenwick tree}\footnote{The +binary indexed tree structure was presented by P. M. Fenwick in 1994 \cite{fen94}.} can be seen as a dynamic version of a prefix sum array. This data structure supports two $O(\log n)$ time operations: calculating the sum of elements in a range @@ -738,7 +739,9 @@ takes $O(1)$ time using bit operations. \index{segment tree} -A \key{segment tree} is a data structure +A \key{segment tree}\footnote{The origin of this structure is unknown. +The bottom-up-implementation in this chapter corresponds to +the implementation in \cite{sta06}.} is a data structure that supports two operations: processing a range query and modifying an element in the array. diff --git a/list.tex b/list.tex index 601b445..70242eb 100644 --- a/list.tex +++ b/list.tex @@ -107,7 +107,13 @@ Computer Science and Computational Biology}, Cambridge University Press, 1997. +\bibitem{hor74} + E. Horowitz and S. Sahni. + Computing partitions with applications to the knapsack problem. + \emph{Journal of the ACM}, 21(2):277--292, 1974. + \bibitem{huf52} + D. A. Huffman. A method for the construction of minimum-redundancy codes. \emph{Proceedings of the IRE}, 40(9):1098--1101, 1952. @@ -125,11 +131,20 @@ The statistics of dimers on a lattice: I. The number of dimer arrangements on a quadratic lattice. \emph{Physica}, 27(12):1209--1225, 1961. +\bibitem{knu98} + D. E. Knuth. + \emph{The Art of Computer Programming. Volume 3: Sorting and Searching}, Addison–Wesley, 1998 (2nd edition). + \bibitem{kru56} J. B. Kruskal. On the shortest spanning subtree of a graph and the traveling salesman problem. \emph{Proceedings of the American Mathematical Society}, 7(1):48--50, 1956. +\bibitem{lev66} + V. I. Levenshtein. + Binary codes capable of correcting deletions, insertions, and reversals. + \emph{Soviet physics doklady}, 10(8):707--710, 1966. + \bibitem{mai84} M. G. Main and R. J. Lorentz. An $O(n \log n)$ algorithm for finding all repetitions in a string. @@ -145,11 +160,20 @@ Shortest connection networks and some generalizations. \emph{Bell System Technical Journal}, 36(6):1389--1401, 1957. +\bibitem{q27} + 27-Queens Puzzle: Massively Parallel Enumeration and Solution Counting. + \url{https://github.com/preusser/q27} + \bibitem{sha81} M. Sharir. A strong-connectivity algorithm and its applications in data flow analysis. \emph{Computers \& Mathematics with Applications}, 7(1):67--72, 1981. +\bibitem{sta06} + P. Stańczyk. + \emph{Algorytmika praktyczna w konkursach Informatycznych}, + MSc thesis, University of Warsaw, 2006. + \bibitem{str69} V. Strassen. Gaussian elimination is not optimal.