Overview
A permutation of some objects is a (possible) rearrangement of those objects. The number of permutations is since there are possible ways to pick the first object, possible ways to pick the second, and so on.
The above approach prints out all permutations of an array (assuming distinct values).
Lexicographic Ordering
We can find the next lexicographic ordering of an array via a procedure of “pivot”, “swap”, and “reverse”. The function void next(const size_t n, int A[static n])
defined in Overview shows the details, taking in a permutation and producing the next, lexicographically speaking. To prove correctness, consider the following:
[ a₁ a₂ ... aᵢ | aᵢ₊₁ aᵢ₊₂ ... aₙ ]
Here the RHS side is the longest increasing sequence we could find, from right to left. That is, . Denote as the pivot. Next, swap the smallest element in the RHS greater than , say , with . This produces
[ a₁ a₂ ... aⱼ | aᵢ₊₁ aᵢ₊₂ ... aᵢ ... aₙ ]
Notice the RHS remains in sorted order. Since was the next smallest element, reversing the reverse-sorted RHS produces the next permutation, lexicographically speaking:
[ a₁ a₂ ... aⱼ | aₙ ... aᵢ ... aᵢ₊₂ aᵢ₊₁ ]
Eventually the swapped will be the largest in the RHS ensuring that the breakpoint will eventually move one more position leftward.
Falling Factorials
If we generalize to choosing elements of objects, we can calculate the -permutation of . This is denoted as , sometimes called the falling factorial.
The derivation works by noting that we have possible ways to pick the first object, ways to pick the second, up until ways to pick the last object.
Bibliography
- Gries, David. The Science of Programming. Texts and Monographs in Computer Science. New York: Springer-Verlag, 1981.
- Oscar Levin, Discrete Mathematics: An Open Introduction, 3rd ed., n.d., https://discrete.openmathbooks.org/pdfs/dmoi3-tablet.pdf.