Start revision for Chapter 10

This commit is contained in:
Antti H S Laaksonen 2017-05-22 23:32:52 +03:00
parent 758ed890ce
commit 04f3c313cc
1 changed files with 126 additions and 60 deletions

View File

@ -330,7 +330,7 @@ difference & $a \setminus b$ & $a$ \& (\textasciitilde$b$) \\
For example, the following code first constructs For example, the following code first constructs
the sets $x=\{1,3,4,8\}$ and $y=\{3,6,8,9\}$, the sets $x=\{1,3,4,8\}$ and $y=\{3,6,8,9\}$,
and then calculates the set $z = x \cup y = \{1,3,4,6,8,9\}$: and then constructs the set $z = x \cup y = \{1,3,4,6,8,9\}$:
\begin{lstlisting} \begin{lstlisting}
int x = (1<<1)+(1<<3)+(1<<4)+(1<<8); int x = (1<<1)+(1<<3)+(1<<4)+(1<<8);
@ -367,6 +367,76 @@ do {
} while (b=(b-x)&x); } while (b=(b-x)&x);
\end{lstlisting} \end{lstlisting}
\section{Bit optimizations}
It is often possible to optimize algorithms
using bit operations.
Such optimizations do not change the
time complexity of the algorithm,
but they may have a large impact
on the actual running time of the code.
In this section we discuss examples
of such situations.
\subsubsection{Hamming distances}
\index{Hamming distance}
The \key{Hamming distance} between two bit strings
of equal length is
the number of positions where the strings differ.
For example, the Hamming distance between
01101 and 11001 is 2.
Consider the following problem: We are given
a list of $n$ bit strings, each of length $k$,
and our task is to calculate the minimum Hamming distance
between two strings in the list.
For example, the minimum distance for the list
$[00111,01101,11101]$ is 2.
A straightforward way to solve the problem is
to go through all pairs of string and calculate
their Hamming distances.
Such an algorithm works in $O(n^2 k)$ time.
The following function can be used to calculate
the Hamming distance between two strings:
\begin{lstlisting}
int distance(string a, string b) {
int d = 0;
for (int i = 0; i < k; i++) {
if (a[i] != b[i]) d++;
}
return d;
}
\end{lstlisting}
However, if $k$ is small, we can optimize the code
by storing the bit strings as integers and
calculating the Hamming distances using bit operations.
In particular, if $k \le 32$, we can just store
the strings as \texttt{int} values and use the
following function to calculate distances:
\begin{lstlisting}
int distance(int a, int b) {
return __builtin_popcount(a^b);
}
\end{lstlisting}
In the above function, the xor operation constructs
a bit string that has one bits in positions
where $a$ and $b$ differ.
Then, the number of bits is calculated using
the \texttt{\_\_builtin\_popcount} function.
To compare the implementations, we generated
a list of 10000 random bit strings of length 30.
Using the first approach, the search took
13.5 seconds, and after the bit optimization,
it took only 0.5 seconds.
Thus, the bit optimized code was almost
30 times faster than the original code.
\subsubsection{}
\section{Dynamic programming} \section{Dynamic programming}
\subsubsection{From permutations to subsets} \subsubsection{From permutations to subsets}
@ -379,7 +449,7 @@ contains a subset of a set and possibly
some additional information\footnote{This technique was introduced in 1962 some additional information\footnote{This technique was introduced in 1962
by M. Held and R. M. Karp \cite{hel62}.}. by M. Held and R. M. Karp \cite{hel62}.}.
The benefit in this is that The benefit of this is that
$n!$, the number of permutations of an $n$ element set, $n!$, the number of permutations of an $n$ element set,
is much larger than $2^n$, the number of subsets is much larger than $2^n$, the number of subsets
of the same set. of the same set.
@ -388,68 +458,64 @@ $n! \approx 2.4 \cdot 10^{18}$ and $2^n \approx 10^6$.
Hence, for certain values of $n$, Hence, for certain values of $n$,
we can efficiently go through subsets but not through permutations. we can efficiently go through subsets but not through permutations.
As an example, consider the problem of As an example, consider the following problem:
calculating the number of There is an elevator with maximum weight $x$,
permutations of a set $\{0,1,\ldots,n-1\}$, and $n$ people with known weights
where the difference between any two consecutive who want to get from the ground floor
elements is larger than one. to the top floor.
For example, when $n=4$, there are two such permutations: What is the minimum number of rides needed
$(1,3,0,2)$ and $(2,0,3,1)$. if the people enter the elevator in an optimal order?
Let $f(x,k)$ denote the number of valid permutations For example, suppose that $x=10$, $n=5$
of a subset $x$ where the last element is $k$ and and the weights are as follows:
the difference between any two consecutive \begin{center}
elements is larger than one. \begin{tabular}{ll}
For example, $f(\{0,1,3\},1)=1$, person & weight \\
because there is a permutation $(0,3,1)$, \hline
and $f(\{0,1,3\},3)=0$, because 0 and 1 $A$ & 2 \\
cannot be next to each other. $B$ & 3 \\
$C$ & 3 \\
$D$ & 5 \\
$E$ & 6 \\
\end{tabular}
\end{center}
In this case, the minimum number of rides is 2.
One optimal order is $\{A,C,D,B,E\}$,
which partitions the people into two rides:
first $\{A,C,D\}$ (total weight 10),
and then $\{B,E\}$ (total weight 9).
Using $f$, the answer to the problem equals The problem can be easily solved in $O(n! n)$ time
\[ \sum_{i=0}^{n-1} f(\{0,1,\ldots,n-1\},i), \] by testing all possible permutations of $n$ people.
because the permutation has to contain all However, we can use dynamic programming to get
elements $\{0,1,\ldots,n-1\}$ and the last a more efficient $O(2^n n)$ time algorithm.
element can be any element. The idea is to calculate for each subset of people
two values: the minimum number of rides needed and
the minimum weight of people who ride in the last group.
The dynamic programming values can be stored as follows: Let $\texttt{rides}(X)$ denote the minimum number
\begin{lstlisting} of rides and $\texttt{weight}(X)$ denote the minimum
int d[1<<n][n]; weight of the last group, where $X$ is a subset
\end{lstlisting} of people. For example,
\[ \texttt{rides}(\{B,D,E\})=2 \hspace{10px} \textrm{and}
\hspace{10px} \texttt{weight}(\{B,D,E\})=5,\]
because the optimal rides are $\{B,E\}$ and $\{D\}$,
and the second ride has weight 5.
Of course, our final goal is to calculate the value
of $\texttt{rides}(\{A,B,C,D,E\})$ that is the solution
to the problem.
First, $f(\{k\},k)=1$ for all values of $k$: It turns out that we can calculate the values
\begin{lstlisting} of the functions recursively and then apply
for (int i = 0; i < n; i++) d[1<<i][i] = 1; dynamic programming.
\end{lstlisting} The idea is to go through all people
that belong to $X$ and optimally
Then, the other values can be calculated choose the last person who enters the elevator.
as follows: For example, if $X=\{B,D,E\}$,
\begin{lstlisting} one of $B$, $D$ and $E$ is the last person
for (int b = 0; b < (1<<n); b++) { who enters the elevator.
for (int i = 0; i < n; i++) { Each such choice yields a subproblem
for (int j = 0; j < n; j++) { for a smaller subset of people.
if (abs(i-j) > 1 && (b&(1<<i)) && (b&(1<<j))) {
d[b][i] += d[b^(1<<i)][j];
}
}
}
}
\end{lstlisting}
In the above code,
the variable $b$ goes through all subsets and each
permutation is of the form $(\ldots,j,i)$,
where the difference between $i$ and $j$ is
larger than one and $i$ and $j$ belong to $b$.
Finally, the number of solutions can be
calculated as follows:
\begin{lstlisting}
int s = 0;
for (int i = 0; i < n; i++) {
s += d[(1<<n)-1][i];
}
\end{lstlisting}
\subsubsection{Counting subsets} \subsubsection{Counting subsets}