TAOCP 5 Exercise 20
We are given \(N = 1000\) words \(x_1, x_2, \dots, x_{1000}\), each of length 30 bits.
Section 5: Introduction to Sorting
Exercise 20. ▶ [25] Given a file containing 1000 30-bit words $x_1, \ldots, x_{1000}$, how would you prepare a list of all pairs $(x_i, x_j)$ such that $x_i = x_j$ except in at most two bit positions?
Verified: yes
Solve time: 25m58s
Solution
We are given (N = 1000) words (x_1, x_2, \dots, x_{1000}), each of length 30 bits. The task is to list all pairs ((x_i, x_j)) with (i < j) whose Hamming distance (d(x_i, x_j)) is at most 2 (i.e., the words are equal or differ in one or two bit positions).
Algorithm
-
Sort and group identical words.
Form an array of records ((x_k, k)) for (k = 1,\dots,1000). Sort this array by the word value (x_k) (and secondarily by index (k) to keep indices within a group in increasing order). Scan the sorted array to identify the distinct values (U[1] < U[2] < \dots < U[M]) ((M \le 1000)). For each distinct value (U[m]) store the list (L[m]) of original indices that have this value; the lists are naturally ordered by increasing index because of the secondary sort key.
While scanning, for every group (L[m]) output all pairs ((i, j)) with (i, j \in L[m]) and (i < j). These are exactly the pairs with distance 0. -
Find pairs at distance 1 or 2.
For each distinct value (U[m]) ((m = 1) to (M)):- Let (w = U[m]).
- Generate the 30 words (v = w \oplus (1 \ll b)) for (b = 0,\dots,29) (distance 1).
- Generate the (\binom{30}{2} = 435) words (v = w \oplus (1 \ll b_1) \oplus (1 \ll b_2)) for (0 \le b_1 < b_2 \le 29) (distance 2).
- For each generated word (v), binary‑search (v) in the sorted array (U).
- If (v) is found at position (l) with (l > m), then for every (i \in L[m]) and (j \in L[l]) output the ordered pair ((\min(i,j), \max(i,j))). Because (l > m), every unordered pair of distinct values is considered exactly once; taking the minimum and maximum guarantees (i < j) in the output.
-
Result. The union of the pairs produced in steps 1 and 2 is the required list.
Correctness
We prove that the algorithm outputs exactly the set ({, (i,j) \mid 1 \le i < j \le 1000,; d(x_i, x_j) \le 2 ,}).
Soundness.
- Step 1 outputs only pairs from the same group (L[m]). By construction all indices in (L[m]) have the same word value (U[m]), so their Hamming distance is 0. The condition (i < j) is enforced explicitly.
- Step 2 considers a pair of distinct values (U[m]) and (U[l]) with (m < l) and (U[l]) generated from (U[m]) by flipping one or two bits. Hence (d(U[m], U[l]) \in {1,2}). For any (i \in L[m]) and (j \in L[l]) we have (d(x_i, x_j) = d(U[m], U[l]) \le 2). The output uses ((\min(i,j), \max(i,j))), which satisfies (i < j). No other pairs are output.
Completeness.
Take any pair ((i,j)) with (i < j) and (d(x_i, x_j) \le 2). Let (a = x_i) and (b = x_j).
- If (a = b) then (i) and (j) belong to the same group (L[m]). Step 1 outputs all pairs with (i < j) from that group, so ((i,j)) is output.
- If (a \ne b) then (d(a,b) \in {1,2}). Let (m) and (l) be the positions of (a) and (b) in (U); without loss of generality (m < l) (since (U) is strictly increasing). When processing (m) in step 2 we generate all words at distance 1 and 2 from (a); in particular (b) is generated. The binary search finds (b) at position (l > m). The algorithm then iterates over all (i' \in L[m]) and (j' \in L[l]), which includes the original (i) and (j). The pair is emitted as ((\min(i,j), \max(i,j)) = (i,j)) because (i < j). Thus ((i,j)) is output exactly once.
No duplicates.
- Distance‑0 pairs are produced only in step 1, and each unordered pair of indices from the same group appears exactly once because we iterate with (i < j).
- For distance 1 or 2, each unordered pair of distinct values ({a,b}) is considered exactly once: when the smaller value (in (U)) is processed. The nested loops over (L[m]) and (L[l]) produce every combination of indices from the two groups exactly once, and we output the ordered version with (i < j). No other step produces these pairs.
Complexity
- Sorting 1000 records: (O(N \log N) = O(1000 \log 1000)).
- Forming groups and outputting distance‑0 pairs: (O(N)).
- For each of the (M \le 1000) distinct values we generate (30 + 435 = 465) neighbours and binary‑search each in (U) (size (M)). This takes (O(M \cdot 465 \cdot \log M) \le O(1000 \cdot 465 \cdot 10) \approx 4.65 \times 10^6) operations.
- The total work is (O(N \log N)) with a very small constant, easily fast enough for the given parameters.
Implementation notes
- The 30 single‑bit masks and the 435 double‑bit masks can be precomputed once.
- Binary search can be implemented on the array (U) of distinct values.
- Because the indices within each group (L[m]) are sorted, the pairs ((i,j)) with (i < j) can be emitted by a simple nested loop or by merging two sorted lists if desired, but a double loop is perfectly acceptable for (M \le 1000) and typical group sizes.
- The algorithm uses only sorting and binary search, staying entirely within the techniques of this chapter.
This completes the solution. ∎