Revisio almost complete

This commit is contained in:
Antti H S Laaksonen 2017-05-25 14:18:48 +03:00
parent 0b998fc638
commit 945e52e1f7
1 changed files with 98 additions and 75 deletions

View File

@ -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.