Algorithm Search⌘K

Proofs: Cycle-leader in-place permutation

These are the human proofs of Cycle-leader in-place permutation, at full length.

APPLY and APPLY-READONLY move every item to its target and restore P full

This proof shows: correctness, termination.

We use the pseudocode of the page. Let PP be a permutation of [n][n], let A0A_0 be the array on entry, and let the goal be the array A′A' with A′[P[i]]=A0[i]A'[P[i]] = A_0[i] for every ii, equivalently A′[m]=A0[P−1(m)]A'[m] = A_0[P^{-1}(m)]. For an index ii write C(i)={i,P(i),P2(i),…}C(i) = \{i, P(i), P^2(i), \ldots\} for its cycle and ℓ(i)=∣C(i)∣\ell(i) = |C(i)|, so Pℓ(i)(i)=iP^{\ell(i)}(i) = i and the indices i,P(i),…,Pℓ(i)−1(i)i, P(i), \ldots, P^{\ell(i)-1}(i) are distinct. Write ∣P[j]∣|P[j]| for the value of the entry of PP at jj ignoring its sign.

Lemma 1 (one cycle). Suppose the cycle walk of lines L3 to L7 starts at ii when A[m]=A0[m]A[m] = A_0[m] for every m∈C(i)m \in C(i) and when ∣P[j]∣=P(j)|P[j]| = P(j) for every jj. Then it ends after ℓ(i)−1\ell(i) - 1 rounds of the loop at line L4, with A[m]=A′[m]A[m] = A'[m] for every m∈C(i)m \in C(i), with AA unchanged outside C(i)C(i), and with the entries of PP on C(i)C(i) negated and all other entries unchanged.

Proof. We claim that before round tt of the loop, for 1≤t≤ℓ(i)1 \le t \le \ell(i), we have j=Pt(i)j = P^t(i), x=A0[Pt−1(i)]x = A_0[P^{t-1}(i)], A[Ps(i)]=A′[Ps(i)]A[P^s(i)] = A'[P^s(i)] and P[Ps(i)]<0P[P^s(i)] < 0 for 1≤s<t1 \le s < t, and every other entry of AA and PP on C(i)C(i) is as on entry. For t=1t = 1, line L3 sets x=A0[i]x = A_0[i] and j=P(i)j = P(i), and nothing else has changed. Suppose the claim holds before round tt and j=Pt(i)≠ij = P^t(i) \ne i, so t<ℓ(i)t < \ell(i) and jj has not been written yet. Line L5 stores x=A0[Pt−1(i)]x = A_0[P^{t-1}(i)] at j=P(Pt−1(i))j = P(P^{t-1}(i)), which is A′[j]A'[j] by the definition of A′A', and sets xx to the old content A0[j]=A0[Pt(i)]A_0[j] = A_0[P^t(i)]. Line L6 reads k=P[j]=Pt+1(i)k = P[j] = P^{t+1}(i), still positive since jj was not marked, negates P[j]P[j] and sets j=Pt+1(i)j = P^{t+1}(i). So the claim holds before round t+1t + 1. The loop stops at the first tt with Pt(i)=iP^t(i) = i, which is t=ℓ(i)t = \ell(i). Then x=A0[Pℓ(i)−1(i)]=A0[P−1(i)]x = A_0[P^{\ell(i)-1}(i)] = A_0[P^{-1}(i)], which line L7 writes to A[i]A[i], so A[i]=A′[i]A[i] = A'[i], and line L7 negates P[i]P[i]. Every index of C(i)C(i) has now been written once and negated once, and nothing outside C(i)C(i) was read for writing or changed. □\square

Lemma 2 (APPLY). Before iteration ii of line L1, the cycles that contain an index smaller than ii are exactly the cycles whose entries of PP are all negative, they hold their final items, and every other cycle holds its original items and has all its entries of PP positive.

Proof. By induction on ii. Before i=1i = 1 nothing is marked and nothing has moved. At iteration ii, if C(i)C(i) contains an index smaller than ii then by the hypothesis its entries, including P[i]P[i], are negative, line L2 skips it, and the statement holds for i+1i + 1. Otherwise ii is the smallest index of C(i)C(i), the hypothesis says that C(i)C(i) holds its original items with positive entries, and the absolute values of all entries equal PP. Lemma 1 then finishes C(i)C(i) and marks it, without touching the other cycles. In both cases the cycles with an index smaller than i+1i + 1 are exactly the finished, negated ones. □\square

Theorem 1. APPLY returns A′A' and leaves PP unchanged.

Proof. After the last iteration of line L1, Lemma 2 with i=n+1i = n + 1 says every cycle is finished, so A=A′A = A', and every entry of PP is negative with absolute value P(j)P(j). Line L8 negates every entry once, which restores PP. □\square

Lemma 3 (leader test). At iteration ii of line L10, line L11 ends with j=ij = i exactly when i=min⁡C(i)i = \min C(i).

Proof. The walk visits P(i),P2(i),…P(i), P^2(i), \ldots and stops at the first visited index that is at most ii. Such an index exists, since Pℓ(i)(i)=iP^{\ell(i)}(i) = i, so the walk stops after at most ℓ(i)\ell(i) steps. If i=min⁡C(i)i = \min C(i), every index visited before returning to ii is larger than ii, so the walk stops at ii. If some index of C(i)C(i) is smaller than ii, the walk meets one of them before it can come back to ii, since it passes through every element of C(i)C(i) before Pℓ(i)(i)=iP^{\ell(i)}(i) = i, and stops there with j<ij < i. □\square

Theorem 2. APPLY-READONLY returns A′A' and does not write PP.

Proof. Line L14 is the walk of Lemma 1 without the marks. The argument of Lemma 1 does not use the signs except to keep P[j]P[j] readable, so a walk started at ii when C(i)C(i) holds its original items finishes C(i)C(i) and changes nothing else. By Lemma 3, each cycle is walked exactly once, at its smallest index, and at that moment no index of it has been written, because the only walks so far started at the leaders of other cycles. So at the end every cycle is finished and A=A′A = A'. No line writes PP. □\square

Termination. Each walk of line L4 or line L14 stops after ℓ(i)−1\ell(i) - 1 rounds by Lemma 1, each walk of line L11 after at most ℓ(i)\ell(i) steps by Lemma 3, and the loops at lines L1, L8 and L10 run nn times. So both procedures halt, APPLY after O(n)O(n) steps in total, since the rounds of line L4 over all cycles add up to ∑C(∣C∣−1)=n−c\sum_C (|C| - 1) = n - c, where cc is the number of cycles. ■\blacksquare

Back to Cycle-leader in-place permutation