Can Every Subset Be Visited by Changing One Bit?
Suppose a system has three optional features. A configuration is a subset of these features, or equivalently a 3-bit binary word: a $1$ means a feature is enabled, and a $0$ means it is disabled. There are $2^3 = 8$ total configurations.
Ordinary binary counting visits all eight configurations in numerical sequence ($000 \to 001 \to 010 \to 011 \to \dots$), but the transition from $011$ to $100$ flips all three bits simultaneously. In physical hardware, sensors, and state machines, multi-bit transitions cause glitching, power spikes, and timing race conditions. Can we visit every configuration exactly once while changing only one feature at a time?
The Power of Gray Codes
A Gray code is a minimal-change ordering of binary words where every consecutive pair differs in exactly one bit position. Gray codes allow testing frameworks to update features incrementally, rotary encoders to read positions without digital noise, and algorithms to update combinatorial state in $O(1)$ time per step.
This chapter explores algorithmic generation across four foundational tasks:
3-Bit Reflected Gray Code Walk
Using three features $A, B, C$ represented by the left, middle, and right bits respectively:
| Step | Binary Word | Enabled Features Subset | Feature Changed From Previous Step |
|---|---|---|---|
| 0 | 000 | $\varnothing$ | Start |
| 1 | 001 | $\{C\}$ | Feature C |
| 2 | 011 | $\{B, C\}$ | Feature B |
| 3 | 010 | $\{B\}$ | Feature C |
| 4 | 110 | $\{A, B\}$ | Feature A |
| 5 | 111 | $\{A, B, C\}$ | Feature C |
| 6 | 101 | $\{A, C\}$ | Feature B |
| 7 | 100 | $\{A\}$ | Feature C |
Notice that Step 7 (100) and Step 0 (000) also differ in exactly one bit ($A$), forming a complete Hamiltonian cycle on the 3D hypercube!
5.1 Generating Permutations and Inversions
A permutation of $n$ distinct symbols is an ordered arrangement of those symbols. For $\{1, 2, 3\}$, the six permutations listed in standard dictionary (lexicographic) order are:
Lexicographic order compares two sequences at their first differing position from left to right. Whichever sequence possesses the smaller symbol at that position appears first.
Narayana Pandita's Next-Permutation Algorithm
Often attributed to the 14th-century Indian mathematician Pandita Narayana, this algorithm finds the immediate lexicographic successor of a sequence of distinct ordered elements in $O(n)$ time. The steps below use 1-based positions $a_1,\dots,a_n$; the equivalent 0-based array notation is $A[0],\dots,A[n-1]$.
- Find Pivot: Locate the rightmost position $i$ such that $a_i < a_{i+1}$. (If no such $i$ exists, the entire sequence is decreasing, and generation is complete).
- Find Swap Target: In the strictly decreasing suffix to the right of position $i$, locate the rightmost position $j$ where $a_j > a_i$.
- Swap: Swap elements $a_i$ and $a_j$.
- Reverse Suffix: Reverse the entire suffix starting at position $i+1$ to make it strictly increasing (yielding the smallest possible suffix).
Worked example (0-based indexing). Starting with $A=[1,3,5,4,2]$, scan from the right to find the last ascent: $A[1]=3 < A[2]=5$, so the pivot is $i=1$. The suffix $[5,4,2]$ is decreasing. Scanning it from the right, the first value greater than $3$ is $A[3]=4$, so $j=3$. Swap the pivot and target to get $[1,4,5,3,2]$, then reverse the suffix after index $1$, $[5,3,2]$, to obtain the successor $[1,4,2,3,5]$.
Why is this the exact immediate successor? The suffix following the rightmost pivot $i$ is strictly decreasing, making it the maximum possible arrangement of those specific symbols. Any larger permutation must alter the pivot or an earlier position. Replacing $a_i$ with the smallest strictly larger symbol in the suffix makes the increase minimal. Reversing the suffix afterwards turns it from strictly decreasing to strictly increasing, producing the smallest possible prefix extension. In inversion terms, this changes the suffix from having all $\binom{m}{2}$ possible internal inversions (where $m$ is its length) to having none; this local change helps minimize the successor's remaining suffix.
Last permutation. If no pivot exists, the whole sequence is decreasing, which is the final permutation in lexicographic order. For example, reversing $[5,4,3,2,1]$ returns the first permutation $[1,2,3,4,5]$ when generation is meant to cycle.
Inversions and Minimum Adjacent Swaps
An inversion of a permutation $\pi = (\pi_1, \pi_2, \dots, \pi_n)$ is an out-of-order pair of positions $(i, j)$ where $i < j$ but $\pi_i > \pi_j$. The total number of inversions is denoted $\operatorname{inv}(\pi)$.
In simple terms: an inversion is a pair of values that appears in the wrong order: the larger value comes first. In an increasing list such as $(1,2,3,4)$, there are no inversions. To check a pair, look at their order in the permutation: if $x$ appears before $y$ and $x>y$, that pair is an inversion.
Concrete example. For $\pi=(3,1,4,2)$, compare each pair from left to right:
- $3$ appears before $1$, and $3>1$: inversion $(3,1)$.
- $3$ appears before $4$, but $3<4$: not an inversion.
- $3$ appears before $2$, and $3>2$: inversion $(3,2)$.
- $1$ appears before $4$ and before $2$, and is smaller than both: neither pair is an inversion.
- $4$ appears before $2$, and $4>2$: inversion $(4,2)$.
Thus the inversion pairs are $(3,1)$, $(3,2)$, and $(4,2)$, so $\operatorname{inv}(\pi)=3$.
A direct inversion counter checks all $\binom{n}{2}$ position pairs in $O(n^2)$ time. The count ranges from $0$ for the increasing permutation to $\binom{n}{2}=\frac{n(n-1)}{2}$ for the decreasing permutation: there are $\binom{n}{2}$ pairs in total, and in descending order every pair is inverted. Swapping an adjacent inverted pair reduces $\operatorname{inv}(\pi)$ by exactly one without affecting any other pairwise relationships. Repeating such swaps sorts the sequence, so the inversion count is exactly the minimum number of adjacent swaps needed to sort it; Bubble Sort, for example, performs that many swaps when it swaps adjacent out-of-order elements until sorted.
Inversion count and lexicographic order describe different things. For $n=3$, the lexicographic list has inversion counts $0,1,1,2,2,3$ for $(1,2,3),(1,3,2),(2,1,3),(2,3,1),(3,1,2),(3,2,1)$. The endpoints are always the increasing permutation with zero inversions and the decreasing permutation with the maximum, but the count does not generally increase at every lexicographic step: for $n=4$, $(1,4,3,2)$ has $3$ inversions and its lexicographic successor $(2,1,3,4)$ has only $1$.
Connection to the immediate successor. Narayana's next-permutation algorithm (described above) finds the rightmost ascent, swaps its pivot with the smallest larger value to its right, then reverses the suffix. That suffix is initially decreasing, so a suffix of length $m$ has all $\binom{m}{2}$ possible internal inversions; after reversal it is increasing and has none. For the earlier example, the suffix $[5,3,2]$ after the swap has $3$ internal inversions, while $[2,3,5]$ after reversal has $0$. This reversal makes the tail as small as possible after the pivot is increased, which is why the result is the immediate lexicographic successor. It does not mean inversion counts always rise as we advance lexicographically—the $n=4$ counterexample above shows they can fall.
Inversion Count and Successor Practice
Problem: Find the inversion count of $\pi = (2, 4, 1, 3)$ and locate its immediate next lexicographic permutation.
- Inversions: Pairwise comparisons where $i < j$ but $\pi_i > \pi_j$:
- Element $2$: precedes $1 \implies \mathbf{1 \text{ inversion }} (2,1)$.
- Element $4$: precedes $1$ and $3 \implies \mathbf{2 \text{ inversions }} (4,1), (4,3)$.
- Element $1$: precedes $3 \implies \mathbf{0 \text{ inversions}}$.
- Total Inversions: $1 + 2 + 0 = \mathbf{3}$.
- Next Permutation:
- The rightmost ascent is $1 < 3$ at positions $3$ and $4$ (1-based), so the pivot is $1$.
- The rightmost value greater than the pivot is $3$.
- Swap $1$ and $3$ to obtain $(2, 4, 3, 1)$; the suffix after the pivot has one element, so reversing it leaves the sequence unchanged.
- Successor: $\mathbf{(2, 4, 3, 1)}$.
5.2 Generating combinations and subsets
Represent a $k$-element subset of $\{0,1,\ldots,n-1\}$ by its increasing tuple
$$ 0\le c_1\lt c_2\lt\cdots\lt c_k\le n-1. $$
Increasing tuples remove duplicates caused by different orders of the same selected elements. For $1\le k\le n$, the first tuple is $(0,1,\ldots,k-1)$ and the last is $(n-k,n-k+1,\ldots,n-1)$.
To find the next tuple in lexicographic order, scan from right to left for the largest index $i$ satisfying
$$ c_i\lt n-k+i-1. $$
Increment $c_i$ by one. Then reset every later coordinate to the smallest possible value: $c_{i+1}=c_i+1$, $c_{i+2}=c_i+2$, and so on. If no coordinate can increase, stop.
The upper bound on $c_i$ reserves enough larger elements for the remaining $k-i$ positions. Choosing the rightmost movable coordinate keeps the longest possible prefix unchanged. Resetting the suffix to its minimum produces the immediate successor, so the generator is complete and has no duplicates.
For $k=0$, there is one combination, the empty tuple. If $k\gt n$ or $k\lt0$, there are no combinations. These cases should be handled before attempting to scan tuple positions.
All subsets. A subset can also be represented by an $n$-bit membership word. Enumerating every binary word lists all $2^n$ subsets. Alternatively, generate combinations separately for sizes $k=0,1,\ldots,n$; these classes are disjoint and contain $\sum_k\binom nk=2^n$ objects altogether.
Generating combinations and subsets: a worked example
Problem. Generate all two-element subsets of $\{0,1,2,3\}$ in lexicographic order.
| Current tuple | Successor action | Next tuple |
|---|---|---|
| $(0,1)$ | Increment the last coordinate | $(0,2)$ |
| $(0,2)$ | Increment the last coordinate | $(0,3)$ |
| $(0,3)$ | Increment the first coordinate; reset the suffix | $(1,2)$ |
| $(1,2)$ | Increment the last coordinate | $(1,3)$ |
| $(1,3)$ | Increment the first coordinate; reset the suffix | $(2,3)$ |
| $(2,3)$ | No coordinate can increase | Stop |
Every tuple is increasing, so it represents a legal subset exactly once. Every two-element subset occurs because the listed sequence takes immediate successors from the smallest tuple to the largest. There are six outputs, agreeing with $\binom42=6$.
The transition $(0,3)\to(1,2)$ changes both selected elements. Lexicographic combination order is therefore not automatically a minimal-change order.
Exercise. Find the successor of $(0,2,4)$ among three-element subsets of $\{0,1,2,3,4\}$. Hint: The last coordinate is already at its maximum. Answer: $(0,3,4)$.
Scanning and resetting the tuple takes $O(k)$ time in the worst case per successor, with $O(1)$ auxiliary space beyond the tuple. Copying an output requires $O(k)$ time. These bounds describe this straightforward generator; they do not assert that every transition needs to change all coordinates.
5.3 Lexicographic and Gray-code Orderings
For fixed-length binary words of length $n$, lexicographic order (using $0 < 1$) matches ordinary binary numerical order. For length $n = 3$, counting in lexicographic order yields: $000, 001, 010, 011, 100, 101, 110, 111$. However, a Gray code ordering visits the exact same set of $2^n$ binary words in a sequence where every adjacent pair differs by only a single bit.
The total number of differing bit positions between two binary words $a$ and $b$ is called their Hamming distance, denoted $H(a, b)$. While lexicographic order suffers from jumps with Hamming distances up to $n$ (such as $011 \to 100$ where $H(011, 100) = 3$), a Gray code guarantees $H(g_i, g_{i+1}) = 1$ for all steps $i$.
Recursive Reflected Construction ($G_n$)
Let $G_0 = (\varepsilon)$ contain only the empty word. To construct the Gray code sequence $G_n$ of length $n$ from $G_{n-1}$:
- Take the sequence $G_{n-1}$ and prefix a
0to every word in its original forward order. - Take the sequence $G_{n-1}$ in reversed order ($G_{n-1}^R$) and prefix a
1to every word. - Concatenate the two halves to form $G_n$.
Start with $G_0=(\varepsilon)$, where $\varepsilon$ is the empty word. Prefixing $0$ and $1$ gives $G_1=(0,1)$. Reflecting $G_1$ gives $G_1^R=(1,0)$, so the two prefixed halves are $(00,01)$ and $(11,10)$; hence $G_2=(00,01,11,10)$.
Building $G_3$ from $G_2$. Keep $G_2=(00,01,11,10)$ in its original order for the first half and prefix each word with $0$: $(000,001,011,010)$. Reverse $G_2$ to get $(10,11,01,00)$, prefix each word with $1$ for the reflected half $(110,111,101,100)$, then concatenate:
For instance, the middle transition is $010\to110$: the two words share the same tail $10$, so only the new leading bit changes.
Why does reflection guarantee Hamming distance $1$? The construction preserves the one-bit-change property inside each half and makes the join between the halves differ in only the leading bit:
- Base case: $G_1=(0,1)$ contains both one-bit words, and the only transition changes one bit.
- Completeness and uniqueness: The first half contains every $(n-1)$-bit word prefixed by $0$ exactly once. The second contains every such word prefixed by $1$ exactly once. The halves cannot overlap because their first bits differ, so together they contain all $2^n$ words.
- Inside either half: The prefixed bit is constant, so it contributes no difference between neighboring words. The remaining bits follow $G_{n-1}$ or the same sequence in reverse; reversing order does not change the Hamming distance of neighboring pairs.
- At the join: The last word of the first half is $0+\operatorname{last}(G_{n-1})$. Since the second half uses the reversed list, its first word is $1+\operatorname{last}(G_{n-1})$. Their tails match exactly, so only the leading bit changes.
- Wrap-around (cyclic version): The first word is $0+\operatorname{first}(G_{n-1})=0^n$. The last word is $1+\operatorname{first}(G_{n-1})=10^{n-1}$, because reflection puts the original first word last. These differ only in the leading bit, so the last and first words also have Hamming distance $1$.
Direct Closed-Form Computation: $g(i) = i \oplus \lfloor i / 2 \rfloor$
Instead of recursively constructing previous lists, we can compute the Gray code word at step $i$ ($0 \le i < 2^n$) directly in $O(1)$ time using bitwise operations:
where $\oplus$ represents the bitwise exclusive OR (XOR) operation, which outputs 1 wherever two input bits differ and 0 where they match. Dividing by 2 is equivalent to a logical right shift by 1 bit.
Invertibility (Unranking Gray Codes): To recover index $i$ from a Gray code word $g$, start with the most significant bit (MSB) equal to $g$, and compute each subsequent bit as $i_k = i_{k+1} \mathbin{\oplus} g_k$. Because this mapping is bijective, all $2^n$ outputs are distinct.
Algebraic Proof of 1-Bit Adjacency: Suppose integer $i$ has $t$ trailing ones in binary. Incrementing $i$ to $i+1$ flips those $t$ trailing ones and the first preceding zero bit, giving $i \mathbin{\oplus} (i+1) = 2^{t+1} - 1$. Taking XOR with right shifts gives:
Since $2^t$ contains exactly one set bit at position $t$, $g(i)$ and $g(i+1)$ differ in exactly one bit position!
Language-Independent Generator Pseudocode: Validate that $n \ge 0$. For each integer $i$ from $0$ through $2^n-1$, choose $i$ itself for lexicographic order or $i \mathbin{\oplus} \lfloor i/2 \rfloor$ for Gray order. Format the integer as an $n$-digit binary string with leading zeros and emit it.
Complexity & Storage Analysis: Because emitting full string representations takes $O(n)$ time per word, generating all $2^n$ binary words takes $O(n 2^n)$ total time and $O(n)$ auxiliary working space. Converting the generator to an explicit array costs $O(n 2^n)$ memory.
Important Limitation: Fixed-Size Subsets
Reflected Gray codes cover all subsets across varying sizes. However, two distinct subsets of the same fixed size $k$ cannot differ by only one bit (since changing 1 bit adds or removes an element, altering $k$). A minimal-change ordering for fixed-size combinations must instead perform an element swap, changing at least 2 bits (e.g., Chase's sequence or Revolving Door algorithm).
Comparing Transition Costs ($n = 3$)
Question: For length $n = 3$, compare the transition from Step 3 to Step 4 in Lexicographic order versus Gray code order.
- Lexicographic Order (Step 3 ➔ Step 4): $011 \to 100$.
Bits changed: Left bit ($0 \to 1$), middle bit ($1 \to 0$), right bit ($1 \to 0$).
Hamming Distance = 3 bit flips. - Gray Code Order (Step 3 ➔ Step 4): $010 \to 110$.
Bits changed: Left bit ($0 \to 1$) only.
Hamming Distance = 1 bit flip.
5.4 Backtracking and Recursive Generation
Backtracking is a systematic strategy for searching through a space of possibilities by constructing candidate objects one choice at a time. Think of it as exploring a maze: you follow a path as far as possible, and whenever you reach a dead end or a complete solution, you backtrack—stepping back to the most recent decision point to try a different path.
In a backtracking algorithm, the space of all partial solutions forms a search tree (or state-space tree):
- The root represents the initial empty state.
- An internal node represents a valid partial construction (a prefix).
- A leaf node represents a fully constructed complete object (or a dead end).
- Edges represent making a single valid decision to extend the current state.
The Backtracking Core Loop
Every recursive backtracking procedure maintains a mutable state (like a choice prefix) and follows three universal steps at each search tree node:
- Choose: Select a candidate symbol from the remaining available choices and append it to the current prefix.
- Explore: Recurse deeper into the search tree with the updated prefix. If the prefix is complete, emit it as an output object.
- Un-choose (Backtrack): Remove the candidate symbol from the prefix, restoring the exact state so the next candidate choice can be attempted.
Generating Permutations via Search Trees
To generate all $n!$ permutations of $\{1, 2, \dots, n\}$, we maintain a growing prefix list and a set of unused symbols:
- At the root node, the prefix is empty
(), and all $n$ symbols are unused. - At depth $k$, we try appending each remaining unused symbol in turn.
- When the unused set becomes empty (depth $n$), the current prefix is a valid leaf permutation and is emitted.
- When trying choices at each node in increasing numeric order, the leaves are emitted in exact lexicographic order!
Correctness Invariant: The prefix contains distinct elements, and the unused set contains exactly the symbols not in the prefix. Each permutation corresponds to a unique root-to-leaf path. Because every unused symbol is systematically attempted at each node, no valid permutation is skipped, and no permutation is generated twice.
Pruning Search Trees: Constrained Backtracking
Backtracking becomes exceptionally powerful when combined with pruning. Instead of generating a full candidate object and checking its validity at the end, pruning checks constraint feasibility at every internal node. If a partial prefix violates a rule, the algorithm prunes (cuts off) that entire sub-tree immediately!
Consider generating binary words of length $n = 3$ with no adjacent ones (no 11 allowed):
- At any node, we can always safely append
0. - We can append
1only if the current prefix is empty or its last digit is0. - If a prefix ends in
1, attempting to append1creates the illegal substring11. We reject this branch immediately without exploring its children!
11 or 011) immediately, avoiding dead-end recursive subtree expansions.Complexity & Performance Analysis:
- For the "no adjacent ones" problem, the number of valid prefixes at successive lengths follows the Fibonacci recurrence $F_{n+2}$ from Chapter 4.
- Because invalid branches are pruned in $O(1)$ time upon creation, the total number of visited internal tree nodes is strictly within a constant factor of the number of valid emitted outputs $M$.
- Thus, internal traversal takes $O(M)$ operations for $M$ valid outputs (Constant Amortized Time per object).
- Working memory is limited to the single mutable prefix stack and recursion call stack of depth $O(n)$.
Constrained Backtracking with Target Ones
Problem: List all binary words of length $n = 4$ containing exactly two nonadjacent ones using backtracking with pruning.
Pruning Invariants: For a prefix with $a$ ones and $s$ remaining unfilled positions, prune the branch if:
- $a > 2$ (Too many ones chosen).
- $a + s < 2$ (Not enough remaining slots to reach two ones).
- The last symbol chosen was
1and we attempt to append1(Adjacent ones constraint).
| Valid Path | Emitted Word | Status | Reason |
|---|---|---|---|
0 ➔ 1 ➔ 0 ➔ 1 |
0101 |
Valid Leaf | 2 nonadjacent ones ✓ |
1 ➔ 0 ➔ 0 ➔ 1 |
1001 |
Valid Leaf | 2 nonadjacent ones ✓ |
1 ➔ 0 ➔ 1 ➔ 0 |
1010 |
Valid Leaf | 2 nonadjacent ones ✓ |
0 ➔ 0 ➔ 0 ➔ ... |
0000 |
Pruned | Cannot reach 2 ones ($a+s = 0+1 < 2$) |
1 ➔ 1 ➔ ... |
11.. |
Pruned | Forbidden adjacent ones (11) |
There are exactly 3 valid words: 0101, 1001, and 1010.
This agrees with the combinatorial choice $\binom{4 - 2 + 1}{2} = \binom{3}{2} = \mathbf{3}$!
5.5 Ranking and Unranking Combinatorial Objects
When working with a collection of $M$ combinatorial objects (such as all $n!$ permutations or all $\binom{n}{k}$ subsets), it is often necessary to map each object to a unique integer index, and vice versa. This bridge between abstract structures and simple integers is defined by two complementary operations:
- Ranking: Converts a specific combinatorial object into its unique zero-based position index $R \in \{0, 1, \ldots, M-1\}$ within a specified ordering.
- Unranking: Reconstructs the exact combinatorial object corresponding to a given integer index $R \in \{0, 1, \ldots, M-1\}$.
Order Defines the Rank
Rank is not an intrinsic property of an object—it depends strictly on the chosen sequence order! For binary words of length 3, the word 101 has rank 5 in standard lexicographic order ($000 \to 001 \to 010 \to 011 \to 100 \to 101$), but rank 6 in Reflected Gray Code order ($000 \to 001 \to 011 \to 010 \to 110 \to 111 \to 101$).
Permutation Ranking and the Lehmer Code
Fix the symbol set $\{0, 1, \ldots, n-1\}$ in standard lexicographic order. At each position $i$ (from left to right) of a permutation $\pi$, let $d_i$ denote the number of currently unused symbols that are strictly smaller than the symbol chosen at position $i$. Equivalently, $d_i$ is the number of smaller elements located to the right of position $i$ in the permutation.
The sequence $(d_1, d_2, \ldots, d_n)$ is called the Lehmer code of the permutation. Each digit $d_i$ satisfies $0 \le d_i \le n-i$. Furthermore, the sum of these digits equals the total inversion count of the permutation: $\operatorname{inv}(\pi) = \sum_{i=1}^n d_i$.
Using the Lehmer code, the exact lexicographic rank of permutation $\pi$ is given by:
Why does this formula work? At position $i$, choosing a symbol that has $d_i$ smaller available candidates skips exactly $d_i$ blocks of permutations, where each block contains $(n-i)!$ permutations sharing the same prefix. Summing all skipped blocks yields the exact number of permutations preceding $\pi$.
The Factorial Unranking Procedure
To reverse the process and reconstruct the permutation at rank $R$ ($0 \le R < n!$), we maintain a sorted list of remaining unused symbols. At position $i$, we divide the current residual rank by $(n-i)!$:
- The quotient gives the zero-based index of the symbol to select from the unused list.
- The remainder becomes the new residual rank for the next position.
Permutation Rank Round-Trip ($n = 3$)
Problem: Rank $\pi = (1, 2, 0)$ and unrank $R = 3$ for $n = 3$.
- Ranking $(1, 2, 0)$:
- Position 1: Symbol
1. Unused $\{0, 1, 2\}$. Smaller unused $\{0\} \implies d_1 = 1$. - Position 2: Symbol
2. Unused $\{0, 2\}$. Smaller unused $\{0\} \implies d_2 = 1$. - Position 3: Symbol
0. Unused $\{0\}$. Smaller unused $\{\} \implies d_3 = 0$. - Rank: $1 \cdot 2! + 1 \cdot 1! + 0 \cdot 0! = 2 + 1 + 0 = \mathbf{3}$.
- Position 1: Symbol
- Unranking $R = 3$:
- $3 \div 2! = \text{quotient } \mathbf{1}$, rem $1$. From $\{0, 1, 2\}$, pick index 1 $\implies \mathbf{1}$.
- $1 \div 1! = \text{quotient } \mathbf{1}$, rem $0$. From $\{0, 2\}$, pick index 1 $\implies \mathbf{2}$.
- $0 \div 0! = \text{quotient } \mathbf{0}$, rem $0$. From $\{0\}$, pick index 0 $\implies \mathbf{0}$.
- Unranked Permutation: $\mathbf{(1, 2, 0)}$.
5.6 Combinatorial Number Systems
For fixed-size combinations ($k$-subsets of $\{0, 1, \dots, n-1\}$), ranking relies on binomial coefficients using colexicographic order (colex), rather than standard lexicographic order.
In colex order, two distinct $k$-subsets are compared by inspecting their largest differing element. Whichever subset contains the larger differing element comes later in colex order.
The Combinatorial Number System Theorem
Represent any $k$-subset as an increasing sorted sequence $a_1 < a_2 < \dots < a_k$. Its colex rank is given by the exact binomial sum:
Convention: $\binom{a}{j} = 0$ whenever $a < j$. This guarantees that the first subset $\{0, 1, \dots, k-1\}$ evaluates to rank $0$.
Greedy Unranking Algorithm: To find the $k$-subset matching rank $R$, search backwards from $j = k$ down to $1$. At each position $j$, find the largest integer $a_j$ such that $\binom{a_j}{j} \le R$. Subtract $\binom{a_j}{j}$ from $R$, and repeat for $j-1$ with upper bound $a_j-1$.
| Colex Rank ($R$) | 3-Subset $\{a_1, a_2, a_3\}$ | Binomial Expansion $\binom{a_1}{1} + \binom{a_2}{2} + \binom{a_3}{3}$ |
|---|---|---|
| 0 | $\{0, 1, 2\}$ | $\binom{0}{1} + \binom{1}{2} + \binom{2}{3} = 0 + 0 + 0 = 0$ |
| 1 | $\{0, 1, 3\}$ | $\binom{0}{1} + \binom{1}{2} + \binom{3}{3} = 0 + 0 + 1 = 1$ |
| 2 | $\{0, 2, 3\}$ | $\binom{0}{1} + \binom{2}{2} + \binom{3}{3} = 0 + 1 + 1 = 2$ |
| 3 | $\{1, 2, 3\}$ | $\binom{1}{1} + \binom{2}{2} + \binom{3}{3} = 1 + 1 + 1 = 3$ |
| 4 | {0, 1, 4} | $\binom{0}{1} + \binom{1}{2} + \binom{4}{3} = 0 + 0 + 4 = 4$ |
| 5 | {0, 2, 4} | $\binom{0}{1} + \binom{2}{2} + \binom{4}{3} = 0 + 1 + 4 = 5$ |
| 6 | $\{1, 2, 4\}$ | $\binom{1}{1} + \binom{2}{2} + \binom{4}{3} = 1 + 1 + 4 = 6$ |
| 7 | $\{0, 3, 4\}$ | $\binom{0}{1} + \binom{3}{2} + \binom{4}{3} = 0 + 3 + 4 = 7$ |
| 8 | $\{1, 3, 4\}$ | $\binom{1}{1} + \binom{3}{2} + \binom{4}{3} = 1 + 3 + 4 = 8$ |
| 9 | $\{2, 3, 4\}$ | $\binom{2}{1} + \binom{3}{2} + \binom{4}{3} = 2 + 3 + 4 = 9$ |
5.7 Constant-Amortized-Time (CAT) Generation
In combinatorial generation, an algorithm is said to run in Constant Amortized Time (CAT) per object if its total internal operations to generate $M$ objects is bounded by $O(M)$. This implies that the average work per generated object is $O(1)$, even if occasional individual transition steps take significantly longer.
Analogy: The Amortization Savings Bank
Think of amortized analysis like paying for maintenance: on 7 days, you perform a lightweight step that costs 1 unit each. On the 8th day, you do a major overhaul that costs 3 units. Across all 8 days, the total is 10 units, averaging 1.25 units per day. The quick, inexpensive steps "bank" savings that offset the rare expensive step.
Consider an $n$-bit binary counter visiting all $2^n$ configurations from $000 \dots 0$ to $111 \dots 1$. To increment the counter, we scan from the rightmost bit (LSB), turning trailing 1s into 0s, until we flip the first 0 to 1.
Proof of Constant Amortized Complexity: Numbering bits from right to left as $j = 1, 2, \dots, n$, bit position $j$ flips exactly $2^{n-j+1} - 1$ times across the full counting sequence. Summing across all $n$ positions:
Because the total work across all $2^n$ outputs is strictly less than $2 \cdot 2^n$, the average internal work per state transition is less than 2 bit flips—proving $O(1)$ constant amortized time!
5.8 Uniform Random Sampling of Finite Structures
Uniform sampling selects an object from a finite set of size $M$ such that every object has an identical probability $p = 1/M$ of being chosen.
1. Sampling via Unranking: Draw a random integer $R$ uniformly from $\{0, 1, \dots, M-1\}$, then return $\operatorname{unrank}(R)$. Because unranking is a bijection, every object corresponds to exactly one rank, guaranteeing exact uniform probability $1/M$ in $O(1)$ sampling time without generating preceding objects!
2. Exact Rejection Sampling (Avoiding Modulo Bias): Naively computing random_int() % M introduces probability bias if the random generator range is not a perfect multiple of $M$. To sample uniformly without bias, select the smallest power of two $2^b \ge M$, generate a $b$-bit integer, and reject any value $\ge M$, repeating until a valid integer $< M$ is accepted.
Direct Permutation Sampling: The Fisher–Yates Shuffle
Rather than converting huge ranks, the Fisher–Yates shuffle generates a uniformly random permutation in $O(n)$ time using in-place swaps:
- Start with an array initialized to sorted order $(0, 1, \dots, n-1)$.
- For index $i$ running from $n-1$ down to $1$:
- Pick a uniform random integer $j$ from $\{0, 1, \dots, i\}$.
- Swap elements at position $i$ and position $j$.
Proof of Uniformity: At step $i$, position $i$ receives a uniformly chosen element from the $i+1$ remaining candidates with probability $1/(i+1)$. Multiplying probabilities across all steps gives:
Uniform Statistic Distribution Practice
Question: In a uniform permutation of 3 symbols, is the inversion count distribution uniform across $\{0, 1, 2, 3\}$?
- Total Permutations ($3! = 6$):
- $\operatorname{inv}(0, 1, 2) = \mathbf{0}$ (1 permutation) $\implies P(0) = \mathbf{1/6}$.
- $\operatorname{inv}(0, 2, 1) = \mathbf{1}$, $\operatorname{inv}(1, 0, 2) = \mathbf{1}$ (2 permutations) $\implies P(1) = \mathbf{2/6}$.
- $\operatorname{inv}(1, 2, 0) = \mathbf{2}$, $\operatorname{inv}(2, 0, 1) = \mathbf{2}$ (2 permutations) $\implies P(2) = \mathbf{2/6}$.
- $\operatorname{inv}(2, 1, 0) = \mathbf{3}$ (1 permutation) $\implies P(3) = \mathbf{1/6}$.
- Answer: No! The probabilities are $(1/6, 2/6, 2/6, 1/6)$. Sampling permutations uniformly does not imply a uniform distribution on their derived statistics.
Build and verify a rank–unrank round trip
Generation provides an independent list against which a ranking system can be checked. Ranking supplies an exact index, and unranking supplies direct access to an object. Once the two maps are proved inverse, a uniform random index becomes a uniform random object.
Permutation inversion counts add another useful consistency check. The Lehmer digits used to compute a rank must sum to the inversion count obtained by testing position pairs. This connects permutation generation, ranking, and sampling without assuming that inversion counts themselves are uniformly distributed.
One application is testing a scheduler on all small orderings of distinct tasks, then sampling larger orderings when exhaustive testing becomes impractical. A stored rank identifies the chosen ordering exactly under a fixed symbol order and ranking convention. Uniform sampling covers permutations fairly, but it does not guarantee equal coverage of properties such as inversion count, execution time, or resource demand.
Build and verify a rank–unrank round trip
Inputs and conventions. Use permutations of $\{0,1,2,3\}$ in lexicographic order, and three-element subsets of $\{0,1,2,3,4\}$ in colex order. All ranks are zero-based. Use exact integers and validate input ranges.
Task 1: warm-up calculations. Generate the six permutations of $\{0,1,2\}$ in lexicographic order. Compute their Lehmer codes, ranks, and inversion counts. Separately, reproduce the ten-entry colex table in Section 5.6.
Solution check: The permutation ranks are $0,1,2,3,4,5$, and the corresponding inversion counts are $0,1,1,2,2,3$. The colex ranks are $0$ through $9$ in the displayed subset order.
Task 2: implement both directions. Implement factorial ranking and unranking for permutations. Implement binomial-sum ranking and greedy unranking for combinations. Use the procedures in Sections 5.5 and 5.6, including explicit handling of empty objects.
Worked checks: Permutation $(2,0,3,1)$ has rank $13$ and three inversions. Subset $\{0,2,4\}$ has colex rank $5$. Unranking those integers must recover those objects.
Task 3: exhaustive verification. Generate all $24$ four-symbol permutations independently using next-permutation or backtracking. For each emitted permutation, check that its rank equals its position in the generated lexicographic list. Check both round trips for every permutation and every integer rank from zero through $23$. Compare each Lehmer digit sum with a direct inversion counter.
Generate the ten three-element subsets independently. Sort them by their tuples read from largest element to smallest to obtain colex order. Check that their ranks are exactly zero through nine, in that order, and check both round trips for every subset and every rank.
Testing only a round trip is insufficient to verify a specified order: two mutually inverse maps could still implement a different order. The independently generated reference lists provide that additional check.
Task 4: prove the general round trip. For permutations, explain why each quotient chooses one factorial-sized prefix block and each remainder stays within it. For combinations, establish the residual bound after each greedy binomial choice using Pascal’s identity. Finish both arguments with their empty-object base cases.
Hints: Induct on the number of positions still to fill. State both inverse identities, rather than proving only that unranking produces a legal object. For a combination step, the residual must be smaller than $\binom{a_j}{j-1}$ after selecting $a_j$.
Task 5: connect to sampling. Draw a uniform integer from zero through $23$ and unrank it as a permutation. Explain why each permutation has probability $1/24$. Collect a frequency table if desired, but distinguish a statistical check from the bijection proof: observed frequencies need not be exactly equal in a finite experiment.
Boundary checks. Test the empty permutation and the empty subset, each with rank zero; the first and last ranks; a repeated symbol in a purported permutation; and an out-of-range rank. Reject invalid inputs rather than silently reducing ranks modulo the family size.
Optional extension. Use completion counts to rank and unrank length-five binary words with no adjacent ones in lexicographic order. At each position, the block beginning with $0$ has as many words as its valid completions. Ranking skips that block when the actual next symbol is $1$; unranking enters it when the residual rank is smaller than its size. There are $13$ words, so the valid ranks are zero through twelve. Prove the round trip, then obtain a uniform constrained word by choosing one of those ranks uniformly.
Deliverables. Submit the reference lists, implementations, exhaustive-check results, proofs of both inverse identities, and the sampling argument. Report the order and indexing convention beside every rank.
Assessment: 25 points. Award five points each for correct generation and ordering; permutation ranking with inversion checks; combination ranking with correct colex conventions; proofs and boundary handling; and a sampling argument that separates mathematical guarantees from experimental evidence.
Next in the Series
In Chapter 6: Binomial Coefficients and Identities, we'll explore See algebraic identities as counts of the same objects.