]> mj.ucw.cz Git - saga.git/blob - ram.tex
f327f3838881141dc8b9db83f5a9670338aac49d
[saga.git] / ram.tex
1 \ifx\endpart\undefined
2 \input macros.tex
3 \fi
4
5 \chapter{Fine Details of Computation}
6 \id{ramchap}
7
8 \section{Models and machines}
9
10 Traditionally, computer scientists use a~variety of computational models
11 as a~formalism in which their algorithms are stated. If we were studying
12 NP-completeness, we could safely assume that all the models are equivalent,
13 possibly up to polynomial slowdown which is negligible. In our case, the
14 differences between good and not-so-good algorithms are on a~much smaller
15 scale. In this chapter, we will replace the usual ``yardsticks'' by a~micrometer,
16 state our computation models carefully and develop a repertoire of basic
17 data structures taking advantage of the fine details of the models.
18
19 We would like to keep the formalism close enough to the reality of the contemporary
20 computers. This rules out Turing machines and similar sequentially addressed
21 models, but even the remaining models are subtly different from each other. For example, some of them
22 allow indexing of arrays in constant time, while on the others,
23 arrays have to be emulated with pointer structures, requiring $\Omega(\log n)$
24 time to access a~single element of an~$n$-element array. It is hard to say which
25 way is superior --- while most ``real'' computers have instructions for constant-time
26 indexing, it seems to be physically impossible to fulfil this promise regardless of
27 the size of memory. Indeed, at the level of logical gates inside the computer,
28 the depth of the actual indexing circuits is logarithmic.
29
30 In recent decades, most researchers in the area of combinatorial algorithms
31 have been considering two computational models: the Random Access Machine and the Pointer
32 Machine. The former is closer to the programmer's view of a~real computer,
33 the latter is slightly more restricted and ``asymptotically safe.''
34 We will follow this practice and study our algorithms in both models.
35
36 \para
37 The \df{Random Access Machine (RAM)} is not a~single coherent model, but rather a~family
38 of closely related machines, sharing the following properties.
39 (See Cook and Reckhow \cite{cook:ram} for one of the usual formal definitions
40 and Hagerup \cite{hagerup:wordram} for a~thorough description of the differences
41 between the RAM variants.)
42
43 The \df{memory} of the machine is represented by an~array of \df{memory cells}
44 addressed by non-negative integers, each of them containing a~single non-negative integer.
45 The \df{program} is a~finite sequence of \df{instructions} of two basic kinds: calculation
46 instructions and control instructions.
47
48 \df{Calculation instructions} have two source arguments and one destination
49 argument, each \df{argument} being either an~immediate constant (not available
50 as destination), a~directly addressed memory cell (specified by its number)
51 or an~indirectly addressed memory cell (its address is stored in a~directly
52 addressed memory cell).
53
54 \df{Control instructions} include branches (to a~specific instruction in
55 the program), conditional branches (e.g., jump if two arguments specified as
56 in the calculation instructions are equal) and an~instruction to halt the program.
57
58 At the beginning of the computation, the memory contains the input data
59 in specified cells and arbitrary values in all other cells.
60 Then the program is executed one instruction at a~time. When it halts,
61 specified memory cells are interpreted as the program's output.
62
63 \para\id{wordsize}%
64 In the description of the RAM family, we have omitted several details
65 on~purpose, because different members of the family define them differently.
66 These are: the size of the available integers, the time complexity of a~single
67 instruction, the space complexity assigned to a~single memory cell and the set
68 of operations available in calculation instructions.
69
70 If we impose no limits on the magnitude of the numbers and we assume that
71 arithmetic and logical operations work on them in constant time, we get
72 a~very powerful parallel computer --- we can emulate an~exponential number
73 of parallel processors using arithmetics and suddenly almost everything can be
74 computed in constant time, modulo encoding and decoding of input and output.
75 Such models are unrealistic and there are two basic possibilities how to
76 avoid this behavior:
77
78 \numlist\ndotted
79 \:Keep unbounded numbers, but increase costs of instructions: each instruction
80   consumes time proportional to the number of bits of the numbers it processes,
81   including memory addresses. Similarly, space usage is measured in bits,
82   counting not only the values, but also the addresses of the respective memory
83   cells.
84 \:Place a~limit on the size of the numbers ---define the \df{word size~$W$,}
85   the number of bits available in the memory cells--- and keep the cost of
86   instructions and memory cells constant. The word size must not be constant,
87   since we can address only~$2^W$ cells of memory. If the input of the algorithm
88   is stored in~$N$ cells, we need~$W\ge\log N$ just to be able to read the input.
89   On the other hand, we are interested in polynomial-time algorithms only, so $\Theta(\log N)$-bit
90   numbers should be sufficient. In practice, we pick~$W$ to be the larger of
91   $\Theta(\log N)$ and the size of integers used in the algorithm's input and output.
92   We will call an integer which fits in a~single memory cell a~\df{machine word.}
93 \endlist
94
95 Both restrictions easily avoid the problems of unbounded parallelism. The first
96 choice is theoretically cleaner and Cook et al.~show nice correspondences to the
97 standard complexity classes, but the calculations of time and space complexity tend
98 to be somewhat tedious. What more, when compared with the RAM with restricted
99 word size, the complexities are usually exactly $\Theta(W)$ times higher.
100 This does not hold in general (consider a~program which uses many small numbers
101 and $\O(1)$ large ones), but it is true for the algorithms we are interested in.
102 Therefore we will always assume that the operations have unit cost and we make
103 sure that all numbers are limited by the available word size.
104
105 \para
106 As for the choice of RAM operations, the following three instruction sets are often used:
107
108 \itemize\ibull
109 \:\df{Word-RAM} --- allows the ``C-language operators'', i.e., addition,
110   subtraction, multiplication, division, remainder, bitwise $\band$, $\bor$, exclusive
111   $\bor$ ($\bxor$) and negation ($\bnot$), and bitwise shifts ($\shl$ and~$\shr$).
112 \:\df{${\rm AC}^0$-RAM} --- allows all operations from the class ${\rm AC}^0$, i.e.,
113   those computable by constant-depth polynomial-size boolean circuits with unlimited
114   fan-in and fan-out. This includes all operations of the Word-RAM except for multiplication,
115   division and remainders, and also many other operations like computing the Hamming
116   weight (number of bits set in a~given number).
117 \:Both restrictions at once.
118 \endlist
119
120 Thorup discusses the usual techniques employed by RAM algorithms in~\cite{thorup:aczero}
121 and he shows that they work on both Word-RAM and ${\rm AC}^0$-RAM, but the combination
122 of the two restrictions is too weak. On the other hand, the intersection of~${\rm AC}^0$
123 with the instruction set of modern processors is already strong enough (e.g., when we
124 add some floating-point operations and multimedia instructions available on the Intel's
125 Pentium~4~\cite{intel:pentium}).
126
127 We will therefore use the Word-RAM instruction set, mentioning differences from the
128 ${\rm AC}^0$-RAM where necessary.
129
130 \nota
131 When speaking of the \df{RAM,} we implicitly mean the version with numbers limited
132 by a~specified word size of $W$~bits, unit cost of operations and memory cells and the instruction
133 set of the Word-RAM. This corresponds to the usage in recent algorithmic literature,
134 although the authors rarely mention the details. In some cases, a~non-uniform variant
135 of the Word-RAM is considered as well (e.g., in~\cite{hagerup:dd}):
136
137 \defn\id{nonuniform}%
138 A~Word-RAM is called \df{weakly non-uniform,} if it is equipped with $\O(1)$-time
139 access to a~constant number of word-sized constants, which depend only on the word
140 size. These are called \df{native constants} and they are available in fixed memory
141 cells when the program starts. (By analogy with the high-level programming languages,
142 these constants can be thought of as computed at ``compile time.'')
143
144 \para
145 The \df{Pointer Machine (PM)} also does not have any well established definition. The
146 various kinds of pointer machines are mapped by Ben-Amram in~\cite{benamram:pm},
147 but unlike the RAM's they turn out to be equivalent up to constant slowdown.
148 Our definition will be closely related to the \em{linking automaton} proposed
149 by Knuth in~\cite{knuth:fundalg}, we will only adapt it to use RAM-like
150 instructions instead of an~opaque control unit.
151
152 The PM works with two different types of data: \df{symbols} from a~finite alphabet
153 and \df{pointers}. The memory of the machine consists of a~fixed amount of \df{registers}
154 (some of them capable of storing a~single symbol, each of the others holds a~single pointer)
155 and an~arbitrary amount of \df{cells}. The structure of all cells is the same: each of them
156 again contains a~fixed number of fields for symbols and pointers. Registers can be addressed
157 directly, the cells only via pointers --- by using a~pointer stored either in a~register,
158 or in a~cell pointed to by a~register (longer chains of pointers cannot be followed in
159 constant time).
160
161 We can therefore view the whole memory as a~directed graph, whose vertices
162 correspond to the cells (the registers are stored in a~single special cell).
163 The outgoing edges of each vertex correspond to pointer fields of the cells and they are
164 labeled with distinct labels drawn from a~finite set. In addition to that,
165 each vertex contains a~fixed amount of symbols. The program can directly access
166 vertices within distance~2 from the register vertex.
167
168 The program is a~finite sequence of instructions of the following kinds:
169
170 \itemize\ibull
171 \:\df{symbol instructions,} which read a~pair of symbols, apply an~arbitrary
172   function on them and write the result to a~symbol register or field;
173 \:\df{pointer instructions} for assignment of pointers to pointer registers/fields
174   and for creation of new memory cells (a~pointer to the new cell is assigned
175   immediately);
176 \:\df{control instructions} --- similarly to the RAM; conditional jumps can decide
177   on~arbitrary unary relations on symbols and compare pointers for equality.
178 \endlist
179
180 Time and space complexity are defined in the straightforward way: all instructions
181 have unit cost and so do all memory cells.
182
183 Both input and output of the machine are passed in the form of a~linked structure
184 pointed to by a~designated register. For example, we can pass graphs back and forth
185 without having to encode them as strings of numbers or symbols. This is important,
186 because with the finite alphabet of the~PM, symbolic representations of graphs
187 generally require super-linear space and therefore also time.\foot{%
188 The usual representation of edges as pairs of vertex labels uses $\Theta(m\log n)$ bits
189 and as a~simple counting argument shows, this is asymptotically optimal for general
190 sparse graphs. On the other hand, specific families of sparse graphs can be stored
191 more efficiently, e.g., by a~remarkable result of Tur\'an~\cite{turan:succinct},
192 planar graphs can be encoded in~$\O(n)$ bits. Encoding of dense graphs is of
193 course trivial as the adjacency matrix has only~$\Theta(n^2)$ bits.}
194
195 \para
196 Compared to the RAM, the PM lacks two important capabilities: indexing of arrays
197 and arithmetic instructions. We can emulate both with poly-logarithmic slowdown,
198 but it will turn out that they are rarely needed in graph algorithms. We are
199 also going to prove that the RAM is strictly stronger, so we will prefer to
200 formulate our algorithms in the PM model and use RAM only when necessary.
201
202 \thm
203 Every program for the Word-RAM with word size~$W$ can be translated to a~PM program
204 computing the same with $\O(W^2)$ slowdown (given a~suitable encoding of inputs and
205 outputs, of course). If the RAM program does not use multiplication, division
206 and remainder operations, $\O(W)$~slowdown is sufficient.
207
208 \proofsketch
209 Represent the memory of the RAM by a~balanced binary search tree or by a~radix
210 trie of depth~$\O(W)$. Values are encoded as~linked lists of symbols pointed
211 to by the nodes of the tree. Both direct and indirect accesses to the memory
212 can therefore be done in~$\O(W)$ time. Use standard algorithms for arithmetic
213 on big numbers: $\O(W)$ per operation except for multiplication, division and
214 remainders which take $\O(W^2)$.\foot{We could use more efficient arithmetic
215 algorithms, but the quadratic bound is good enough for our purposes.}
216 \qed
217
218 \FIXME{Add references, especially to the unbounded parallelism remark.}
219
220 \thm
221 Every program for the PM running in polynomial time can be translated to a~program
222 computing the same on the Word-RAM with only $\O(1)$ slowdown.
223
224 \proofsketch
225 Encode each cell of the PM's memory to $\O(1)$ integers. Store the encoded cells to
226 the memory of the RAM sequentially and use memory addresses as pointers. As the symbols
227 are finite and there is only a~polynomial number of cells allocated during execution
228 of the program, $\O(\log N)$-bit integers suffice ($N$~is the size of the program's input).
229 \qed
230
231 \para
232 There are also \df{randomized} versions of both machines. These are equipped
233 with an~additional instruction for generating a~single random bit. The standard
234 techniques of design and analysis of randomized algorithms apply (see for
235 example Motwani and Raghavan~\cite{motwani:randalg}).
236
237 \FIXME{Consult sources. Does it make more sense to generate random words at once on the RAM?}
238
239 \rem
240 There is one more interesting machine: the \df{Immutable Pointer Machine} (see
241 the description of LISP machines in \cite{benamram:pm}). It differs from the
242 ordinary PM by the inability to modify existing memory cells. Only the contents
243 of the registers are allowed to change. All cell modifications thus have to
244 be performed by creating a~copy of the particular cell with some fields changed.
245 This in turn requires the pointers to the cell to be updated, possibly triggering
246 a~cascade of further cell copies. For example, when a~node of a~binary search tree is
247 updated, all nodes on the path from that node to the root have to be copied.
248
249 One of the advantages of this model is that the states of the machine are
250 persistent --- it is possible to return to a~previously visited state by recalling
251 the $\O(1)$ values of the registers (everything else could not have changed
252 since that time) and ``fork'' the computations. This corresponds to the semantics
253 of pure functional languages, e.g., Haskell~\cite{jones:haskell}.
254
255 Unless we are willing to accept a~logarithmic penalty in execution time and space
256 (in fact, our emulation of the Word-RAM on the PM can be easily made immutable),
257 the design of efficient algorithms for the immutable PM requires very different
258 techniques. Therefore, we will concentrate on the imperative models instead
259 and refer the interested reader to the thorough treatment of purely functional
260 data structures in the Okasaki's monograph~\cite{okasaki:funcds}.
261
262 %--------------------------------------------------------------------------------
263
264 \section{Bucket sorting and contractions}\id{bucketsort}%
265
266 The Contractive Bor\o{u}vka's algorithm (\ref{contbor}) needed to contract a~given
267 set of edges in the current graph and flatten it afterwards, all this in time $\O(m)$.
268 We have spared the technical details for this section and they will be useful
269 in further algorithms, too.
270
271 As already suggested, the contractions can be performed by building an~auxiliary
272 graph and finding its connected components. Thus we will take care of the flattening
273 only.
274
275 \para
276 On the RAM, it is sufficient to sort the edges lexicographically (each edge viewed
277 as an~ordered pair of vertex identifiers with the smaller of the identifiers placed
278 first). We can do that by a two-pass bucket sort with~$n$ buckets corresponding
279 to the vertex identifiers.
280
281 However, there is a~catch in this. Suppose that we use the standard representation
282 of graphs by adjacency lists whose heads are stored in an array indexed by vertex
283 identifiers. When we contract and flatten the graph, the number of vertices decreases,
284 but if we inherit the original vertex identifiers, the arrays will still have the
285 same size. Hence we spend a~super-linear amount of time on scanning the increasingly
286 sparse arrays, most of the time skipping unused entries.
287
288 To avoid this, we have to renumber the vertices after each contraction to component
289 identifiers from the auxiliary graph and we create a~new vertex array. This way,
290 the representation of the graph will be kept linear with respect to the size of the
291 current graph.
292
293 \para
294 The pointer representation of graphs does not suffer from sparsity as the vertices
295 are always identified by pointers to per-vertex structures. Each such structure
296 then contains all attributes associated with the vertex, including the head of its
297 adjacency list. However, we have to find a~way how to perform bucket sorting
298 without arrays.
299
300 We will keep a~list of the per-vertex structures which defines the order of~vertices.
301 Each such structure will contain a~pointer to the head of the corresponding bucket,
302 again stored as a~list. Putting an~edge to a~bucket can be done in constant time then,
303 scanning all~$n$ buckets takes $\O(n+m)$ time.
304
305 \FIXME{Add an example of locally determined orders, e.g., tree isomorphism?}
306
307 %--------------------------------------------------------------------------------
308
309 \section{Data structures on the RAM}
310
311 There is a~lot of data structures designed specifically for the RAM, taking
312 advantage of both indexing and arithmetics. In many cases, they surpass the known
313 lower bounds for the same problem on the~PM and they often achieve constant time
314 per operation, at least when either the magnitude of the values or the size of
315 the data structure are suitably bounded.
316
317 A~classical result of this type are the trees of van Emde Boas~\cite{boas:vebt},
318 which represent a~subset of the integers $\{0,\ldots,U-1\}$, allowing insertion,
319 deletion and order operations (minimum, maximum, successor etc.) in time $\O(\log\log U)$,
320 regardless of the size of the subset. If we replace the heap used in the Jarn\'\i{}k's
321 algorithm (\ref{jarnik}) by this structure, we immediately get an~algorithm
322 for finding the MST in integer-weighted graphs in time $\O(m\log\log w_{max})$,
323 where $w_{max}$ is the maximum weight. We will show later that it is even
324 possible to achieve linear time complexity for arbitrary integer weights.
325
326 A~real breakthrough has been made by Fredman and Willard, who introduced
327 the Fusion trees~\cite{fw:fusion} which again perform membership and predecessor
328 operation on a~set of $n$~integers, but this time with complexity $\O(\log_W n)$
329 per operation on a~Word-RAM with $W$-bit words. This of course assumes that
330 each element of the set fits in a~single word. As $W$ must at least~$\log n$,
331 the operations take $\O(\log n/\log\log n)$ and we are able to sort $n$~integers
332 in time~$o(n\log n)$. This was a~beginning of a~long sequence of faster and
333 faster sorting algorithms, culminating with the work by Thorup and Han.
334 They have improved the time complexity of integer sorting to $\O(n\log\log n)$ deterministically~\cite{han:detsort}
335 and expected $\O(n\sqrt{\log\log n})$ for randomized algorithms~\cite{hanthor:randsort},
336 both in linear space.
337
338 Despite the recent progress, the corner-stone of most RAM data structures
339 is still the representation of data structures by integers introduced by Fredman
340 and Willard. It will also form a~basis for the rest of this chapter.
341
342 \FIXME{Add more history.}
343
344 %--------------------------------------------------------------------------------
345
346 \section{Bits and vectors}\id{bitsect}
347
348 In this rather technical section, we will show how the RAM can be used as a~vector
349 computer to operate in parallel on multiple elements, as long as these elements
350 fit in a~single machine word. At the first sight this might seem useless, because we
351 cannot require large word sizes, but surprisingly often the elements are small
352 enough relative to the size of the algorithm's input and thus also relative to
353 the minimum possible word size. Also, as the following lemma shows, we can
354 easily emulate slightly longer words:
355
356 \lemman{Multiple-precision calculations}
357 Given a~RAM with $W$-bit words, we can emulate all calculation and control
358 instructions of a~RAM with word size $kW$ in time depending only on the~$k$.
359 (This is usually called \df{multiple-precision arithmetics.})
360
361 \proof
362 We split each word of the ``big'' machine to $W'$-bit blocks, where $W'=W/2$, and store these
363 blocks in $2k$ consecutive memory cells. Addition, subtraction, comparison and
364 bitwise logical operations can be performed block-by-block. Shifts by a~multiple
365 of~$W'$ are trivial, otherwise we can combine each block of the result from
366 shifted versions of two original blocks.
367 To multiply two numbers, we can use the elementary school algorithm using the $W'$-bit
368 blocks as digits in base $2^{W'}$ --- the product of any two blocks fits
369 in a~single word.
370
371 Division is harder, but Newton-Raphson iteration (see~\cite{ito:newrap})
372 converges to the quotient in a~constant number of iterations, each of them
373 involving $\O(1)$ multiple-precision additions and multiplications. A~good
374 starting approximation can be obtained by dividing the two most-significant
375 (non-zero) blocks of both numbers.
376
377 Another approach to division is using the improved elementary school algorithm as described
378 by Knuth in~\cite{knuth:seminalg}. It uses $\O(k^2)$ steps, but the steps involve
379 calculation of the most significant bit set in a~word. We will show below that it
380 can be done in constant time, but we have to be careful to avoid division instructions.
381 \qed
382
383 \notan{Bit strings}\id{bitnota}%
384 We will work with binary representations of natural numbers by strings over the
385 alphabet $\{\0,\1\}$: we will use $\(x)$ for the number~$x$ written in binary,
386 $\(x)_b$ for the same padded to exactly $b$ bits by adding leading zeroes,
387 and $x[k]$ for the value of the $k$-th bit of~$x$ (with a~numbering of bits such that $2^k[k]=1$).
388 The usual conventions for operations on strings will be utilized: When $s$
389 and~$t$ are strings, we write $st$ for their concatenation and
390 $s^k$ for the string~$s$ repeated $k$~times.
391 When the meaning is clear from the context,
392 we will use $x$ and $\(x)$ interchangeably to avoid outbreak of symbols.
393
394 \defn
395 The \df{bitwise encoding} of a~vector ${\bf x}=(x_0,\ldots,x_{d-1})$ of~$b$-bit numbers
396 is an~integer~$x$ such that $\(x)=\(x_{d-1})_b\0\(x_{d-2})_b\0\ldots\0\(x_0)_b$, i.e.,
397 $x = \sum_i 2^{(b+1)i}\cdot x_i$. (We have interspersed the elements with \df{separator bits.})
398
399 \notan{Vectors}\id{vecnota}%
400 We will use boldface letters for vectors and the same letters in normal type
401 for their encodings. The elements of a~vector~${\bf x}$ will be written as
402 $x_0,\ldots,x_{d-1}$.
403
404 \para
405 If we want to fit the whole vector in a~single word, the parameters $b$ and~$d$ must satisty
406 the condition $(b+1)d\le W$.
407 By using multiple-precision arithmetics, we can encode all vectors satisfying $bd=\O(W)$.
408 We will now describe how to translate simple vector manipulations to sequences of $\O(1)$ RAM operations
409 on their codes. As we are interested in asymptotic complexity only, we prefer clarity
410 of the algorithms over saving instructions. Among other things, we freely use calculations
411 on words of size $\O(bd)$, assuming that the Multiple-precision lemma comes to save us
412 when necessary.
413
414 \para
415 First of all, let us observe that we can use $\band$ and $\bor$ with suitable constants
416 to write zeroes or ones to an~arbitrary set of bit positions at once. These operations
417 are usually called \df{bit masking}. Also, any element of a~vector can be extracted or
418 replaced by a~different value in $\O(1)$ time by masking and shifts.
419
420 \newdimen\slotwd
421 \def\setslot#1{\setbox0=#1\slotwd=\wd0}
422 \def\slot#1{\hbox to \slotwd{\hfil #1\hfil}}
423 \def\[#1]{\slot{$#1$}}
424 \def\9{\rack{\0}{\hss$\cdot$\hss}}
425
426 \def\alik#1{%
427 \medskip
428 \halign{\hskip 0.15\hsize\hfil $ ##$&\hbox to 0.6\hsize{${}##$ \hss}\cr
429 #1}
430 \medskip
431 }
432
433 \algn{Operations on vectors with $d$~elements of $b$~bits each}\id{vecops}
434
435 \itemize\ibull
436
437 \:$\<Replicate>(\alpha)$ --- creates a~vector $(\alpha,\ldots,\alpha)$:
438
439 \alik{\<Replicate>(\alpha)=\alpha\cdot(\0^b\1)^d. \cr}
440
441 \:$\<Sum>(x)$ --- calculates the sum of the elements of~${\bf x}$, assuming that
442 the result fits in $b$~bits:
443
444 \alik{\<Sum>(x) = x \bmod \1^{b+1}. \cr}
445
446 This is correct because when we calculate modulo~$\1^{b+1}$, the number $2^{b+1}=\1\0^{b+1}$
447 is congruent to~1 and thus $x = \sum_i 2^{(b+1)i}\cdot x_i \equiv \sum_i 1^i\cdot x_i \equiv \sum_i x_i$.
448 As the result should fit in $b$~bits, the modulo makes no difference.
449
450 If we want to avoid division, we can use double-precision multiplication instead:
451
452 \setslot{\hbox{~$\0x_{d-1}$}}
453 \def\.{\[]}
454 \def\z{\[\0^b\1]}
455 \def\dd{\slot{$\cdots$}}
456 \def\vd{\slot{$\vdots$}}
457 \def\rule{\noalign{\medskip\nointerlineskip}$\hrulefill$\cr\noalign{\nointerlineskip\medskip}}
458
459 \alik{
460 \[\0x_{d-1}] \dd \[\0x_2] \[\0x_1] \[\0x_0] \cr
461 *~~ \z \dd \z\z\z \cr
462 \rule
463 \[x_{d-1}] \dd \[x_2] \[x_1] \[x_0] \cr
464 \[x_{d-1}] \[x_{d-2}] \dd \[x_1] \[x_0] \. \cr
465 \[x_{d-1}] \[x_{d-2}] \[x_{d-3}] \dd \[x_0] \. \. \cr
466 \vd\vd\vd\vd\.\.\.\cr
467 \[x_{d-1}] \dd \[x_2]\[x_1]\[x_0] \. \. \. \. \cr
468 \rule
469 \[r_{d-1}] \dd \[r_2] \[r_1] \[s_d] \dd \[s_3] \[s_2] \[s_1] \cr
470 }
471
472 This way, we even get the vector of all partial sums:
473 $s_k=\sum_{i=0}^{k-1}x_i$, $r_k=\sum_{i=k}^{d-1}x_i$.
474
475 \:$\<Cmp>(x,y)$ --- element-wise comparison of~vectors ${\bf x}$ and~${\bf y}$,
476 i.e., a~vector ${\bf z}$ such that $z_i=1$ if $x_i<y_i$ and $z_i=0$ otherwise.
477
478 We replace the separator zeroes in~$x$ by ones and subtract~$y$. These ones
479 change back to zeroes exactly at the positions where $x_i<y_i$ and they stop
480 carries from propagating, so the fields do not interact with each other:
481
482 \setslot{\vbox{\hbox{~$x_{d-1}$}\hbox{~$y_{d-1}$}}}
483 \def\9{\rack{\0}{\hss ?\hss}}
484 \alik{
485    \1 \[x_{d-1}] \1 \[x_{d-2}] \[\cdots] \1 \[x_1] \1 \[x_0]  \cr
486 -~ \0 \[y_{d-1}] \0 \[y_{d-2}] \[\cdots] \0 \[y_1] \0 \[y_0]  \cr
487 \rule
488    \9 \[\ldots]  \9 \[\ldots]  \[\cdots] \9 \[\ldots] \9 \[\ldots] \cr
489 }
490
491 It only remains to shift the separator bits to the right positions, negate them
492 and mask out all other bits.
493
494 \:$\<Rank>(x,\alpha)$ --- returns the number of elements of~${\bf x}$ which are less than~$\alpha$,
495 assuming that the result fits in~$b$ bits:
496
497 \alik{
498 \<Rank>(x,\alpha) = \<Sum>(\<Cmp>(x,\<Replicate>(\alpha))). \cr
499 }
500
501 \:$\<Insert>(x,\alpha)$ --- inserts~$\alpha$ into a~sorted vector $\bf x$:
502
503 We calculate $k = \<Rank>(x,\alpha)$ first, then insert~$\alpha$ as the $k$-th
504 field of~$\bf x$ using masking operations and shifts.
505
506 \:$\<Unpack>(\alpha)$ --- creates a~vector whose elements are the bits of~$\(\alpha)_d$.
507 In other words, inserts blocks~$\0^b$ between the bits of~$\alpha$. Assuming that $b\ge d$,
508 we can do it as follows:
509
510 \algo
511 \:$x\=\<Replicate>(\alpha)$.
512 \:$y\=(2^{b-1},2^{b-2},\ldots,2^0)$. \cmt{bitwise encoding of this vector}
513 \:$z\=x \band y$.
514 \:Return $\<Cmp>(z,y)$.
515 \endalgo
516
517 Let us observe that $z_i$ is either zero or equal to~$y_i$ depending on the value
518 of the $i$-th bit of the number~$\alpha$. Comparing it with~$y_i$ normalizes it
519 to either zero or one.
520
521 \:$\<Unpack>_\pi(\alpha)$ --- like \<Unpack>, but changes the order of the
522 bits according to a~fixed permutation~$\pi$: The $i$-th element of the
523 resulting vector is equal to~$\alpha[\pi(i)]$.
524
525 Implemented as above, but with a~mask $y=(2^{\pi(b-1)},\ldots,2^{\pi(0)})$.
526
527 \:$\<Pack>(x)$ --- the inverse of \<Unpack>: given a~vector of zeroes and ones,
528 it produces a~number whose bits are the elements of the vector (in other words,
529 it crosses out the $\0^b$ blocks).
530
531 We interpret the~$x$ as an~encoding of a~vector with elements one bit shorter
532 and we sum these elements. For example, when $n=4$ and~$b=4$:
533
534 \setslot{\hbox{$x_3$}}
535 \def\z{\[\0]}
536 \def\|{\hskip1pt\vrule height 10pt depth 4pt\hskip1pt}
537 \def\.{\hphantom{\|}}
538
539 \alik{
540 \|\z\.\z\.\z\.\z\.\[x_3]\|\z\.\z\.\z\.\z\.\[x_2]\|\z\.\z\.\z\.\z\[x_1]\|\z\.\z\.\z\.\z\.\[x_0]\|\cr
541 \|\z\.\z\.\z\.\z\|\[x_3]\.\z\.\z\.\z\|\z\.\[x_2]\.\z\.\z\|\z\.\z\[x_1]\.\z\|\z\.\z\.\z\.\[x_0]\|\cr
542 }
543
544 However, this ``reformatting'' does not produce a~correct encoding of a~vector,
545 because the separator zeroes are missing. For this reason, the implementation
546 of~\<Sum> using modulo does not work correctly (it produces $\0^b$ instead of $\1^b$).
547 We therefore use the technique based on multiplication instead, which does not need
548 the separators. (Alternatively, we can observe that $\1^b$ is the only case
549 affected, so we can handle it separately.)
550
551 \endlist
552
553 \para
554 We can use the aforementioned tricks to perform interesting operations on individual
555 numbers in constant time, too. Let us assume for a~while that we are
556 operating on $b$-bit numbers and the word size is at least~$b^2$.
557 This enables us to make use of intermediate vectors with $b$~elements
558 of $b$~bits each.
559
560 \algn{Integer operations in quadratic workspace}\id{lsbmsb}
561
562 \itemize\ibull
563
564 \:$\<Weight>(\alpha)$ --- computes the Hamming weight of~$\alpha$, i.e., the number of ones in~$\(\alpha)$.
565
566 We perform \<Unpack> and then \<Sum>.
567
568 \:$\<Permute>_\pi(\alpha)$ --- shuffles the bits of~$\alpha$ according
569 to a~fixed permutation~$\pi$.
570
571 We perform $\<Unpack>_\pi$ and \<Pack> back.
572
573 \:$\<LSB>(\alpha)$ --- finds the least significant bit of~$\alpha$,
574 i.e., the smallest~$i$ such that $\alpha[i]=1$.
575
576 By a~combination of subtraction with $\bxor$, we create a~number
577 which contains ones exactly at the position of $\<LSB>(\alpha)$ and below:
578
579 \alik{
580 \alpha&=                        \9\9\9\9\9\1\0\0\0\0\cr
581 \alpha-1&=                      \9\9\9\9\9\0\1\1\1\1\cr
582 \alpha\bxor(\alpha-1)&=         \0\9\9\9\0\1\1\1\1\1\cr
583 }
584
585 Then we calculate the \<Weight> of the result and subtract~1.
586
587 \:$\<MSB>(\alpha)$ --- finds the most significant bit of~$\alpha$ (the position
588 of the highest bit set).
589
590 Reverse the bits of the number~$\alpha$ first by calling \<Permute>, then apply \<LSB>
591 and subtract the result from~$b-1$.
592
593 \endlist
594
595 \rem
596 As noted by Brodnik~\cite{brodnik:lsb} and others, the space requirements of
597 the \<LSB> operation can be reduced to linear. We split the input to $\sqrt{b}$
598 blocks of $\sqrt{b}$ bits each. Then we determine which blocks are non-zero and
599 identify the lowest such block (this is a~\<LSB> of a~number whose bits
600 correspond to the blocks). Finally we calculate the \<LSB> of this block. In
601 both calls to \<LSB,> we have a $\sqrt{b}$-bit number in a~$b$-bit word, so we
602 can use the previous algorithm. The same trick of course works for finding the
603 \<MSB> as well.
604
605 The following algorithm shows the details.
606
607 \algn{LSB in linear workspace}
608
609 \algo\id{lsb}
610 \algin A~$w$-bit number~$\alpha$.
611 \:$b\=\lceil\sqrt{w}\,\rceil$. \cmt{size of a~block}
612 \:$\ell\=b$. \cmt{the number of blocks is the same}
613 \:$x\=(\alpha \band (\0\1^b)^\ell) \bor (\alpha \band (\1\0^b)^\ell)$.
614 \hfill\break
615 \cmt{encoding of a~vector~${\bf x}$ such that $x_i\ne 0$ iff the $i$-th block is non-zero}%
616 \foot{Why is this so complicated? It is tempting to take $\alpha$ itself as a~code of this vector,
617 but we unfortunately need the separator bits between elements, so we create them and
618 relocate the bits we have overwritten.}
619 \:$y\=\<Cmp>(0,x)$. \cmt{$y_i=1$ if the $i$-th block is non-zero, otherwise $y_0=0$}
620 \:$\beta\=\<Pack>(y)$. \cmt{each block compressed to a~single bit}
621 \:$p\=\<LSB>(\beta)$. \cmt{the index of the lowest non-zero block}
622 \:$\gamma\=(\alpha \shr bp) \band \1^b$. \cmt{the contents of that block}
623 \:$q\=\<LSB>(\gamma)$. \cmt{the lowest bit set there}
624 \algout $\<LSB>(\alpha) = bp+q$.
625 \endalgo
626
627 \rem
628 We have used a~plenty of constants which depend on the format of the vectors.
629 Either we can write non-uniform programs (see \ref{nonuniform}) and use native constants,
630 or we can observe that all such constants can be easily manufactured. For example,
631 $(\0^b\1)^d = \1^{(b+1)d} / \1^{b+1} = (2^{(b+1)d}-1)/(2^{b+1}-1)$. The only exceptions
632 are the~$w$ and~$b$ in the LSB algorithm \ref{lsb}, which we are unable to produce
633 in constant time. In practice we use the ``bit tricks'' as frequently called subroutines
634 in an~encompassing algorithm, so we usually can spend a~lot of time on the precalculation
635 of constants performed once during algorithm startup.
636
637 %--------------------------------------------------------------------------------
638
639 \section{Q-Heaps}\id{qheaps}%
640
641 We have shown how to perform relatively complicated operations on a~set of values
642 in constant time, but so far only under the assumption that the number of these
643 values is small enough and that the values themselves are also small enough
644 (so that the whole set fits in $\O(1)$ machine words). Now we will show how to
645 lift the restriction on the magnitude of the values and still keep constant time
646 complexity. We will describe a~simplified version of the Q-Heaps developed by
647 Fredman and Willard in~\cite{fw:transdich}.
648
649 The Q-Heap represents a~set of at most~$k$ word-sized integers, where $k\le W^{1/4}$
650 and $W$ is the word size of the machine. It will support insertion, deletion, finding
651 of minimum, and other operations described below, in constant time, provided that
652 we are willing to spend~$\O(2^{k^4})$ time on preprocessing.
653
654 The exponential-time preprocessing may sound alarming, but a~typical application uses
655 Q-Heaps of size $k=\log^{1/4} N$, where $N$ is the size of the algorithm's input,
656 which guarantees that $k\le W^{1/4}$ and $\O(2^{k^4}) = \O(N)$. Let us however
657 remark that the whole construction is primarily of theoretical importance
658 and that the huge constants involved everywhere make these heaps useless
659 for practical algorithms. However, many of the tricks used prove themselves
660 useful even in real-life implementations.
661
662 Preprocessing makes it possible to precompute tables for almost arbitrary functions
663 and then assume that they can be evaluated in constant time:
664
665 \lemma\id{qhprecomp}%
666 When~$f$ is a~function computable in polynomial time, $\O(2^{k^4})$ time is enough
667 to precompute a~table of the values of~$f$ for the values of its arguments whose total
668 bit size is $\O(k^3)$.
669
670 \proof
671 There are $2^{\O(k^3)}$ possible combinations of arguments of the given size and for each of
672 them we spend $\O(k^c)$ time by calculating the function (for some~$c\ge 1$). It remains
673 to observe that $2^{\O(k^3)}\cdot \O(k^c) = \O(2^{k^4})$.
674 \qed
675
676 \para
677 We will first show an~auxiliary construction based on tries and then derive
678 the actual definition of the Q-Heap from it.
679
680 \nota
681 Let us introduce some notation first:
682 \itemize\ibull
683 \:$W$ --- the word size of the RAM,
684 \:$k = \O(W^{1/4})$ --- the limit on the size of the heap,
685 \:$n\le k$ --- the number of elements in the represented set,
686 \:$X=\{x_1, \ldots, x_n\}$ --- the elements themselves: distinct $W$-bit numbers
687 indexed in a~way that $x_1 < \ldots < x_n$,
688 \:$c_i = \<MSB>(x_i \bxor x_{i+1})$ --- the most significant bit of those in which $x_i$ and~$x_{i+1}$ differ,
689 \:$R_X(x)$ --- the rank of~$x$ in~$X$, that is the number of elements of~$X$, which are less than~$x$
690 (where $x$~itself need not be an~element of~$X$).\foot{We will dedicate the whole chapter \ref{rankchap} to the
691 study of various ranks.}
692 \endlist
693
694 \defn
695 A~\df{trie} for a~set of strings~$S$ over a~finite alphabet~$\Sigma$ is
696 a~rooted tree whose vertices are the prefixes of the strings in~$S$ and there
697 is an~edge going from a~prefix~$\alpha$ to a~prefix~$\beta$ iff $\beta$ can be
698 obtained from~$\alpha$ by adding a~single symbol of the alphabet. The edge
699 will be labeled with the particular symbol. We will also define a~\df{letter depth}
700 of a~vertex to be the length of the corresponding prefix and mark the vertices
701 which match a~string of~$S$.
702
703 A~\df{compressed trie} is obtained from the trie by removing the vertices of outdegree~1.
704 Whereever is a~directed path whose internal vertices have outdegree~1, we replace this
705 path by a~single edge labeled with the contatenation of the original edge's labels.
706
707 In both kinds of tries, we will order the outgoing edges of every vertex by their labels
708 lexicographically.
709
710 \obs
711 In both tries, the root of the tree is the empty word and for every vertex, the
712 corresponding prefix is equal to the concatenation of edge labels on the path
713 leading from the root to the vertex. The letter depth of the vertex is equal to
714 the total size of these labels. All leaves correspond to strings in~$S$, but so can
715 some internal vertices if there are two strings in~$S$ such that one is a~prefix
716 of the other.
717
718 Furthermore, the labels of all edges leaving a~common vertex are always
719 distinct and when we compress the trie, no two such labels have share their initial
720 symbols. This allows us to search in the trie efficiently: when looking for
721 a~string~$x$, we follow the path from the root and whenever we visit
722 an~internal vertex of letter depth~$d$, we test the $d$-th character of~$x$,
723 follow the edge whose label starts with this character, and check that the
724 rest of the label matches.
725
726 The compressed trie is also efficient in terms of space consumption --- it has
727 $\O(\vert S\vert)$ vertices (this can be easily shown by induction on~$\vert S\vert$)
728 and all edge labels can be represented in space linear in the sum of the
729 lengths of the strings in~$S$.
730
731 \defn
732 For our set~$X$, we will define~$T$ as a~compressed trie for the set of binary
733 encodings of the numbers~$x_i$, padded to exactly $W$~bits, i.e., for $S = \{ \(x)_W ; x\in X \}$.
734
735 \obs
736 The trie~$T$ has several interesting properties. Since all words in~$S$ have the same
737 length, the leaves of the trie correspond to these exact words, that is to the numbers~$x_i$.
738 The inorder traversal of the trie enumerates the words of~$S$ in lexicographic order
739 and therefore also the~$x_i$'s in the order of their values. Between each
740 pair of leaves $x_i$ and~$x_{i+1}$ it visits an~internal vertex whose letter depth
741 is exactly~$W-1-c_i$.
742
743 \para
744 Let us now modify the algorithm for searching in the trie and make it compare
745 only the first symbols of the edges. For $x\in X$, the algorithm will still
746 return the correct leave, for all~$x$ outside~$X$ it will no longer fail
747 and instead it will land in some leaf $x_i$. We will call the index of this leaf $T(x)$.
748 At the first sight this vertex may seem unrelated, but we will show that it can
749 be used to determine the rank of~$x$ in~$X$, which will later form a~basis
750 for all other Q-Heap operations:
751
752 \lemma\id{qhdeterm}%
753 The rank $R_X(x)$ is uniquely determined by a~combination of:
754 \itemize\ibull
755 \:the trie~$T$,
756 \:the index $i=T(x)$ of the leaf visited when searching for~$x$ in~$T$,
757 \:the relation ($<$, $=$, $>$) between $x$ and $x_i$,
758 \:the bit position $b=\<MSB>(x\bxor x_i)$ of the first disagreement between~$x$ and~$x_i$.
759 \endlist
760
761 \proof
762 If $x\in X$, we detect that from $x_i=x$ and the rank is obviously~$i$ itself.
763 Let us assume that $x\not\in X$ and imagine that we follow the same path as during the search for~$T(x)$,
764 but this time we check the full edge labels. The position~$b$ is the first position
765 where~$\(x)$ disagrees with a~label. Before this point, all edges not taken by
766 the search were leading either to subtrees containing elements all smaller than~$x$
767 or all larger than~$x$ and the only values not known yet are those in the subtree
768 below the edge which we currently consider. Now if $x[b]=0$ (and therefore $x<x_i$),
769 all values in the subtree have $x_j[b]=1$ and thus they are larger. In the other
770 case, $x[b]=1$ and $x_j[b]=0$, so they are smaller.
771 \qed
772
773 \para
774 The preceding lemma shows that the rank can be computed in polynomial time, but
775 unfortunately the variables on which it depends are too large for the table to
776 be efficiently precomputed. We will therefore carefully choose a~representation
777 of the trie, which is compact enough.
778
779 \lemma\id{citree}%
780 The trie is uniquely determined by the order of the values~$c_1,\ldots,c_{n-1}$.
781
782 \proof
783 We already know that the letter depths of the trie vertices are exactly
784 the numbers~$W-1-c_i$. The root of the trie must have the smallest of these
785 letter depths, i.e., it must correspond to the highest numbered bit. Let
786 us call this bit~$c_i$. This implies that the values $x_1,\ldots,x_i$
787 must lie in the left subtree of the root and $x_{i+1},\ldots,x_n$ in its
788 right subtree. Both subtrees can be then constructed recursively.\foot{This
789 construction is also known as the \df{cartesian tree} for the sequence
790 $c_1,\ldots,c_n$.}
791 \qed
792
793 \para
794 However, the vector of the $c_i$'s is also too long (is has $k\log W$ bits
795 and we have no upper bound on~$W$ in terms of~$k$), so we will compress it even
796 further:
797
798 \nota
799 \itemize\ibull
800 \:$B = \{c_1,\ldots,c_n\}$ --- the set of all bit positions examined by the trie,
801 stored as a~sorted array,
802 \:$C : \{1,\ldots,n\} \rightarrow \{1,\ldots,n\}$ --- a~function such that
803 $c_i = B[C(i)]$,
804 \:$x[B]$ --- a~bit string containing the bits of~$x$ originally located
805 at the positions indexed by~$B$.
806 \endlist
807
808 \obs
809 The set~$B$ has $\O(k\log W)=\O(W)$ bits, so it can be stored in a~constant number
810 of machine words as a~vector. The function~$C$ can be also stored as a~vector
811 of $k\log k$ bits.
812
813 \lemma
814 The rank $R_X(x)$ can be computed in constant time from:
815 \itemize\ibull
816 \:the function~$C$,
817 \:the values $x_1,\ldots,x_n$,
818 \:the bit string~$x[B]$,
819 \:$x$ itself.
820 \endlist
821
822 \proof
823 We know that the trie~$T$ is uniquely determined by the order of the $c_i$'s
824 and therefore by the function~$C$ since the array~$B$ is sorted. The shape of
825 the trie together with the bits in $x[B]$ determine the leaf $T[x]$ visited
826 when searching for~$x$. All this can be computed in polynomial time and it
827 depends on $\O(k\log k)$ bits of input, so according to Lemma~\ref{qhprecomp}
828 we can look it up in a~precomputed table.
829
830 Similarly we will determine all other ingredients of Lemma~\ref{qhdeterm} in
831 constant time. As we know~$x$ and all the $x_i$'s, we can immediately find
832 the relation $x$ and $x_{T[x]}$ and use the LSB/MSB algorithm (\ref{lsb})
833 to find the topmost disagreeing bit.
834
835 All these ingredients can be stored in $\O(k\log k)$ bits, so we may assume
836 that the rank can be looked up in constant time as well.
837 \qed
838
839
840 \endpart