Algorithm Search⌘K

Proofs: Permutation rank and unrank algorithm

These are the human proofs of Permutation rank and unrank algorithm, at full length.

RANK and UNRANK are inverse bijections that respect lexicographic order full

This proof shows: correctness, termination.

We use the pseudocode of the page. Let SnS_n be the permutations of [n][n], and for a∈Sna \in S_n let ci(a)=∣{j>i:aj<ai}∣c_i(a) = |\{j > i : a_j < a_i\}| be its Lehmer code. Let Ui(a)=[n]∖{a1,…,ai−1}U_i(a) = [n] \setminus \{a_1, \ldots, a_{i-1}\} be the set of values not used before position ii; it has n−i+1n - i + 1 elements and contains ai,…,ana_i, \ldots, a_n.

Lemma 1 (digits). ci(a)=∣{v∈Ui(a):v<ai}∣c_i(a) = |\{v \in U_i(a) : v < a_i\}|, and 0≤ci(a)≤n−i0 \le c_i(a) \le n - i.

Proof. The values of Ui(a)U_i(a) other than aia_i are exactly ai+1,…,ana_{i+1}, \ldots, a_n, the values at the positions after ii. So the values of Ui(a)U_i(a) below aia_i are the values after position ii below aia_i. There are n−in - i positions after ii. □\square

Lemma 2 (rank formula). rank⁡(a):=∣{b∈Sn:b<a}∣=∑i=1nci(a) (n−i)!\operatorname{rank}(a) := |\{b \in S_n : b < a\}| = \sum_{i=1}^{n} c_i(a)\,(n-i)!, where << is lexicographic order.

Proof. For b<ab < a, let ii be the first position with bi≠aib_i \ne a_i; then bi<aib_i < a_i. So the set of b<ab < a is the disjoint union over ii of the sets Bi={b:b1⋯bi−1=a1⋯ai−1, bi<ai}B_i = \{b : b_1 \cdots b_{i-1} = a_1 \cdots a_{i-1},\ b_i < a_i\}. For b∈Bib \in B_i, the value bib_i lies in Ui(a)U_i(a) and is below aia_i, which leaves ci(a)c_i(a) choices by Lemma 1, and then bi+1,…,bnb_{i+1}, \ldots, b_n is any order of the other n−in - i values of Ui(a)U_i(a), which leaves (n−i)!(n-i)! choices. Every such choice is a permutation in BiB_i. So ∣Bi∣=ci(a)(n−i)!|B_i| = c_i(a)(n-i)!. □\square

Lemma 3 (the Fenwick tree). If the tree TT represents a 0-1 array f[1…n]f[1 \ldots n], then PREFIX(T,i)(T, i) returns f[1]+⋯+f[i]f[1] + \cdots + f[i] and ADD(T,i,δ)(T, i, \delta) makes TT represent ff with δ\delta added to f[i]f[i]. This is the lemma proved on the Fenwick tree page: cell T[j]T[j] holds ∑j−lowbit⁡(j)<v≤jf[v]\sum_{j - \operatorname{lowbit}(j) < v \le j} f[v]. When every f[v]=1f[v] = 1, cell jj holds lowbit⁡(j)\operatorname{lowbit}(j), so lines L1 and L8 build the tree of the all-ones array.

Lemma 4 (SELECT). Let TT represent a 0-1 array ff with F(p)=f[1]+⋯+f[p]F(p) = f[1] + \cdots + f[p], and let 1≤k0≤F(n)1 \le k_0 \le F(n). Then SELECT(T,k0)(T, k_0) returns the least qq with F(q)≥k0F(q) \ge k_0, which is the k0k_0-th smallest vv with f[v]=1f[v] = 1.

Proof. Let 2m2^m be the initial value of ss, the largest power of two with 2m≤n2^m \le n. We claim that before each test of line L14 with s=2ts = 2^t, three facts hold: pp is a multiple of 2t+12^{t+1} (for t=mt = m, p=0p = 0), F(p)<k0F(p) < k_0 and k=k0−F(p)k = k_0 - F(p), and the least qq with F(q)≥k0F(q) \ge k_0 satisfies p<q≤p+2t+1p < q \le p + 2^{t+1}, or p<q≤np < q \le n when t=mt = m. Initially p=0p = 0, F(0)=0<k0F(0) = 0 < k_0, k=k0k = k_0, and q≤n<2m+1q \le n < 2^{m+1}. In a round with s=2ts = 2^t, since pp is a multiple of 2t+12^{t+1}, lowbit⁡(p+s)=s\operatorname{lowbit}(p + s) = s, so by Lemma 3 the cell T[p+s]T[p + s] holds F(p+s)−F(p)F(p + s) - F(p) when p+s≤np + s \le n. The test T[p+s]<kT[p + s] < k is then F(p+s)<k0F(p + s) < k_0. If it holds, q>p+sq > p + s, and the new p′=p+sp' = p + s has F(p′)<k0F(p') < k_0 and k′=k−T[p+s]=k0−F(p′)k' = k - T[p + s] = k_0 - F(p'). If it fails, or if p+s>np + s > n, then q≤p+sq \le p + s. In both cases the new pp is a multiple of 2t2^t and p<q≤p+2tp < q \le p + 2^t (when p+s>np + s > n, q≤n<p+sq \le n < p + s). This is the claim for s=2t−1s = 2^{t-1}. When the loop ends after the round with s=1s = 1, p<q≤p+1p < q \le p + 1, so q=p+1q = p + 1. Since ff is 0-1 and FF grows by 1 exactly at the values with f[v]=1f[v] = 1, the least qq with F(q)≥k0F(q) \ge k_0 is the k0k_0-th such value. □\square

