Set A is a finite totally ordered set of symbols. Because set A is finite and totally ordered, it's well-ordered. So, I'll just say A = {1, 2, ..., n} for some n. (1, 2, ..., n) is a finite n-long sequence over A. Consider its set of permutations and the lexicographic order over it. What's the kth permutation with respect to the order?
Notice that there's n! many n-permutations and (n-1)! many n-permutations that begin with a chosen first number. For example, there are (n-1)! n-permutations that are of form (1, ...) and (n-1)! n-permutations that are of form (2, ...) and (n-1)! n-permutations that are of form (3, ...) and so on. So, the corresponding number for the first permutation of form (x, ...) is immediately apparent.
k' = (n - 1)! for (2, 1, 3, ..., n)
k' = 2(n - 1)! for (3, 1, 2, ..., n)
...
So, if a sequence is of form (x,...), then it's number is greater than or equal to (x-1)(n-1)!
What then can we say about sequences of form (x,y, ...), x≠y? Is there a numbering for the pairs (x,y), x≠y for which I can make a similar bound? First, observe that there are n(n-1) = nP2 such pairs.
The obvious intuition for nP2 is that by choosing one element and then another without replacement, there's n(n - 1) choices: nP2 = (n choose 1)(n-1 choose 1) = n(n - 1).
Observe that the set of permutations of the sequence (1,2, ..., n) is also the set of all n-long sequences whose elements are distinct. Notice, that this means that elements of a permutation sequence s are coupled by the following constraint/sentence: for all i and j in [1,n], if i ≠ j, then s(i) ≠ s(j). An interesting consequence of this coupling is that if you pick all other n-1 elements of the sequence the final element is fully determined.
This coupled-ness makes finding a desired numbering over the original permutation sequence very inconvenient. Consider the first sequence of form (2,1, ...) and the first sequence of form (2, 3, ...). The first sequences' corresponding number is (n - 1)(n - 2)!=(n - 1)!, but the second's corresponding number is n(n - 2)! . Finding this numbering would involve numbering the pairs A✕A - {(1,1), (2,2), ..., (n,n)} lexicographically, so the numberings for sequences (x, x - 1, ...) and (x, x + 1, ...) have to be adjacent by a factor of (n - 2)! .
Permutations with more inversions have a higher number. Can you characterize the of permutations with k exactly inversions? What about permutations with less than or equal to k inversions? What happens to a permutation's number when a swap is performed?
Still, there's an easy way to construct the desired correspondence between the n-permutations and [1, n!]. Naturally, it involves an intermediate sequence whose elements are decoupled.
First, I'll introduce a cool concept: the minimum excluded value. Say that S is a subset of a well-ordered set A. mex(S) is the minimum excluded value, i.e. the smallest value that is in A, but not in S. Here's a generalization: the kth smallest excluded value, which is the kth smallest value that is in A: kex(k,S). Notice that kex(1,S) = mex(S). Also, notice that kex(k, {1, ..., n}) = k.
One way to construct an n-permutation s1 is to
choose s1(0) by drawing the s2(0)th element from A = {1, ..., n},
and then choose s1(1) by drawing the s2(1)th element from A - {s1(0)},
and then choose s1(2) by drawing the s2(2)th element from A - {s1(0), s1(1)},
and so on.
The relation between s1 and s2 can be expressed neatly with kex:
s1(i) = kex( s2(i), A - {s1(0), ..., s1(i - 1)} )
Notice that the elements of this "picking sequence" s2 are decoupled, unlike the permutation sequence s1. Also, notice that s2(i) ∈ {1, 2, ..., n - i}. The decoupled-ness permits an easy numbering:
k' = (s2(0) - 1)(n - 1)! + ... + (s2(n - 1) - 1)0!
if s2'(i) = (s2(i)-1), then
k' = s2'(0)(n - 1)! + ... + s2'(n - 1)0!
This is a decomposition into a factorial numeral system, and the digits can be retrieved with division.
Here's accompanying Kotlin code
val _factorial : MutableList<Int> = MutableList(n) {0}
_factorial[0] = 1
for (i in 1..(n-1)) {
_factorial[i] = _factorial[i-1] * i
}
val factorial : List<Int> = _factorial as List<Int>
val permutation = StringBuilder()
val picks : MutableList<Int> = mutableListOf<Int>()
for (i in 1..n) {
picks.add(i)
}
var kMinusOne : Int = k - 1 // k' is kMinusOne
for (i in 1..n) {
val s : Int = kMinusOne / factorial[n - i]
kMinusOne = kMinusOne % factorial[n-i]
permutation.append(picks[s])
picks.removeAt(s)
}
return permutation.toString()
}
Related:
Cantor expansion
mixed-radix numeral systems, factorial number systems
Minimum excluded value (mex)
kth order statistics
Lehmer codes