Algorithm Search⌘K

Proofs: Rank/select dictionary

These are the human proofs of Rank/select dictionary, at full length.

The rank/select dictionary returns rank and select correctly full

This proof shows: correctness.

We use the pseudocode and the notation of the page. P[0…m−1]P[0 \ldots m-1] lists the positions of the 1s of BB in increasing order, so select1(j)=P[j−1]\text{select}_1(j) = P[j - 1] for 1≤j≤m1 \le j \le m. The parameters k1,k2≥1k_1, k_2 \ge 1 and T1,T2≥1T_1, T_2 \ge 1 are fixed. The correctness does not depend on their values; only the time and the space do.

Lemma 1 (BUILD-SELECT). After BUILD-SELECT, for each group gg, with Q=Qg=P[gk1…min⁡(m,(g+1)k1)−1]Q = Q_g = P[g k_1 \ldots \min(m, (g+1)k_1) - 1]: G[g]=Q[0]G[g] = Q[0]; if max⁡Q−min⁡Q≥T1\max Q - \min Q \ge T_1 then sparse[g][g] is true and E1[g]=QE_1[g] = Q; otherwise sparse[g][g] is false and, for each hh, with R=Q[hk2…min⁡(∣Q∣,(h+1)k2)−1]R = Q[h k_2 \ldots \min(|Q|, (h+1)k_2) - 1]: S[g][h]=R[0]−Q[0]S[g][h] = R[0] - Q[0], and E2[g][h]E_2[g][h] is the list (r−Q[0])r∈R(r - Q[0])_{r \in R} if max⁡R−min⁡R≥T2\max R - \min R \ge T_2, and empty otherwise.

Proof. The loop of line L1 runs once for each gg with 0≤g<⌈m/k1⌉0 \le g < \lceil m/k_1 \rceil, so gk1<mg k_1 < m and QQ is not empty. Lines L2 to L4 and L6 set the fields of group gg as stated, since PP is increasing and so Q[0]=min⁡QQ[0] = \min Q and Q[last]=max⁡QQ[\text{last}] = \max Q. Likewise, for a dense group, the loop of line L7 runs once for each hh with hk2<∣Q∣h k_2 < |Q|, so RR is not empty, and lines L8 to L11 set S[g][h]S[g][h] and E2[g][h]E_2[g][h] as stated. No step writes the fields of another group. □\square

Lemma 2 (SCAN). If p≤P[a]p \le P[a] and no 1 lies in positions p,…,P[a]−1p, \ldots, P[a] - 1, then SCAN(p,u)(p, u) returns P[a+u−1]P[a + u - 1] for each u≥1u \ge 1 with a+u−1<ma + u - 1 < m.

Proof. The 1s at positions ≥p\ge p are, in order, P[a],P[a+1],…P[a], P[a+1], \ldots, so the uu-th of them is P[a+u−1]P[a + u - 1]. SCAN masks the bits below pp in the word of pp, so the words it reads contain exactly the 1s at positions ≥p\ge p, in increasing order. The loop subtracts the full counts of words that hold fewer than the remaining number of wanted 1s, as in SELECT1 of the bit vector; by the same invariant, it stops at the word that holds the wanted 1, and the clearing of the lowest bits finds it. The loop stops because a+u−1<ma + u - 1 < m, so the wanted 1 exists. □\square

Lemma 3 (SELECT1). For 1≤j≤m1 \le j \le m, SELECT1(j)(j) returns P[j−1]P[j - 1].

Proof. Write j−1=gk1+tj - 1 = g k_1 + t with 0≤t<k10 \le t < k_1, as line L12 does. Then P[j−1]P[j-1] is element tt of QgQ_g: the group QgQ_g starts at index gk1g k_1 of PP, and t<∣Qg∣t < |Q_g| because gk1+t=j−1<mg k_1 + t = j - 1 < m and t<k1t < k_1.

Case 1: group gg is sparse. By Lemma 1, E1[g]=QgE_1[g] = Q_g, so line L13 returns Qg[t]=P[j−1]Q_g[t] = P[j-1].

Case 2: group gg is dense. Write t=hk2+ut = h k_2 + u with 0≤u<k20 \le u < k_2, as line L14 does. Element tt of QgQ_g is element uu of the subgroup R=Rg,hR = R_{g,h}, and u<∣R∣u < |R| by the same argument as above.

  • If E2[g][h]E_2[g][h] is not empty, by Lemma 1 it holds R[u]−Qg[0]R[u] - Q_g[0] at index uu, and G[g]=Qg[0]G[g] = Q_g[0], so line L15 returns R[u]=P[j−1]R[u] = P[j-1].
  • Otherwise, line L16 calls SCAN(p,u+1)(p, u + 1) with p=G[g]+S[g][h]=Qg[0]+(R[0]−Qg[0])=R[0]p = G[g] + S[g][h] = Q_g[0] + (R[0] - Q_g[0]) = R[0]. R[0]R[0] is P[a]P[a] for a=gk1+hk2a = g k_1 + h k_2, and p=P[a]p = P[a], so the hypothesis of Lemma 2 holds. SCAN returns P[a+u]=R[u]=P[j−1]P[a + u] = R[u] = P[j - 1]. □\square

Lemma 4 (SELECT0). The same argument, applied to the list of the positions of the 0s and to the masked complement of the words, shows that SELECT0(j)(j) returns the position of the jj-th 0. The mask of the last word removes the padding bits, which the complement would turn into false 0s.

Lemma 5 (RANK1). For 0≤i≤n0 \le i \le n, RANK1(i)(i) returns rank1(i)\text{rank}_1(i).

Proof. With k=⌊i/w⌋k = \lfloor i/w \rfloor and q=⌊k/s⌋q = \lfloor k/s \rfloor, the build stores RS[q]=rank1(qsw)RS[q] = \text{rank}_1(q s w) and RB[k]=rank1(kw)−RS[q]RB[k] = \text{rank}_1(k w) - RS[q]. The term POPCOUNT(W[k]∧(2i mod w−1))(W[k] \wedge (2^{i \bmod w} - 1)) counts the 1s at positions kwkw to i−1i - 1. The sum is rank1(i)\text{rank}_1(i), as in the proof for Jacobson's rank structure. The build stores one word past the last full word, so W[k]W[k] exists for i=ni = n. ■\blacksquare

The time bound. For the default parameters a dense subgroup has span less than T2T_2, and its (u+1)(u+1)-th 1 lies in [R[0],R[last]][R[0], R[\text{last}]], so SCAN reads at most ⌈T2/w⌉+1\lceil T_2 / w \rceil + 1 words. With T2=⌈lg⁡ℓ⌉4T_2 = \lceil \lg \ell \rceil^4 and w≥ℓ−1w \ge \ell - 1, this is O(1)O(1) words for nn large enough, and each other line does O(1)O(1) work.

Back to Rank/select dictionary