cphb/luku10.tex

555 lines
14 KiB
TeX
Raw Normal View History

2016-12-28 23:54:51 +01:00
\chapter{Bit manipulation}
2017-01-07 12:34:28 +01:00
A computer internally manipulates data
as bits, i.e., as numbers 0 and 1.
In this chapter, we will learn how integers
are represented as bits, and how bit operations
can be used for manipulating them.
It turns out that there are many uses for
bit operations in the implementation of algorithms.
\section{Bit representation}
\index{bit representation}
The \key{bit representation} of a number
indicates which powers of two form the number.
For example, the bit representation of the number 43
is 101011 because
$43 = 2^5 + 2^3 + 2^1 + 2^0$ where
bits 0, 1, 3 and 5 from the right are ones,
and all other bits are zeros.
The length of a bit representation of a number
in a computer is static, and depends on the
data type chosen.
For example, the \texttt{int} type in C++ is
usually a 32-bit type, and an \texttt{int} number
consists of 32 bits.
In this case, the bit representation of 43
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}.
The first bit of a signed number is the sign
($+$ or $-$), and we can represent numbers
$-2^{n-1} \ldots 2^{n-1}-1$ using $n$ bits.
In an unsigned number, in turn,
all bits belong to the number and we
can represent numbers $0 \ldots 2^n-1$ using $n$ bits.
In an signed bit representation,
the first bit of a nonnegative number is 0,
and the first bit of a negative number is 1.
\key{Two's complement} is used which means that
the opposite number of a number can be calculated
by first inversing all the bits in the number,
and then increasing the number by one.
For example, the representation of $-43$
as an \texttt{int} number is as follows:
2016-12-28 23:54:51 +01:00
\[11111111111111111111111111010101\]
2017-01-07 12:34:28 +01:00
The connection between signed and unsigned numbers
is that the representations of a signed
number $-x$ and an unsigned number $2^n-x$
are equal.
Thus, the above representation corresponds to
the unsigned number $2^{32}-43$.
In C++, the numbers are signed as default,
but we can create unsigned numbers by
using the keyword \texttt{unsigned}.
For example, in the code
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
the signed number
$x=-43$ becomes the unsigned number $y=2^{32}-43$.
If a number becomes too large or too small for the
bit representation chosen, it will overflow.
In practice, in a signed representation,
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$.
For example, in the 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-01-07 12:34:28 +01:00
we increase $2^{31}-1$ by one to get $-2^{31}$.
2016-12-28 23:54:51 +01:00
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
that has bit 1 in positions where both the numbers
$x$ and $y$ have bit 1.
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.
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
that has bit 1 in positions where at least one
of the numbers
$x$ and $y$ have bit 1.
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
that has bit 1 in positions where exactly one
of the numbers
$x$ and $y$ have bit 1.
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$
have been inversed.
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
depends on the length of the bit representation
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-01-07 12:34:28 +01:00
The left bit shift $x < < k$ produces a number
where the bits of $x$ have been moved $k$ steps to
the left by adding $k$ zero bits to the number.
The right bit shift $x > > k$ produces a number
where the bits of $x$ have been moved $k$ steps
to the right by removing $k$ last bits from the number.
2016-12-28 23:54:51 +01:00
2017-01-07 12:34:28 +01:00
For example, $14 < < 2 = 56$
because $14$ equals 1110,
and it becomes $56$ that equals 111000.
Correspondingly, $49 > > 3 = 6$
because $49$ equals 110001,
and it becomes $6$ that equals 110.
2016-12-28 23:54:51 +01:00
2017-01-07 12:34:28 +01:00
Note that the left bit shift $x < < k$
corresponds to multiplying $x$ by $2^k$,
and the right bit shift $x > > k$
corresponds to dividing $x$ by $2^k$
rounding downwards.
2016-12-28 23:54:51 +01:00
2017-01-07 12:34:28 +01:00
\subsubsection{Bit manipulation}
2016-12-28 23:54:51 +01:00
2017-01-07 12:34:28 +01:00
The bits in a number are indexed from the right
to the left beginning from zero.
A number of the form $1 < < k$ contains a one bit
in position $k$, and all other bits are zero,
so we can manipulate single bits of numbers
using these numbers.
2016-12-28 23:54:51 +01:00
2017-01-07 12:34:28 +01:00
The $k$th bit in $x$ is one if
2016-12-28 23:54:51 +01:00
$x$ \& $(1 < < k) = (1 < < k)$.
2017-01-07 12:34:28 +01:00
The formula $x$ | $(1 < < k)$
sets the $k$th bit of $x$ to one,
the formula
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,
and the formula
$x$ $\XOR$ $(1 < < k)$
inverses the $k$th bit of $x$.
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.
The formula $x$ | $(x-1)$, in turn,
inverses all the bits after the last one bit.
Also note that a positive number $x$ is
of the form $2^k$ if $x$ \& $(x-1) = 0$.
\subsubsection*{Additional functions}
The g++ compiler contains the following
functions for bit manipulation:
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-01-07 12:34:28 +01:00
The following code shows how to use the functions:
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-01-07 12:34:28 +01:00
The functions support \texttt{int} numbers,
but there are also \texttt{long long} versions
of the functions
available with the prefix \texttt{ll}.
2017-01-07 12:51:53 +01:00
\section{Bit representation of 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\}$
corresponds to a $n$ bit number
where the one bits indicate which elements
are included in the subset.
For example, the bit representation for $\{1,3,4,8\}$
is 100011010 that equals $2^8+2^4+2^3+2^1=282$.
2016-12-28 23:54:51 +01:00
2017-01-07 12:51:53 +01:00
The bit representation of a set uses little memory
because only one bit is needed for the information
whether an element belongs to the set.
In addition, we can efficiently manipulate sets
that are stored as bits.
2016-12-28 23:54:51 +01:00
2017-01-07 12:51:53 +01:00
\subsubsection{Set operations}
2016-12-28 23:54:51 +01:00
2017-01-07 12:51:53 +01:00
In the following code, the variable $x$
contains a subset of $\{0,1,2,\ldots,31\}$.
The code adds elements 1, 3, 4 and 8
to the set and then prints the elements in the set.
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-01-07 12:51:53 +01:00
// add numbers 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-01-07 12:51:53 +01:00
The output of the code is as follows:
2016-12-28 23:54:51 +01:00
\begin{lstlisting}
1 3 4 8
\end{lstlisting}
2017-01-07 12:51:53 +01:00
Using the bit representation of a set,
we can efficiently implement set operations
using bit operations:
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$
(this contains the elements that are in both the sets)
\item $a$ | $b$ is the union $a \cup b$ of $a$ and $b$
(this contains the elements that are at least
in one of the sets)
\item $a$ \& (\textasciitilde$b$) is the difference
$a \setminus b$ of $a$ and $b$
(this contains the elements that are in $a$
but not in $b$)
2016-12-28 23:54:51 +01:00
\end{itemize}
2017-01-07 12:51:53 +01:00
The following code constructs the union
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-01-07 12:51:53 +01:00
The output of the code is as follows:
2016-12-28 23:54:51 +01:00
\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-01-07 12:51:53 +01:00
The following code iterates through
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
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 12:51:53 +01:00
% Esimerkiksi jos $x$ esittää joukkoa $\{2,5,7\}$,
% niin koodi käy läpi osajoukot
% $\emptyset$, $\{2\}$, $\{5\}$, $\{7\}$,
% $\{2,5\}$, $\{2,7\}$, $\{5,7\}$ ja $\{2,5,7\}$.
2016-12-28 23:54:51 +01:00
2017-01-07 14:35:53 +01:00
\section{Dynamic programming}
\subsubsection{From permutations to subsets}
Using dynamic programming, it is often possible
to change iteration over permutations into
iteration over subsets.
In this case, the dynamic programming state
contains a subset of a set and possibly
some additional information.
The benefit in this technique is that
$n!$, the number of permutations of an $n$ element set,
is much larger than $2^n$, the number of subsets.
For example, if $n=20$, then
$n!=2432902008176640000$ and $2^n=1048576$.
Thus, for certain values of $n$,
we can go through subsets but not through permutations.
As an example, let's calculate the number of
permutations of set $\{0,1,\ldots,n-1\}$
where the difference between any two successive
elements is larger than one.
For example, there are two solutions for $n=4$:
2016-12-28 23:54:51 +01:00
\begin{itemize}
\item $(1,3,0,2)$
\item $(2,0,3,1)$
\end{itemize}
2017-01-07 14:35:53 +01:00
Let $f(x,k)$ denote the number of permutations
for a subset $x$
where the last number is $k$ and
the difference between any two successive
elements is larger than one.
For example, $f(\{0,1,3\},1)=1$
because there is a permutation $(0,3,1)$,
and $f(\{0,1,3\},3)=0$ because 0 and 1
can't be next to each other.
2016-12-28 23:54:51 +01:00
2017-01-07 14:35:53 +01:00
Using $f$, the solution for the problem is the sum
2016-12-28 23:54:51 +01:00
\[ \sum_{i=0}^{n-1} f(\{0,1,\ldots,n-1\},i). \]
\noindent
2017-01-07 14:35:53 +01:00
The dynamic programming states can be stored as follows:
2016-12-28 23:54:51 +01:00
\begin{lstlisting}
long long d[1<<n][n];
\end{lstlisting}
\noindent
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}
\noindent
2017-01-07 14:35:53 +01:00
After this, the other values can be calculated
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}
\noindent
2017-01-07 14:35:53 +01:00
The variable $b$ contains the bit representation
of the subset, and the corresponding
permutation is of the form $(\ldots,j,i)$.
It is required that the difference between
$i$ and $j$ is larger than 1, and the
numbers belong to subset $b$.
2016-12-28 23:54:51 +01:00
2017-01-07 14:35:53 +01:00
Finally, the number of solutions can be
calculated as follows to $s$:
2016-12-28 23:54:51 +01:00
\begin{lstlisting}
long long s = 0;
for (int i = 0; i < n; i++) {
s += d[(1<<n)-1][i];
}
\end{lstlisting}
2017-01-07 14:35:53 +01:00
\subsubsection{Sums of subsets}
Let's assume that every subset $x$
of $\{0,1,\ldots,n-1\}$
is assigned a value $c(x)$,
and our task is to calculate for
each subset $x$ the sum
\[s(x)=\sum_{y \subset x} c(y)\]
that corresponds to the sum
\[s(x)=\sum_{y \& x = y} c(y)\]
using bit operations.
The following table gives an example of
the values of the functions when $n=3$:
2016-12-28 23:54:51 +01:00
\begin{center}
\begin{tabular}{rrr}
$x$ & $c(x)$ & $s(x)$ \\
\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}
2017-01-07 14:35:53 +01:00
For example, $s(110)=c(000)+c(010)+c(100)+c(110)=5$.
2016-12-28 23:54:51 +01:00
2017-01-07 14:35:53 +01:00
The problem can be solved in $O(2^n n)$ time
by defining a function $f(x,k)$ that calculates
the sum of values $c(y)$ where $x$ can be
converted into $y$ by changing any one bits
in positions $0,1,\ldots,k$ to zero bits.
Using this function, the solution for the
problem is $s(x)=f(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*}
f(x,0) = \begin{cases}
2017-01-07 14:35:53 +01:00
c(x) & \textrm{if bit 0 in $x$ is 0}\\
c(x)+c(x \XOR 1) & \textrm{if bit 0 in $x$ is 1}\\
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*}
f(x,k) = \begin{cases}
2017-01-07 14:35:53 +01:00
f(x,k-1) & \textrm{if bit $k$ in $x$ is 0}\\
f(x,k-1)+f(x \XOR (1 < < k),k-1) & \textrm{if bit $k$ in $x$ is 1}\\
2016-12-28 23:54:51 +01:00
\end{cases}
\end{equation*}
2017-01-07 14:35:53 +01:00
Thus, we can calculate the values for the function
as follows using dynamic programming.
The code assumes that the array \texttt{c}
contains the values for $c$,
and it constructs an array \texttt{s}
that contains the values for $s$.
2016-12-28 23:54:51 +01:00
\begin{lstlisting}
for (int x = 0; x < (1<<n); x++) {
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}
2017-01-07 14:35:53 +01:00
Actually, a much shorter implementation is possible
because we can calculate the results directly
to array \texttt{s}:
2016-12-28 23:54:51 +01:00
\begin{lstlisting}
for (int x = 0; x < (1<<n); x++) s[x] = c[x];
for (int k = 0; k < n; k++) {
for (int x = 0; x < (1<<n); x++) {
if (x&(1<<k)) s[x] += s[x^(1<<k)];
}
}
\end{lstlisting}