Algorithm Search⌘K

Proofs: Bit vector

These are the human proofs of Bit vector, at full length.

The packed bit vector answers access, rank and select correctly full

This proof shows: correctness.

We use the pseudocode of the page, with words of ww bits, and write βt(x)=⌊x/2t⌋ mod 2\beta_t(x) = \lfloor x / 2^t \rfloor \bmod 2 for bit tt of a word xx. Let b[0…n−1]b[0 \ldots n-1] be the input and mm the number of its 1s.

Lemma 1 (BUILD). After BUILD, (P1) βi mod w(W[⌊i/w⌋])=b[i]\beta_{i \bmod w}(W[\lfloor i/w \rfloor]) = b[i] for 0≤i<n0 \le i < n, and (P2) βt(W[⌈n/w⌉−1])=0\beta_t(W[\lceil n/w \rceil - 1]) = 0 for each tt with w(⌈n/w⌉−1)+t≥nw(\lceil n/w \rceil - 1) + t \ge n.

Proof. The map i↦(⌊i/w⌋,i mod w)i \mapsto (\lfloor i/w \rfloor, i \bmod w) is one to one, so each bit of each word belongs to at most one position ii. Line L1 sets each word to 0. Line L3, at step ii, sets the bit (⌊i/w⌋,i mod w)(\lfloor i/w \rfloor, i \bmod w) to 1 when b[i]=1b[i] = 1, and the OR leaves all other bits as they are. So after the loop of line L2, the bit of position ii is 1 exactly when b[i]=1b[i] = 1, which is (P1). The bits that belong to no position i<ni < n were set to 0 by line L1 and never written, which is (P2). □\square

The structure is static, so (P1) and (P2) hold for each later query.

Lemma 2 (ACCESS). ACCESS(i)(i) returns b[i]b[i] for 0≤i<n0 \le i < n.

Proof. For a word xx and 0≤t<w0 \le t < w, (x≫t)∧1=⌊x/2t⌋ mod 2=βt(x)(x \gg t) \wedge 1 = \lfloor x / 2^t \rfloor \bmod 2 = \beta_t(x). With x=W[⌊i/w⌋]x = W[\lfloor i/w \rfloor] and t=i mod wt = i \bmod w, line L4 returns b[i]b[i] by (P1). □\square

Lemma 3 (RANK1). RANK1(i)(i) returns rank1(i)=∣{p<i:b[p]=1}∣\text{rank}_1(i) = |\{p < i : b[p] = 1\}| for 0≤i≤n0 \le i \le n.

Proof. Let q=⌊i/w⌋q = \lfloor i/w \rfloor and r=i mod wr = i \bmod w, so i=qw+ri = qw + r. The set {0,…,i−1}\{0, \ldots, i-1\} is the disjoint union of the blocks {kw,…,kw+w−1}\{kw, \ldots, kw + w - 1\} for 0≤k<q0 \le k < q and of {qw,…,qw+r−1}\{qw, \ldots, qw + r - 1\}. Each block k<qk < q lies inside {0,…,n−1}\{0, \ldots, n-1\} because kw+w−1≤qw−1<i≤nkw + w - 1 \le qw - 1 < i \le n. By (P1), the number of 1s of bb in block kk is the number of 1 bits of W[k]W[k], which line L6 adds. If r>0r > 0, then qw<i≤nqw < i \le n, so W[q]W[q] exists. The word W[q]∧(2r−1)W[q] \wedge (2^r - 1) keeps the bits 00 to r−1r - 1 of W[q]W[q], which by (P1) are b[qw],…,b[i−1]b[qw], \ldots, b[i-1], and line L7 adds their number of 1s. If r=0r = 0, the last part is empty and line L7 adds nothing. So line L8 returns the number of 1s in b[0…i−1]b[0 \ldots i-1]. □\square

Lemma 4 (SELECT1). For 1≤j≤m1 \le j \le m, SELECT1(j)(j) returns the position pp with b[p]=1b[p] = 1 and rank1(p)=j−1\text{rank}_1(p) = j - 1.

Proof. Let j0j_0 be the argument and ckc_k the number of 1 bits of W[k]W[k]. By (P1) and (P2), ckc_k is the number of 1s of bb in block kk, and ∑kck=m\sum_k c_k = m. We use this invariant of the loop of line L10:

(S) 1≤j1 \le j and j0=j+∑k′<kck′j_0 = j + \sum_{k' < k} c_{k'}.

It holds at the start with k=0k = 0 and j=j0≥1j = j_0 \ge 1. An iteration runs only if ck<jc_k < j, and it sets j←j−ck≥1j \leftarrow j - c_k \ge 1 and k←k+1k \leftarrow k + 1, so (S) holds again. The loop stops: if it reached k=⌈n/w⌉k = \lceil n/w \rceil, then (S) would give j0=j+m>mj_0 = j + m > m, against j0≤mj_0 \le m. So the loop stops at a word W[k]W[k] with ck≥jc_k \ge j, and kk grows by one in each iteration, so at most ⌈n/w⌉\lceil n/w \rceil iterations run. By (S), exactly j0−jj_0 - j ones of bb lie before block kk, so the wanted 1 is the jj-th 1 of W[k]W[k], counted from the least significant bit.

For x≠0x \ne 0, the word x∧(x−1)x \wedge (x - 1) is xx with its lowest 1 bit cleared: if the lowest 1 is at bit tt, then x−1x - 1 has bits 00 to t−1t - 1 set, bit tt cleared and the higher bits of xx, so the AND keeps the higher bits and clears bits 00 to tt. Line L12 does this j−1j - 1 times on x=W[k]x = W[k], which has ck≥jc_k \ge j ones, so it removes the j−1j - 1 lowest ones and x≠0x \ne 0 after it. The lowest 1 of the new xx is the jj-th 1 of W[k]W[k], at bit $t = $ LOWEST(x)(x). Line L13 returns p=kw+tp = kw + t. By (P1) b[p]=1b[p] = 1, and the 1s before pp are the j0−jj_0 - j ones before block kk and the j−1j - 1 ones of W[k]W[k] below bit tt, so rank1(p)=j0−1\text{rank}_1(p) = j_0 - 1. □\square

Lemma 5 (rank and select of 0s). rank0(i)=i−rank1(i)\text{rank}_0(i) = i - \text{rank}_1(i), and SELECT0 is correct when it runs SELECT1 on the words ¬W[k]\neg W[k], with the last word masked to its first n−w(⌈n/w⌉−1)n - w(\lceil n/w \rceil - 1) bits.

Proof. Each position p<ip < i holds a 0 or a 1, so the two counts add to ii. For select, the masked complemented words satisfy (P1) and (P2) for the bit sequence 1−b[0],…,1−b[n−1]1 - b[0], \ldots, 1 - b[n-1]: the complement flips each bit, and the mask clears exactly the padding bits, which the complement had set. Lemma 4 applied to that sequence gives the position of the jj-th 0 of bb. ■\blacksquare

Back to Bit vector