cphb/chapter10.tex

546 lines
15 KiB
TeX
Raw Normal View History

2016-12-28 23:54:51 +01:00
\chapter{Bit manipulation}
2017-02-16 22:31:26 +01:00
All data in computer programs is internally stored as bits,
2017-02-04 21:10:45 +01:00
i.e., as numbers 0 and 1.
2017-01-07 12:34:28 +01:00
In this chapter, we will learn how integers
are represented as bits, and how bit operations
2017-02-04 21:10:45 +01:00
can be used to manipulate them.
2017-01-07 12:34:28 +01:00
It turns out that there are many uses for
2017-02-04 21:10:45 +01:00
bit operations in algorithm programming.
2017-01-07 12:34:28 +01:00
\section{Bit representation}
\index{bit representation}
2017-02-04 21:10:45 +01:00
Every nonnegative integer can be represented as a sum
\[c_k 2^k + \ldots + c_2 2^2 + c_1 2^1 + c_0 2^0,\]
2017-02-16 22:31:26 +01:00
where each coefficient $c_i$ is either 0 or 1.
The bit representation of such a number is
2017-02-04 21:10:45 +01:00
$c_k \cdots c_2 c_1 c_0$.
For example, the number 43 corresponds to the sum
\[1 \cdot 2^5 + 0 \cdot 2^4 + 1 \cdot 2^3 + 0 \cdot 2^2 + 1 \cdot 2^1 + 1 \cdot 2^0,\]
so the bit representation of the number is 101011.
In programming, the length of the bit representation
2017-02-16 22:31:26 +01:00
depends on the data type of the number.
2017-02-04 21:10:45 +01:00
For example, in C++ the type \texttt{int} is
usually a 32-bit type and an \texttt{int} number
2017-01-07 12:34:28 +01:00
consists of 32 bits.
2017-02-04 21:10:45 +01:00
Thus, the bit representation of 43
2017-01-07 12:34:28 +01:00
as an \texttt{int} number is as follows:
2016-12-28 23:54:51 +01:00
\[00000000000000000000000000101011\]
2017-01-07 12:34:28 +01:00
The bit representation of a number is either
\key{signed} or \key{unsigned}.
2017-02-04 21:10:45 +01:00
Usually a signed representation is used,
which means that both negative and positive
numbers can be represented.
A signed number of $n$ bits can contain any
integer between $2^{n-1}$ and $2^{n-1}-1$.
For example, the \texttt{int} type in C++ is
a signed type, and it can contain any
integer between $2^{31}$ and $2^{31}-1$.
The first bit in a signed representation
is the sign of the number (0 for nonnegative numbers
and 1 for negative numbers), and
2017-02-16 22:31:26 +01:00
the remaining $n-1$ bits contain the magnitude of the number.
2017-02-04 21:10:45 +01:00
\key{Two's complement} is used, which means that the
opposite number of a number is calculated by first
inverting all the bits in the number,
2017-01-07 12:34:28 +01:00
and then increasing the number by one.
2017-02-04 21:10:45 +01:00
For example, the bit representation of $-43$
2017-01-07 12:34:28 +01:00
as an \texttt{int} number is as follows:
2016-12-28 23:54:51 +01:00
\[11111111111111111111111111010101\]
2017-02-28 19:00:15 +01:00
In an unsigned representation, only nonnegative
2017-02-04 21:10:45 +01:00
numbers can be used, but the upper bound of the numbers is larger.
2017-02-28 19:00:15 +01:00
An unsigned number of $n$ bits can contain any
2017-02-04 21:10:45 +01:00
integer between $0$ and $2^n-1$.
For example, the \texttt{unsigned int} type in C++
can contain any integer between $0$ and $2^{32}-1$.
There is a connection between signed and unsigned
representations:
a number $-x$ in a signed representation
equals the number $2^n-x$ in an unsigned representation.
For example, the following code shows that
the signed number $x=-43$ equals the unsigned
number $y=2^{32}-43$:
2016-12-28 23:54:51 +01:00
\begin{lstlisting}
int x = -43;
unsigned int y = x;
cout << x << "\n"; // -43
cout << y << "\n"; // 4294967253
\end{lstlisting}
2017-01-07 12:34:28 +01:00
2017-02-04 21:10:45 +01:00
If a number is larger than the upper bound
of the bit representation, the number will overflow.
In a signed representation,
2017-01-07 12:34:28 +01:00
the next number after $2^{n-1}-1$ is $-2^{n-1}$,
and in an unsigned representation,
the next number after $2^{n-1}$ is $0$.
2017-02-16 22:31:26 +01:00
For example, consider the following code:
2016-12-28 23:54:51 +01:00
\begin{lstlisting}
int x = 2147483647
cout << x << "\n"; // 2147483647
x++;
cout << x << "\n"; // -2147483648
\end{lstlisting}
2017-02-16 22:31:26 +01:00
Initially, the value of $x$ is $2^{31}-1$.
This is the largest number that can be stored
in an \texttt{int} variable,
so the next number after $2^{31}-1$ is $-2^{31}$.
2017-01-07 12:34:28 +01:00
\section{Bit operations}
2016-12-28 23:54:51 +01:00
\newcommand\XOR{\mathbin{\char`\^}}
2017-01-07 12:34:28 +01:00
\subsubsection{And operation}
2016-12-28 23:54:51 +01:00
2017-01-07 12:34:28 +01:00
\index{and operation}
2016-12-28 23:54:51 +01:00
2017-01-07 12:34:28 +01:00
The \key{and} operation $x$ \& $y$ produces a number
2017-02-04 21:10:45 +01:00
that has one bits in positions where both
$x$ and $y$ have one bits.
For example, $22$ \& $26$ = 18, because
2016-12-28 23:54:51 +01:00
\begin{center}
\begin{tabular}{rrr}
& 10110 & (22)\\
\& & 11010 & (26) \\
\hline
= & 10010 & (18) \\
\end{tabular}
\end{center}
2017-01-07 12:34:28 +01:00
Using the and operation, we can check if a number
$x$ is even because
$x$ \& $1$ = 0 if $x$ is even, and
$x$ \& $1$ = 1 if $x$ is odd.
2017-02-04 21:10:45 +01:00
More generally, $x$ is divisible by $2^k$
exactly when $x$ \& $(2^k-1)$ = 0.
2016-12-28 23:54:51 +01:00
2017-01-07 12:34:28 +01:00
\subsubsection{Or operation}
2016-12-28 23:54:51 +01:00
2017-01-07 12:34:28 +01:00
\index{or operation}
2016-12-28 23:54:51 +01:00
2017-01-07 12:34:28 +01:00
The \key{or} operation $x$ | $y$ produces a number
2017-02-04 21:10:45 +01:00
that has one bits in positions where at least one
of $x$ and $y$ have one bits.
For example, $22$ | $26$ = 30, because
2016-12-28 23:54:51 +01:00
\begin{center}
\begin{tabular}{rrr}
& 10110 & (22)\\
| & 11010 & (26) \\
\hline
= & 11110 & (30) \\
\end{tabular}
\end{center}
2017-01-07 12:34:28 +01:00
\subsubsection{Xor operation}
2016-12-28 23:54:51 +01:00
2017-01-07 12:34:28 +01:00
\index{xor operation}
2016-12-28 23:54:51 +01:00
2017-01-07 12:34:28 +01:00
The \key{xor} operation $x$ $\XOR$ $y$ produces a number
2017-02-04 21:10:45 +01:00
that has one bits in positions where exactly one
of $x$ and $y$ have one bits.
For example, $22$ $\XOR$ $26$ = 12, because
2016-12-28 23:54:51 +01:00
\begin{center}
\begin{tabular}{rrr}
& 10110 & (22)\\
$\XOR$ & 11010 & (26) \\
\hline
= & 01100 & (12) \\
\end{tabular}
\end{center}
2017-01-07 12:34:28 +01:00
\subsubsection{Not operation}
2016-12-28 23:54:51 +01:00
2017-01-07 12:34:28 +01:00
\index{not operation}
2016-12-28 23:54:51 +01:00
2017-01-07 12:34:28 +01:00
The \key{not} operation \textasciitilde$x$
produces a number where all the bits of $x$
2017-02-04 21:10:45 +01:00
have been inverted.
2017-01-07 12:34:28 +01:00
The formula \textasciitilde$x = -x-1$ holds,
for example, \textasciitilde$29 = -30$.
2016-12-28 23:54:51 +01:00
2017-01-07 12:34:28 +01:00
The result of the not operation at the bit level
2017-02-04 21:10:45 +01:00
depends on the length of the bit representation,
2017-01-07 12:34:28 +01:00
because the operation changes all bits.
For example, if the numbers are 32-bit
\texttt{int} numbers, the result is as follows:
2016-12-28 23:54:51 +01:00
\begin{center}
\begin{tabular}{rrrr}
$x$ & = & 29 & 00000000000000000000000000011101 \\
\textasciitilde$x$ & = & $-30$ & 11111111111111111111111111100010 \\
\end{tabular}
\end{center}
2017-01-07 12:34:28 +01:00
\subsubsection{Bit shifts}
2016-12-28 23:54:51 +01:00
2017-01-07 12:34:28 +01:00
\index{bit shift}
2016-12-28 23:54:51 +01:00
2017-02-04 21:10:45 +01:00
The left bit shift $x < < k$ appends $k$
2017-02-16 22:31:26 +01:00
zero bits to the number,
2017-01-07 12:34:28 +01:00
and the right bit shift $x > > k$
2017-02-04 21:10:45 +01:00
removes the $k$ last bits from the number.
For example, $14 < < 2 = 56$,
because $14$ equals 1110
and $56$ equals 111000.
2017-02-11 20:39:03 +01:00
Similarly, $49 > > 3 = 6$,
2017-02-04 21:10:45 +01:00
because $49$ equals 110001
and $6$ equals 110.
Note that $x < < k$
corresponds to multiplying $x$ by $2^k$,
and $x > > k$
2017-01-07 12:34:28 +01:00
corresponds to dividing $x$ by $2^k$
2017-02-04 21:10:45 +01:00
rounded down to an integer.
2016-12-28 23:54:51 +01:00
2017-02-04 21:10:45 +01:00
\subsubsection{Applications}
2016-12-28 23:54:51 +01:00
2017-02-04 21:10:45 +01:00
A number of the form $1 < < k$ has a one bit
2017-02-16 22:31:26 +01:00
in position $k$ and all other bits are zero,
2017-02-04 21:10:45 +01:00
so we can use such numbers to access single bits of numbers.
For example, the $k$th bit of a number is one
exactly when $x$ \& $(1 < < k)$ is not zero.
The following code prints the bit representation
of an \texttt{int} number $x$:
2016-12-28 23:54:51 +01:00
2017-02-04 21:10:45 +01:00
\begin{lstlisting}
for (int i = 31; i >= 0; i--) {
if (x&(1<<i)) cout << "1";
else cout << "0";
}
\end{lstlisting}
It is also possible to modify single bits
2017-02-16 22:31:26 +01:00
of numbers using the above idea.
2017-02-04 21:10:45 +01:00
For example, the expression $x$ | $(1 < < k)$
2017-01-07 12:34:28 +01:00
sets the $k$th bit of $x$ to one,
2017-02-04 21:10:45 +01:00
the expression
2016-12-28 23:54:51 +01:00
$x$ \& \textasciitilde $(1 < < k)$
2017-01-07 12:34:28 +01:00
sets the $k$th bit of $x$ to zero,
2017-02-04 21:10:45 +01:00
and the expression
2017-01-07 12:34:28 +01:00
$x$ $\XOR$ $(1 < < k)$
2017-02-04 21:10:45 +01:00
inverts the $k$th bit of $x$.
2017-01-07 12:34:28 +01:00
The formula $x$ \& $(x-1)$ sets the last
one bit of $x$ to zero,
and the formula $x$ \& $-x$ sets all the
one bits to zero, except for the last one bit.
2017-02-04 21:10:45 +01:00
The formula $x$ | $(x-1)$
inverts all the bits after the last one bit.
2017-01-07 12:34:28 +01:00
Also note that a positive number $x$ is
of the form $2^k$ if $x$ \& $(x-1) = 0$.
\subsubsection*{Additional functions}
2017-02-04 21:10:45 +01:00
The g++ compiler provides the following
functions for counting bits:
2016-12-28 23:54:51 +01:00
\begin{itemize}
\item
$\texttt{\_\_builtin\_clz}(x)$:
2017-01-07 12:34:28 +01:00
the number of zeros at the beginning of the number
2016-12-28 23:54:51 +01:00
\item
$\texttt{\_\_builtin\_ctz}(x)$:
2017-01-07 12:34:28 +01:00
the number of zeros at the end of the number
2016-12-28 23:54:51 +01:00
\item
$\texttt{\_\_builtin\_popcount}(x)$:
2017-01-07 12:34:28 +01:00
the number of ones in the number
2016-12-28 23:54:51 +01:00
\item
$\texttt{\_\_builtin\_parity}(x)$:
2017-01-07 12:34:28 +01:00
the parity (even or odd) of the number of ones
2016-12-28 23:54:51 +01:00
\end{itemize}
\begin{samepage}
2017-02-04 21:10:45 +01:00
The functions can be used as follows:
2016-12-28 23:54:51 +01:00
\begin{lstlisting}
int x = 5328; // 00000000000000000001010011010000
cout << __builtin_clz(x) << "\n"; // 19
cout << __builtin_ctz(x) << "\n"; // 4
cout << __builtin_popcount(x) << "\n"; // 5
cout << __builtin_parity(x) << "\n"; // 1
\end{lstlisting}
\end{samepage}
2017-02-16 22:31:26 +01:00
The above functions support \texttt{int} numbers,
but there are also \texttt{long long} functions
available with the suffix \texttt{ll}.
2017-01-07 12:34:28 +01:00
2017-02-04 21:10:45 +01:00
\section{Representing sets}
2017-01-07 12:34:28 +01:00
2017-01-07 12:51:53 +01:00
Each subset of a set $\{0,1,2,\ldots,n-1\}$
2017-02-16 22:31:26 +01:00
corresponds to an $n$ bit number
2017-01-07 12:51:53 +01:00
where the one bits indicate which elements
are included in the subset.
2017-02-16 22:31:26 +01:00
For example, the set $\{1,3,4,8\}$
corresponds to the number $2^8+2^4+2^3+2^1=282$,
whose bit representation is 100011010.
2016-12-28 23:54:51 +01:00
2017-02-16 22:31:26 +01:00
The benefit in using the bit representation
is that the information whether an element belongs
2017-02-04 21:47:12 +01:00
to the set requires only one bit of memory.
2017-02-16 22:31:26 +01:00
In addition, set operations can be efficiently
implemented as bit operations.
2016-12-28 23:54:51 +01:00
2017-02-16 22:31:26 +01:00
\subsubsection{Set implementation}
2016-12-28 23:54:51 +01:00
2017-02-04 21:47:12 +01:00
In the following code, $x$
2017-01-07 12:51:53 +01:00
contains a subset of $\{0,1,2,\ldots,31\}$.
2017-02-04 21:47:12 +01:00
The code adds the elements 1, 3, 4 and 8
to the set and then prints the elements.
2016-12-28 23:54:51 +01:00
\begin{lstlisting}
2017-01-07 12:51:53 +01:00
// x is an empty set
2016-12-28 23:54:51 +01:00
int x = 0;
2017-02-04 21:47:12 +01:00
// add elements 1, 3, 4 and 8 to the set
2016-12-28 23:54:51 +01:00
x |= (1<<1);
x |= (1<<3);
x |= (1<<4);
x |= (1<<8);
2017-01-07 12:51:53 +01:00
// print the elements in the set
2016-12-28 23:54:51 +01:00
for (int i = 0; i < 32; i++) {
if (x&(1<<i)) cout << i << " ";
}
cout << "\n";
\end{lstlisting}
2017-02-16 22:31:26 +01:00
The output of the code is as follows:
\begin{lstlisting}
1 3 4 8
\end{lstlisting}
\subsubsection{Set operations}
2017-02-04 21:47:12 +01:00
Set operations can be implemented as follows:
2016-12-28 23:54:51 +01:00
\begin{itemize}
2017-01-07 12:51:53 +01:00
\item $a$ \& $b$ is the intersection $a \cap b$ of $a$ and $b$
\item $a$ | $b$ is the union $a \cup b$ of $a$ and $b$
2017-02-04 21:47:12 +01:00
\item \textasciitilde$a$ is the complement $\bar a$ of $a$
2017-01-07 12:51:53 +01:00
\item $a$ \& (\textasciitilde$b$) is the difference
$a \setminus b$ of $a$ and $b$
2016-12-28 23:54:51 +01:00
\end{itemize}
2017-02-16 22:31:26 +01:00
For example, the following code constructs the union
2017-01-07 12:51:53 +01:00
of $\{1,3,4,8\}$ and $\{3,6,8,9\}$:
2016-12-28 23:54:51 +01:00
\begin{lstlisting}
2017-01-07 12:51:53 +01:00
// set {1,3,4,8}
2016-12-28 23:54:51 +01:00
int x = (1<<1)+(1<<3)+(1<<4)+(1<<8);
2017-01-07 12:51:53 +01:00
// set {3,6,8,9}
2016-12-28 23:54:51 +01:00
int y = (1<<3)+(1<<6)+(1<<8)+(1<<9);
2017-01-07 12:51:53 +01:00
// union of the sets
2016-12-28 23:54:51 +01:00
int z = x|y;
2017-01-07 12:51:53 +01:00
// print the elements in the union
2016-12-28 23:54:51 +01:00
for (int i = 0; i < 32; i++) {
if (z&(1<<i)) cout << i << " ";
}
cout << "\n";
\end{lstlisting}
2017-02-16 22:31:26 +01:00
The output of the code is as follows:
\begin{lstlisting}
1 3 4 6 8 9
\end{lstlisting}
2017-01-07 12:51:53 +01:00
\subsubsection{Iterating through subsets}
2016-12-28 23:54:51 +01:00
2017-02-04 21:47:12 +01:00
The following code goes through
2017-01-07 12:51:53 +01:00
the subsets of $\{0,1,\ldots,n-1\}$:
2016-12-28 23:54:51 +01:00
\begin{lstlisting}
for (int b = 0; b < (1<<n); b++) {
2017-01-07 12:51:53 +01:00
// process subset b
2016-12-28 23:54:51 +01:00
}
\end{lstlisting}
2017-01-07 12:51:53 +01:00
The following code goes through
2017-02-04 21:47:12 +01:00
the subsets with exactly $k$ elements:
2016-12-28 23:54:51 +01:00
\begin{lstlisting}
for (int b = 0; b < (1<<n); b++) {
if (__builtin_popcount(b) == k) {
2017-01-07 12:51:53 +01:00
// process subset b
2016-12-28 23:54:51 +01:00
}
}
\end{lstlisting}
2017-01-07 12:51:53 +01:00
The following code goes through the subsets
of a set $x$:
2016-12-28 23:54:51 +01:00
\begin{lstlisting}
int b = 0;
do {
2017-01-07 12:51:53 +01:00
// process subset b
2016-12-28 23:54:51 +01:00
} while (b=(b-x)&x);
\end{lstlisting}
2017-01-07 14:35:53 +01:00
\section{Dynamic programming}
\subsubsection{From permutations to subsets}
Using dynamic programming, it is often possible
2017-02-16 22:31:26 +01:00
to change an iteration over permutations into
an iteration over subsets, so that
2017-02-04 21:47:12 +01:00
the dynamic programming state
2017-01-07 14:35:53 +01:00
contains a subset of a set and possibly
2017-02-25 16:57:10 +01:00
some additional information\footnote{This technique was introduced in 1962
by M. Held and R. M. Karp \cite{hel62}.}.
2017-01-07 14:35:53 +01:00
2017-02-04 21:47:12 +01:00
The benefit in this is that
2017-01-07 14:35:53 +01:00
$n!$, the number of permutations of an $n$ element set,
2017-02-16 22:31:26 +01:00
is much larger than $2^n$, the number of subsets
of the same set.
2017-01-07 14:35:53 +01:00
For example, if $n=20$, then
2017-02-16 22:31:26 +01:00
$n! \approx 2.4 \cdot 10^{18}$ and $2^n \approx 10^6$.
2017-02-04 21:47:12 +01:00
Hence, for certain values of $n$,
we can efficiently go through subsets but not through permutations.
2017-01-07 14:35:53 +01:00
2017-02-04 21:47:12 +01:00
As an example, consider the problem of
calculating the number of
permutations of a set $\{0,1,\ldots,n-1\}$,
where the difference between any two consecutive
2017-01-07 14:35:53 +01:00
elements is larger than one.
2017-02-04 21:47:12 +01:00
For example, when $n=4$, there are two such permutations:
$(1,3,0,2)$ and $(2,0,3,1)$.
2016-12-28 23:54:51 +01:00
2017-02-04 21:47:12 +01:00
Let $f(x,k)$ denote the number of valid permutations
2017-02-16 22:31:26 +01:00
of a subset $x$ where the last element is $k$ and
the difference between any two consecutive
2017-01-07 14:35:53 +01:00
elements is larger than one.
2017-02-04 21:47:12 +01:00
For example, $f(\{0,1,3\},1)=1$,
2017-01-07 14:35:53 +01:00
because there is a permutation $(0,3,1)$,
2017-02-04 21:47:12 +01:00
and $f(\{0,1,3\},3)=0$, because 0 and 1
cannot be next to each other.
2016-12-28 23:54:51 +01:00
2017-02-16 22:31:26 +01:00
Using $f$, the answer to the problem equals
\[ \sum_{i=0}^{n-1} f(\{0,1,\ldots,n-1\},i), \]
because the permutation has to contain all
elements $\{0,1,\ldots,n-1\}$ and the last
element can be any element.
2016-12-28 23:54:51 +01:00
2017-02-16 22:31:26 +01:00
The dynamic programming values can be stored as follows:
2016-12-28 23:54:51 +01:00
\begin{lstlisting}
2017-02-16 23:47:44 +01:00
int d[1<<n][n];
2016-12-28 23:54:51 +01:00
\end{lstlisting}
2017-01-07 14:35:53 +01:00
First, $f(\{k\},k)=1$ for all values of $k$:
2016-12-28 23:54:51 +01:00
\begin{lstlisting}
for (int i = 0; i < n; i++) d[1<<i][i] = 1;
\end{lstlisting}
2017-02-04 21:47:12 +01:00
Then, the other values can be calculated
2017-01-07 14:35:53 +01:00
as follows:
2016-12-28 23:54:51 +01:00
\begin{lstlisting}
for (int b = 0; b < (1<<n); b++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (abs(i-j) > 1 && (b&(1<<i)) && (b&(1<<j))) {
d[b][i] += d[b^(1<<i)][j];
}
}
}
}
\end{lstlisting}
2017-02-16 22:31:26 +01:00
In the above code,
the variable $b$ goes through all subsets and each
permutation is of the form $(\ldots,j,i)$,
2017-02-04 21:47:12 +01:00
where the difference between $i$ and $j$ is
larger than one and $i$ and $j$ belong to $b$.
2016-12-28 23:54:51 +01:00
2017-01-07 14:35:53 +01:00
Finally, the number of solutions can be
2017-02-04 21:47:12 +01:00
calculated as follows:
2016-12-28 23:54:51 +01:00
\begin{lstlisting}
2017-02-16 23:47:44 +01:00
int s = 0;
2016-12-28 23:54:51 +01:00
for (int i = 0; i < n; i++) {
s += d[(1<<n)-1][i];
}
\end{lstlisting}
2017-02-16 23:47:44 +01:00
\subsubsection{Counting subsets}
Our last problem in this chapter is as follows:
2017-02-16 23:50:04 +01:00
We are given a collection $C$ that consists of $m$ sets,
2017-02-16 23:47:44 +01:00
and our task is to determine for each set
2017-02-16 23:50:04 +01:00
the number of sets in $C$ that are its subsets.
2017-02-16 23:47:44 +01:00
For example, consider the following collection:
\[C = \{\{0\}, \{0,2\}, \{1,4\}, \{0,1,4\}, \{1,4,5\}\}\]
For any set $x$ in $C$,
let $f(x)$ denote the number of sets (including $x$) in $C$
that are subsets of $x$.
For example, $f(\{0,1,4\})=3$, because the
sets $\{0\}$, $\{1,4\}$ and $\{0,1,4\}$ are
subsets of $\{0,1,4\}$.
Using this notation, our task is to calculate the value of $f(x)$
for every set $x$ in the collection.
We will assume that each set is
a subset of $\{0,1,\ldots,n-1\}$.
Thus, the collection can contain at most
$2^n$ sets.
A straightforward way to solve the problem
is to go through all pairs of sets in the collection.
However, a more efficient solution is possible
using dynamic programming.
Let $c(x,k)$ denote the number of sets in
2017-02-16 23:50:04 +01:00
$C$ that equal a set $x$
2017-02-16 23:47:44 +01:00
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).\]
2016-12-28 23:54:51 +01:00
2017-01-07 14:35:53 +01:00
The base cases for the function are:
2016-12-28 23:54:51 +01:00
\begin{equation*}
2017-02-16 23:47:44 +01:00
c(x,-1) = \begin{cases}
2017-02-16 23:55:36 +01:00
0 & \textrm{if $x \notin C$}\\
1 & \textrm{if $x \in C$}\\
2016-12-28 23:54:51 +01:00
\end{cases}
\end{equation*}
2017-01-07 14:35:53 +01:00
For larger values of $k$, the following recursion holds:
2016-12-28 23:54:51 +01:00
\begin{equation*}
2017-02-16 23:47:44 +01:00
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$}\\
2016-12-28 23:54:51 +01:00
\end{cases}
\end{equation*}
2017-02-16 23:47:44 +01:00
We can conveniently implement the algorithm by representing
the sets using bits.
Assume that there is an array
2016-12-28 23:54:51 +01:00
\begin{lstlisting}
2017-02-16 23:47:44 +01:00
int d[1<<n];
2016-12-28 23:54:51 +01:00
\end{lstlisting}
2017-02-16 23:47:44 +01:00
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:
2016-12-28 23:54:51 +01:00
\begin{lstlisting}
for (int k = 0; k < n; k++) {
2017-02-16 23:47:44 +01:00
for (int b = 0; b < (1<<n); b++) {
if (b&(1<<k)) d[b] += d[b^(1<<k)];
2016-12-28 23:54:51 +01:00
}
}
\end{lstlisting}
2017-02-16 23:47:44 +01:00
The above code is based on the recursive definition
2017-02-16 23:53:57 +01:00
of $c$. As a special trick, the code only uses
2017-02-16 23:47:44 +01:00
the array $d$ to calculate all values of the function.
Finally, for each set $x$ in $C$, $f(x)=d[x]$.
2016-12-28 23:54:51 +01:00