Counting subsets

This commit is contained in:
Antti H S Laaksonen 2017-02-17 00:47:44 +02:00
parent c215e415cd
commit 21e36525e2
1 changed files with 58 additions and 65 deletions

View File

@ -427,7 +427,7 @@ element can be any element.
The dynamic programming values can be stored as follows: The dynamic programming values can be stored as follows:
\begin{lstlisting} \begin{lstlisting}
long long d[1<<n][n]; int d[1<<n][n];
\end{lstlisting} \end{lstlisting}
First, $f(\{k\},k)=1$ for all values of $k$: First, $f(\{k\},k)=1$ for all values of $k$:
@ -459,93 +459,86 @@ Finally, the number of solutions can be
calculated as follows: calculated as follows:
\begin{lstlisting} \begin{lstlisting}
long long s = 0; int s = 0;
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
s += d[(1<<n)-1][i]; s += d[(1<<n)-1][i];
} }
\end{lstlisting} \end{lstlisting}
\subsubsection{Sums of subsets} \subsubsection{Counting subsets}
As the last example, we consider the following problem: Our last problem in this chapter is as follows:
Each subset $x$ We are given a collection $C$ of $m$ sets,
of $\{0,1,\ldots,n-1\}$ and our task is to determine for each set
is assigned a value $c(x)$, the number of sets that are its subsets.
and our task is to calculate for For example, consider the following collection:
each subset $x$ the sum \[C = \{\{0\}, \{0,2\}, \{1,4\}, \{0,1,4\}, \{1,4,5\}\}\]
\[s(x)=\sum_{y \subset x} c(y).\] For any set $x$ in $C$,
Using bit operations, the corresponding sum is let $f(x)$ denote the number of sets (including $x$) in $C$
\[s(x)=\sum_{y \& x = y} c(y).\] that are subsets of $x$.
For example, the functions could be For example, $f(\{0,1,4\})=3$, because the
as follows when $n=3$: sets $\{0\}$, $\{1,4\}$ and $\{0,1,4\}$ are
\begin{center} subsets of $\{0,1,4\}$.
\begin{tabular}{rrr} Using this notation, our task is to calculate the value of $f(x)$
$x$ & $c(x)$ & $s(x)$ \\ for every set $x$ in the collection.
\hline
000 & 2 & 2 \\
001 & 0 & 2 \\
010 & 1 & 3 \\
011 & 3 & 6 \\
100 & 0 & 2 \\
101 & 4 & 6 \\
110 & 2 & 5 \\
111 & 0 & 12 \\
\end{tabular}
\end{center}
For example, $s(110)=c(000)+c(010)+c(100)+c(110)=5$.
The problem can be solved in $O(2^n n)$ time We will assume that each set is
by defining a function $f(x,k)$ that calculates a subset of $\{0,1,\ldots,n-1\}$.
the sum of $c(y)$ values such that $x$ can be Thus, the collection can contain at most
turned into $y$ by changing zero or more one bits $2^n$ sets.
at positions $0,1,\ldots,k$ to zero bits. A straightforward way to solve the problem
Using this function, the solution to the is to go through all pairs of sets in the collection.
problem is $s(x)=f(x,n-1)$. However, a more efficient solution is possible
using dynamic programming.
Let $c(x,k)$ denote the number of sets in
$C$ that equal to a set $x$
if we are allowed to remove any subset of
$\{0,1,\ldots,k\}$ from $x$.
For example, in the above collection,
$c(\{0,1,4\},1)=2$,
where the corresponding sets are
$\{1,4\}$ and $\{0,1,4\}$.
It turns out that we can calculate all
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: The base cases for the function are:
\begin{equation*} \begin{equation*}
f(x,0) = \begin{cases} c(x,-1) = \begin{cases}
c(x) & \textrm{if bit 0 of $x$ is 0}\\ 0 & \textrm{if $x$ does not appear in $C$}\\
c(x)+c(x \XOR 1) & \textrm{if bit 0 of $x$ is 1}\\ 1 & \textrm{if $x$ appears in $C$}\\
\end{cases} \end{cases}
\end{equation*} \end{equation*}
For larger values of $k$, the following recursion holds: For larger values of $k$, the following recursion holds:
\begin{equation*} \begin{equation*}
f(x,k) = \begin{cases} c(x,k) = \begin{cases}
f(x,k-1) & \textrm{if bit $k$ of $x$ is 0}\\ c(x,k-1) & \textrm{if $k \notin x$}\\
f(x,k-1)+f(x \XOR (1 < < k),k-1) & \textrm{if bit $k$ of $x$ is 1}\\ c(x,k-1)+c(x \setminus \{k\},k-1) & \textrm{if $k \in x$}\\
\end{cases} \end{cases}
\end{equation*} \end{equation*}
Thus, we can calculate the values of the function We can conveniently implement the algorithm by representing
as follows using dynamic programming. the sets using bits.
The code assumes that the array \texttt{c} Assume that there is an array
contains the values for $c$,
and it constructs an array \texttt{s}
that contains the values for $s$.
\begin{lstlisting} \begin{lstlisting}
for (int x = 0; x < (1<<n); x++) { int d[1<<n];
f[x][0] = c[x];
if (x&1) f[x][0] += c[x^1];
}
for (int k = 1; k < n; k++) {
for (int x = 0; x < (1<<n); x++) {
f[x][k] = f[x][k-1];
if (b&(1<<k)) f[x][k] += f[x^(1<<k)][k-1];
}
if (k == n-1) s[x] = f[x][k];
}
\end{lstlisting} \end{lstlisting}
that is initialized so that $d[x]=1$ if $x$ belongs to $C$
and otherwise $d[x]=0$.
We can now implement the algorithm as follows:
Actually, a much shorter implementation is possible,
because we can calculate the results directly
into the array \texttt{s}:
\begin{lstlisting} \begin{lstlisting}
for (int x = 0; x < (1<<n); x++) s[x] = c[x];
for (int k = 0; k < n; k++) { for (int k = 0; k < n; k++) {
for (int x = 0; x < (1<<n); x++) { for (int b = 0; b < (1<<n); b++) {
if (x&(1<<k)) s[x] += s[x^(1<<k)]; if (b&(1<<k)) d[b] += d[b^(1<<k)];
} }
} }
\end{lstlisting} \end{lstlisting}
The above code is based on the recursive definition
of $c$. As a special trick, the function only uses
the array $d$ to calculate all values of the function.
Finally, for each set $x$ in $C$, $f(x)=d[x]$.