Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Algorithm for finding numerical permutation given lexicographic index

I am looking for an algorithm that given a set of numbers (for example 1 2 3) and an index (for example 2) will get me the second permutation of those numbers according to a lexicographic order. For example in this case the algorithm will return 1 3 2.

like image 248
Eddie Dovzhik Avatar asked Jan 20 '12 11:01

Eddie Dovzhik


People also ask

How do you find lexicographic permutation?

The lexicographic permutation order starts from the identity permutation (1,2,..., n). By successively swapping only two numbers one obtains all possible permutations. The last permutation in lexicographic order will be the permutation with all numbers in reversed order, i.e. (n,n-1,...,2,1).

What is lexicographic order in algorithm?

A lexicographic order ≺ _ lex ( l e x ) allows to compare sequences by comparing the elements of the sequences proceeding from start to end. Given two sequences l1 and l2 of variables of the same length n, [x1,…, xn] and [y1,…, yn], then l 1 ≺ _ l e x l 2 if and only if n=0 or x1y1 or x1=y1 and [x2,…, xn]

How do I find the next permutation in lexicographic order in Python?

First we create a loop that will run n! ties where n is the length of the string as there will be n! permutations. Every iteration prints the string and finds its next larger lexicographical permutation to be printed in the next iteration.


1 Answers

Here is a sample solution in Scala, which I will explain in detail:

/**
    example: index:=15, list:=(1, 2, 3, 4)
*/ 
def permutationIndex (index: Int, list: List [Int]) : List [Int] = 
  if (list.isEmpty) list else {
    val len = list.size     // len = 4
    val max = fac (len)     // max = 24
    val divisor = max / len // divisor = 6
    val i = index / divisor // i = 2
    val el = list (i)
    el :: permutationIndex (index - divisor * i, list.filter (_ != el)) }

Since Scala isn't that well known, I think I have to explain the last line of the algorithm, which is, apart from that, pretty self explanatory.

    el :: elist

constructs a new list from an element el and an list elist. Elist is a recursive call.

    list.filter (_ != el)

is just the list without the element el.

Test it exhaustively with a small List:

(0 to fac (4) - 1).map (permutationIndex (_, List (1, 2, 3, 4))).mkString ("\n")

Test the speed of a bigger List with 2 examples:

scala> permutationIndex (123456789, (1 to 12).toList) 
res45: List[Int] = List(4, 2, 1, 5, 12, 7, 10, 8, 11, 6, 9, 3)

scala> permutationIndex (123456790, (1 to 12).toList) 
res46: List[Int] = List(4, 2, 1, 5, 12, 7, 10, 8, 11, 9, 3, 6)

The result appears immediately on a 5 years old laptop. There are 479 001 600 permutations for a List of 12 elements, but with 100 or 1000 elements, this solution should still work fast - you just would need to use BigInt as Index.

How does it work? I made a graphic, to visualize the example, a List of (1, 2, 3, 4) and an index of 15:

enter image description here

A List of 4 Elements produces 4! permutations (=24). We choose an arbitrary index from 0 to 4!-1, let's say 15.

We can visualize all permutations in a tree, with the first node from 1..4. We divide 4! by 4 and see, that every first-node leads to 6 subtrees. If we divide our index 15 by 6, the result is 2, and the value in our zero-based List with index 2 is 3. So the first Node is 3, and the rest of our List is (1, 2, 4). Here is a table, showing how 15 leads to the element with index 2 in the Array/List/whatever:

0 1 2 3 4 5 | 6 ... 11 | 12 13 14 15 16 17 | 18 ... 23
     0      |     1    |         2         |     3
            |          | 0  1  2  3  4  5  |

We now subtract 12, the first element of the cell (12...17) for the last 3 elements, which have 6 possible permutations, and see, how 15 maps to 3. Number 3 leads to the array index 1 now, which was element 2, so the result so far is List (3, 2, ...).

                        | 0 1 | 2 3 | 4 5 |
                        |  0  |  1  |  3  |
                              | 0 1 |

Again, we subtract 2, and end in 2 remaining elements with 2 permutations, and index (0, 3), mapping to the values (1, 4). We see, that the second element, belonging to the 15 from top, maps to value 3, and the remaining element for the last step is the other one:

                             | 0 | 1 |
                             | 0 | 3 |
                             | 3 | 0 |

Our result is List(3, 2, 4, 1) or the indexes (2, 1, 3, 0). Testing all indexes in order shows, that they yield all permutations in order.

like image 103
user unknown Avatar answered Nov 15 '22 09:11

user unknown