Theorem 1 (RANK). For a∈Sna \in S_n, RANK(a)(a) returns rank⁡(a)\operatorname{rank}(a).

Proof. Invariant: before the iteration of line L2 with index ii, TT represents the indicator array of Ui(a)U_i(a), and r=Ri:=∑k<ick(a)∏t=k+1i−1(n−t+1)r = R_i := \sum_{k < i} c_k(a) \prod_{t=k+1}^{i-1} (n - t + 1). Before i=1i = 1, line L1 makes TT the all-ones tree (Lemma 3), U1(a)=[n]U_1(a) = [n], and r=0=R1r = 0 = R_1. In iteration ii, line L3 sets c=∣{v∈Ui(a):v≤ai−1}∣=ci(a)c = |\{v \in U_i(a) : v \le a_i - 1\}| = c_i(a) by Lemmas 3 and 1. Line L4 removes aia_i, so TT represents Ui+1(a)U_{i+1}(a). Line L5 sets

r=Ri (n−i+1)+ci(a)=∑k<ick(a)∏t=k+1i(n−t+1)+ci(a)=Ri+1.r = R_i\,(n - i + 1) + c_i(a) = \sum_{k < i} c_k(a) \prod_{t=k+1}^{i} (n - t + 1) + c_i(a) = R_{i+1}.

After the last iteration, r=Rn+1=∑k=1nck(a)∏t=k+1n(n−t+1)=∑k=1nck(a)(n−k)!r = R_{n+1} = \sum_{k=1}^{n} c_k(a) \prod_{t=k+1}^{n}(n-t+1) = \sum_{k=1}^{n} c_k(a)(n-k)!, which is rank⁡(a)\operatorname{rank}(a) by Lemma 2. □\square

Theorem 2 (UNRANK). For 0≤r<n!0 \le r < n!, UNRANK(r,n)(r, n) returns the a∈Sna \in S_n with rank⁡(a)=r\operatorname{rank}(a) = r.

Proof. Digits. Write r0=rr_0 = r, and let line L7 process i=n,n−1,…,1i = n, n-1, \ldots, 1, with ρ\rho the value of rr before step ii. At step ii it sets di=ρ mod (n−i+1)d_i = \rho \bmod (n - i + 1) and replaces ρ\rho by ⌊ρ/(n−i+1)⌋\lfloor \rho/(n-i+1) \rfloor. By induction on the steps, before step ii we have ρ=⌊r/∏t=i+1n(n−t+1)⌋=⌊r/(n−i)!⌋\rho = \lfloor r / \prod_{t=i+1}^{n} (n-t+1) \rfloor = \lfloor r/(n-i)! \rfloor, and r=ρ (n−i)!+∑k>idk(n−k)!r = \rho\,(n-i)! + \sum_{k > i} d_k (n-k)! with 0≤dk≤n−k0 \le d_k \le n - k. After step 1, ρ=⌊r/n!⌋=0\rho = \lfloor r / n! \rfloor = 0, so r=∑k=1ndk(n−k)!r = \sum_{k=1}^{n} d_k (n-k)! with 0≤dk≤n−k0 \le d_k \le n - k.

Items. Invariant: before the iteration of line L9 with index ii, TT represents the indicator array of Vi=[n]∖{a1,…,ai−1}V_i = [n] \setminus \{a_1, \ldots, a_{i-1}\}, which has n−i+1n - i + 1 elements, and a1,…,ai−1a_1, \ldots, a_{i-1} are distinct. Initially V1=[n]V_1 = [n] by line L8 and Lemma 3. In iteration ii, 1≤di+1≤n−i+1=∣Vi∣1 \le d_i + 1 \le n - i + 1 = |V_i|, so by Lemma 4 line L10 sets aia_i to the (di+1)(d_i + 1)-th smallest element of ViV_i, which is new. Line L11 removes it, which gives Vi+1V_{i+1}. At the end a1,…,ana_1, \ldots, a_n are nn distinct values of [n][n], so a∈Sna \in S_n, and Ui(a)=ViU_i(a) = V_i for every ii. By Lemma 1, ci(a)c_i(a) is the number of elements of ViV_i below aia_i, which is did_i. By Lemma 2, rank⁡(a)=∑idi(n−i)!=r\operatorname{rank}(a) = \sum_i d_i (n-i)! = r. The permutation is unique because rank⁡\operatorname{rank} is injective: by Lemma 2 it maps SnS_n into {0,…,n!−1}\{0, \ldots, n! - 1\}, and a<ba < b implies rank⁡(a)<rank⁡(b)\operatorname{rank}(a) < \operatorname{rank}(b), since every permutation below aa is also below bb and aa itself is below bb but not below aa. □\square

Termination. The loops of lines L2, L7 and L9 run nn times. The loop of PREFIX clears one set bit of ii per round, the loop of ADD at least doubles lowbit⁡(i)\operatorname{lowbit}(i) per round until i>ni > n, and the loop of SELECT halves ss until it is 0. So every loop ends, after at most ⌊lg⁡n⌋+1\lfloor \lg n \rfloor + 1 rounds for the three tree operations. ■\blacksquare

Back to Permutation rank and unrank algorithm