From c215e415cdde4ff0678f076ae15bbebcf077e520 Mon Sep 17 00:00:00 2001 From: Antti H S Laaksonen Date: Thu, 16 Feb 2017 23:31:26 +0200 Subject: [PATCH] Corrections --- luku10.tex | 108 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 62 insertions(+), 46 deletions(-) diff --git a/luku10.tex b/luku10.tex index 35136bf..6993aa3 100644 --- a/luku10.tex +++ b/luku10.tex @@ -1,6 +1,6 @@ \chapter{Bit manipulation} -All data in a program is internally stored as bits, +All data in computer programs is internally stored 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 @@ -14,15 +14,15 @@ bit operations in algorithm programming. 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,\] -where each coefficient $c_i$ is either 0 or 1, -and the bit representation of such a number is +where each coefficient $c_i$ is either 0 or 1. +The bit representation of such a number is $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 -depends on the data type chosen. +depends on the data type of the number. For example, in C++ the type \texttt{int} is usually a 32-bit type and an \texttt{int} number consists of 32 bits. @@ -44,7 +44,7 @@ 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 -the remaining $n-1$ bits contain the value of the number. +the remaining $n-1$ bits contain the magnitude of the number. \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, @@ -81,8 +81,7 @@ 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 following code, -the next number after $2^{31}-1$ is $-2^{31}$: +For example, consider the following code: \begin{lstlisting} int x = 2147483647 cout << x << "\n"; // 2147483647 @@ -90,6 +89,12 @@ x++; cout << x << "\n"; // -2147483648 \end{lstlisting} +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}$. + + \section{Bit operations} \newcommand\XOR{\mathbin{\char`\^}} @@ -183,7 +188,7 @@ $x$ & = & 29 & 00000000000000000000000000011101 \\ \index{bit shift} The left bit shift $x < < k$ appends $k$ -zeros to the end of the number, +zero bits to the number, and the right bit shift $x > > k$ removes the $k$ last bits from the number. For example, $14 < < 2 = 56$, @@ -202,7 +207,7 @@ rounded down to an integer. \subsubsection{Applications} A number of the form $1 < < k$ has a one bit -in position $k$, and all other bits are zero, +in position $k$ and all other bits are zero, 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. @@ -217,7 +222,7 @@ for (int i = 31; i >= 0; i--) { \end{lstlisting} It is also possible to modify single bits -of numbers using a similar idea. +of numbers using the above idea. For example, the expression $x$ | $(1 < < k)$ sets the $k$th bit of $x$ to one, the expression @@ -267,27 +272,27 @@ cout << __builtin_parity(x) << "\n"; // 1 \end{lstlisting} \end{samepage} -The functions can be used with \texttt{int} numbers, -but there are also \texttt{long long} versions -of the functions -available with the prefix \texttt{ll}. +The above functions support \texttt{int} numbers, +but there are also \texttt{long long} functions +available with the suffix \texttt{ll}. \section{Representing sets} Each subset of a set $\{0,1,2,\ldots,n-1\}$ -corresponds to a $n$ bit number +corresponds to an $n$ bit number where the one bits indicate which elements are included in the subset. -For example, the bit representation of $\{1,3,4,8\}$ -is 100011010 that equals $2^8+2^4+2^3+2^1=282$. +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. -The benefit in using a bit representation is -that the information whether an element belongs +The benefit in using the bit representation +is that the information whether an element belongs to the set requires only one bit of memory. -In addition, we can implement set operations -efficiently as bit operations. +In addition, set operations can be efficiently +implemented as bit operations. -\subsubsection{Set operations} +\subsubsection{Set implementation} In the following code, $x$ contains a subset of $\{0,1,2,\ldots,31\}$. @@ -309,6 +314,14 @@ for (int i = 0; i < 32; i++) { cout << "\n"; \end{lstlisting} +The output of the code is as follows: + +\begin{lstlisting} +1 3 4 8 +\end{lstlisting} + +\subsubsection{Set operations} + Set operations can be implemented as follows: \begin{itemize} \item $a$ \& $b$ is the intersection $a \cap b$ of $a$ and $b$ @@ -318,7 +331,7 @@ Set operations can be implemented as follows: $a \setminus b$ of $a$ and $b$ \end{itemize} -The following code constructs the union +For example, the following code constructs the union of $\{1,3,4,8\}$ and $\{3,6,8,9\}$: \begin{lstlisting} @@ -335,6 +348,12 @@ for (int i = 0; i < 32; i++) { cout << "\n"; \end{lstlisting} +The output of the code is as follows: + +\begin{lstlisting} +1 3 4 6 8 9 +\end{lstlisting} + \subsubsection{Iterating through subsets} The following code goes through @@ -368,17 +387,18 @@ do { \subsubsection{From permutations to subsets} Using dynamic programming, it is often possible -to turn an iteration over permutations into -an iteration over subsets so that +to change an iteration over permutations into +an iteration over subsets, so that the dynamic programming state contains a subset of a set and possibly some additional information. The benefit in this is that $n!$, the number of permutations of an $n$ element set, -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 -$n!=2432902008176640000$ and $2^n=1048576$. +$n! \approx 2.4 \cdot 10^{18}$ and $2^n \approx 10^6$. Hence, for certain values of $n$, we can efficiently go through subsets but not through permutations. @@ -391,36 +411,32 @@ For example, when $n=4$, there are two such permutations: $(1,3,0,2)$ and $(2,0,3,1)$. Let $f(x,k)$ denote the number of valid permutations -of $x$ where the last element is $k$ and -the difference between any two successive +of a subset $x$ where the last element is $k$ and +the difference between any two consecutive 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 cannot be next to each other. -Using $f$, the solution to the problem equals - -\[ \sum_{i=0}^{n-1} f(\{0,1,\ldots,n-1\},i). \] - -\noindent -The dynamic programming states can be stored as follows: +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. +The dynamic programming values can be stored as follows: \begin{lstlisting} long long d[1<