Revisio almost complete
This commit is contained in:
parent
0b998fc638
commit
945e52e1f7
173
chapter10.tex
173
chapter10.tex
|
@ -646,9 +646,8 @@ to change an iteration over permutations into
|
||||||
an iteration over subsets\footnote{This technique was introduced in 1962
|
an iteration over subsets\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 of this is that
|
The benefit of this is that
|
||||||
$n!$, the number of permutations of an $n$ element set,
|
$n!$, the number of permutations,
|
||||||
is much larger than $2^n$, the number of subsets
|
is much larger than $2^n$, the number of subsets.
|
||||||
of the same set.
|
|
||||||
For example, if $n=20$, then
|
For example, if $n=20$, then
|
||||||
$n! \approx 2.4 \cdot 10^{18}$ and $2^n \approx 10^6$.
|
$n! \approx 2.4 \cdot 10^{18}$ and $2^n \approx 10^6$.
|
||||||
Thus, for certain values of $n$,
|
Thus, for certain values of $n$,
|
||||||
|
@ -689,16 +688,20 @@ The idea is to calculate for each subset of people
|
||||||
two values: the minimum number of rides needed and
|
two values: the minimum number of rides needed and
|
||||||
the minimum weight of people who ride in the last group.
|
the minimum weight of people who ride in the last group.
|
||||||
|
|
||||||
Let $\texttt{rides}(S)$ and $\texttt{weight}(S)$ denote
|
Let $\texttt{weight}[p]$ denote the weight of
|
||||||
the minimum number of rides and the minimum
|
person $p$ where $0 \le p < n$.
|
||||||
weight of the last group, where $S$ is a subset
|
Then we define two functions:
|
||||||
of people. For example,
|
$\texttt{rides}(S)$ is the minimum number of
|
||||||
|
rides for a subset $S$,
|
||||||
|
and $\texttt{last}(S)$ is the minimum weight
|
||||||
|
of the last ride.
|
||||||
|
For example, in the above scenario
|
||||||
\[ \texttt{rides}(\{B,D,E\})=2 \hspace{10px} \textrm{and}
|
\[ \texttt{rides}(\{B,D,E\})=2 \hspace{10px} \textrm{and}
|
||||||
\hspace{10px} \texttt{weight}(\{B,D,E\})=5,\]
|
\hspace{10px} \texttt{last}(\{B,D,E\})=5,\]
|
||||||
because the optimal rides are $\{B,E\}$ and $\{D\}$,
|
because the optimal rides are $\{B,E\}$ and $\{D\}$,
|
||||||
and the second ride has weight 5.
|
and the second ride has weight 5.
|
||||||
Of course, our final goal is to calculate the value
|
Of course, our final goal is to calculate the value
|
||||||
of $\texttt{rides}(X)$ where $X$ is a set that
|
of $\texttt{rides}(P)$ where $P$ is a set that
|
||||||
contains all the people.
|
contains all the people.
|
||||||
|
|
||||||
We can calculate the values
|
We can calculate the values
|
||||||
|
@ -706,50 +709,74 @@ of the functions recursively and then apply
|
||||||
dynamic programming.
|
dynamic programming.
|
||||||
The idea is to go through all people
|
The idea is to go through all people
|
||||||
who belong to $S$ and optimally
|
who belong to $S$ and optimally
|
||||||
choose the last person who enters the elevator.
|
choose the last person $p$ who enters the elevator.
|
||||||
For example, if $S=\{B,D,E\}$,
|
|
||||||
one of $B$, $D$ and $E$ is the last person
|
|
||||||
who enters the elevator.
|
|
||||||
Each such choice yields a subproblem
|
Each such choice yields a subproblem
|
||||||
for a smaller subset of people.
|
for a smaller subset of people.
|
||||||
|
If $\texttt{last}(S \setminus p)+\texttt{weight}[p] \le x$,
|
||||||
|
we can add $p$ to the last ride.
|
||||||
|
Otherwise, we have to reserve a new ride
|
||||||
|
that initially only contains $p$.
|
||||||
|
|
||||||
Note that we can use a loop like
|
A convenient way to implement a dynamic programming
|
||||||
|
solution is to declare an array
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
for (int b = 0; b < (1<<n); b++) {
|
pair<int,int> best[1<<N];
|
||||||
// process subset b
|
\end{lstlisting}
|
||||||
|
that contains values of \texttt{rides} and \texttt{weight}
|
||||||
|
as pairs. For an empty group, no rides are needed:
|
||||||
|
\begin{lstlisting}
|
||||||
|
best[0] = {0,0};
|
||||||
|
\end{lstlisting}
|
||||||
|
Then, we can fill the array as follows:
|
||||||
|
|
||||||
|
\begin{lstlisting}
|
||||||
|
for (int s = 1; s < (1<<n); s++) {
|
||||||
|
best[s] = {n,0};
|
||||||
|
for (int p = 0; p < n; p++) {
|
||||||
|
if (s&(1<<p)) {
|
||||||
|
auto option = best[s^(1<<p)];
|
||||||
|
if (option.second+weight[p] <= x) {
|
||||||
|
option.second += weight[p];
|
||||||
|
} else {
|
||||||
|
option.first++;
|
||||||
|
option.second = weight[p];
|
||||||
|
}
|
||||||
|
best[s] = min(best[s], option);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
to go through the subsets,
|
Note that in the above loop, for any two subsets $S_1$ and $S_2$
|
||||||
because if $S_1$ and $S_2$ are two subsets
|
such that $S_1 \subset S_2$, we process $S_1$ before $S_2$.
|
||||||
and $S_1 \subset S_2$,
|
Thus, the dynamic programming values are calculated in the
|
||||||
then $S_1$ comes before $S_2$ in the above order.
|
correct order.
|
||||||
|
|
||||||
\subsubsection{Counting subsets}
|
\subsubsection{Counting subsets}
|
||||||
|
|
||||||
Our last problem in this chapter is as follows:
|
Our last problem in this chapter is as follows:
|
||||||
Let $X=\{0 \ldots n-1\}$, and each subset $S \subset X$,
|
Let $X=\{0 \ldots n-1\}$, and each subset $S \subset X$,
|
||||||
is assigned an integer $\texttt{value}(S)$.
|
is assigned an integer $\texttt{value}[S]$.
|
||||||
Our task is to calculate for each $S$
|
Our task is to calculate for each $S$
|
||||||
\[\texttt{sum}(S) = \sum_{A \subset S} \texttt{value}(A),\]
|
\[\texttt{sum}(S) = \sum_{A \subset S} \texttt{value}[A],\]
|
||||||
i.e., the sum of values of subsets of $S$.
|
i.e., the sum of values of subsets of $S$.
|
||||||
|
|
||||||
For example, suppose that $n=3$ and the values are as follows:
|
For example, suppose that $n=3$ and the values are as follows:
|
||||||
\begin{multicols}{2}
|
\begin{multicols}{2}
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item $\texttt{value}(\emptyset) = 3$
|
\item $\texttt{value}[\emptyset] = 3$
|
||||||
\item $\texttt{value}(\{0\}) = 1$
|
\item $\texttt{value}[\{0\}] = 1$
|
||||||
\item $\texttt{value}(\{1\}) = 4$
|
\item $\texttt{value}[\{1\}] = 4$
|
||||||
\item $\texttt{value}(\{0,1\}) = 5$
|
\item $\texttt{value}[\{0,1\}] = 5$
|
||||||
\item $\texttt{value}(\{2\}) = 5$
|
\item $\texttt{value}[\{2\}] = 5$
|
||||||
\item $\texttt{value}(\{0,2\}) = 1$
|
\item $\texttt{value}[\{0,2\}] = 1$
|
||||||
\item $\texttt{value}(\{1,2\}) = 3$
|
\item $\texttt{value}[\{1,2\}] = 3$
|
||||||
\item $\texttt{value}(\{0,1,2\}) = 3$
|
\item $\texttt{value}[\{0,1,2\}] = 3$
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
\end{multicols}
|
\end{multicols}
|
||||||
In this case, for example,
|
In this case, for example,
|
||||||
\begin{equation*}
|
\begin{equation*}
|
||||||
\begin{split}
|
\begin{split}
|
||||||
\texttt{sum}(\{0,2\}) &= \texttt{value}(\emptyset)+\texttt{value}(\{0\})+\texttt{value}(\{2\})+\texttt{value}(\{0,2\}) \\
|
\texttt{sum}(\{0,2\}) &= \texttt{value}[\emptyset]+\texttt{value}[\{0\}]+\texttt{value}[\{2\}]+\texttt{value}[\{0,2\}] \\
|
||||||
&= 3 + 1 + 5 + 1 = 10.
|
&= 3 + 1 + 5 + 1 = 10.
|
||||||
\end{split}
|
\end{split}
|
||||||
\end{equation*}
|
\end{equation*}
|
||||||
|
@ -759,59 +786,55 @@ one possible solution is to go through all
|
||||||
pairs of subsets in $O(2^{2n})$ time.
|
pairs of subsets in $O(2^{2n})$ time.
|
||||||
However, using dynamic programming, we
|
However, using dynamic programming, we
|
||||||
can solve the problem in $O(2^n n)$ time.
|
can solve the problem in $O(2^n n)$ time.
|
||||||
|
The idea is to focus on sums where the
|
||||||
|
elements that may removed from $S$ are restricted.
|
||||||
|
|
||||||
Let $\texttt{sum}(S,k)$ denote the sum of
|
Let $\texttt{partial}(S,k)$ denote the sum of
|
||||||
values of subsets of $S$
|
values of subsets of $S$ with the restriction
|
||||||
|
that only elements $0 \ldots k$
|
||||||
|
may be removed from $S$.
|
||||||
Let $c(x,k)$ denote the number of sets in
|
For example,
|
||||||
$C$ that equal a set $x$
|
\[\texttt{partial}(\{0,2\},1)=\texttt{value}[\{2\}]+\texttt{value}[\{0,2\}],\]
|
||||||
if we are allowed to remove any subset of
|
because we only may remove elements $0 \ldots 1$.
|
||||||
$\{0,1,\ldots,k\}$ from $x$.
|
We can calculate values of \texttt{sum} using
|
||||||
For example, in the above collection,
|
values of \texttt{partial}, because
|
||||||
$c(\{0,1,4\},1)=2$,
|
\[\texttt{sum}(S) = \texttt{partial}(S,n-1).\]
|
||||||
where the corresponding sets are
|
The base cases for the function are
|
||||||
$\{1,4\}$ and $\{0,1,4\}$.
|
\[\texttt{partial}(S,-1)=\texttt{value}[S],\]
|
||||||
|
because in this case no elements can be removed from $S$.
|
||||||
It turns out that we can calculate all
|
Then, in the general case we can use the following recurrence:
|
||||||
values of $c(x,k)$ in $O(2^n n)$ time.
|
|
||||||
This solves our problem, because
|
|
||||||
\[f(x)=c(x,n-1).\]
|
|
||||||
|
|
||||||
The base cases for the function are:
|
|
||||||
\begin{equation*}
|
\begin{equation*}
|
||||||
c(x,-1) = \begin{cases}
|
\texttt{partial}(S,k) = \begin{cases}
|
||||||
0 & \textrm{if $x \notin C$}\\
|
\texttt{partial}(S,k-1) & k \notin S \\
|
||||||
1 & \textrm{if $x \in C$}\\
|
\texttt{partial}(S,k-1) + \texttt{partial}(S \setminus \{k\},k-1) & k \in S
|
||||||
\end{cases}
|
|
||||||
\end{equation*}
|
|
||||||
For larger values of $k$, the following recursion holds:
|
|
||||||
\begin{equation*}
|
|
||||||
c(x,k) = \begin{cases}
|
|
||||||
c(x,k-1) & \textrm{if $k \notin x$}\\
|
|
||||||
c(x,k-1)+c(x \setminus \{k\},k-1) & \textrm{if $k \in x$}\\
|
|
||||||
\end{cases}
|
\end{cases}
|
||||||
\end{equation*}
|
\end{equation*}
|
||||||
|
Here we focus on the element $k$.
|
||||||
|
If $k \in S$, we have two options: we may either keep $k$ in $S$
|
||||||
|
or remove it from $S$.
|
||||||
|
|
||||||
We can conveniently implement the algorithm by representing
|
There is a particularly clever way to implement the
|
||||||
the sets using bits.
|
calculation of sums. We can declare an array
|
||||||
Assume that there is an array
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
int d[1<<n];
|
int sum[1<<N];
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
that is initialized so that $d[x]=1$ if $x$ belongs to $C$
|
that will contain the sum of each subset.
|
||||||
and otherwise $d[x]=0$.
|
The array is initialized as follows:
|
||||||
We can now implement the algorithm as follows:
|
\begin{lstlisting}
|
||||||
|
for (int s = 0; s < (1<<n); s++) {
|
||||||
|
sum[s] = value[s];
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
Then, we can fill the array as follows:
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
for (int k = 0; k < n; k++) {
|
for (int k = 0; k < n; k++) {
|
||||||
for (int b = 0; b < (1<<n); b++) {
|
for (int s = 0; s < (1<<n); s++) {
|
||||||
if (b&(1<<k)) d[b] += d[b^(1<<k)];
|
if (s&(1<<k)) sum[s] += sum[s^(1<<k)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
The above code is based on the recursive definition
|
This code calculates the values of $\texttt{partial}(S,k)$
|
||||||
of $c$. As a special trick, the code only uses
|
for $k=0 \ldots n-1$ to the array \texttt{sum}.
|
||||||
the array $d$ to calculate all values of the function.
|
Since $\texttt{partial}(S,k)$ is always based on
|
||||||
Finally, for each set $x$ in $C$, $f(x)=d[x]$.
|
$\texttt{partial}(S,k-1)$, we can reuse the array
|
||||||
|
\texttt{sum}, which yields a very efficient implementation.
|
Loading…
Reference in New Issue