Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Finding the (lexicographic) index of a permutation of a given array.

Given an array say "bca", I need to find the number of permutations which are lexicographicaly greater than the given permutation.

Thus, in this example, cab, cba are permutations which are greater. Thus the answer would be 2.

I tried approaching the problem by finding the lexicographic ranking of the array, but am not able to devise an efficient algorithm for the say.

Any help/pointers in the right direction is appreciated!

like image 318
user1628340 Avatar asked Aug 27 '12 17:08

user1628340


People also ask

How do you find the lexicographic order of a 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).

How do you find the lexicographically smallest permutation?

Approach: Since the permutation should be lexicographically smallest with the condition satisfied for k indices i.e. a[i] > a[i+1]. So for that starting K+1 digits should be in decreasing order and the remaining should be in increasing order. So just print the K numbers from N to 1. Then print numbers from 1 to N-K.

What is lexicographic order in array?

Thus, lexicographical order is a way for formalizing word order where the order of the underlying symbols is given. In programming, lexicographical order is popularly known as Dictionary order and is used to sort a string array, compare two strings, or sorting array elements.


2 Answers

Let's look at the permutation dacb. Where does this come in lexicographic order among the 4! = 24 permutations of abcd?

  • Consider the first letter d. Among the remaining letters (acb) there are three letters smaller than d, and 3! = 6 permutations starting with each one of them, for a total of 18 permutations.
  • Consider the first two letters da. Among the remaining letters (cb) there are no letters smaller than a (if there were any there would be 2! = 2 permutations starting with d plus each one), for a total of 0 permutations.
  • Consider the first three letters dac. Among the remaining letters (b) there is one letter smaller than c, and 1! = 1 permutations starting with dab, for a total of 1 permutation.

So in total there are 19 permutations smaller than dacb. Let's check that.

>>> from itertools import permutations
>>> list(enumerate(''.join(p) for p in permutations('abcd')))
[(0, 'abcd'), (1, 'abdc'), (2, 'acbd'), (3, 'acdb'),
 (4, 'adbc'), (5, 'adcb'), (6, 'bacd'), (7, 'badc'),
 (8, 'bcad'), (9, 'bcda'), (10, 'bdac'), (11, 'bdca'),
 (12, 'cabd'), (13, 'cadb'), (14, 'cbad'), (15, 'cbda'),
 (16, 'cdab'), (17, 'cdba'), (18, 'dabc'), (19, 'dacb'),
 (20, 'dbac'), (21, 'dbca'), (22, 'dcab'), (23, 'dcba')]

Looks good. So there are 4! − 19 − 1 = 4 permutations that are greater than dacb.

It should be clear now how to generalize the method to make an algorithm. Here's an implementation in Python:

from math import factorial

def lexicographic_index(p):
    """
    Return the lexicographic index of the permutation `p` among all
    permutations of its elements. `p` must be a sequence and all elements
    of `p` must be distinct.

    >>> lexicographic_index('dacb')
    19
    >>> from itertools import permutations
    >>> all(lexicographic_index(p) == i
    ...     for i, p in enumerate(permutations('abcde')))
    True
    """
    result = 0
    for j in range(len(p)):
        k = sum(1 for i in p[j + 1:] if i < p[j])
        result += k * factorial(len(p) - j - 1)
    return result

def lexicographic_followers(p):
    """
    Return the number of permutations of `p` that are greater than `p`
    in lexicographic order. `p` must be a sequence and all elements
    of `p` must be distinct.
    """
    return factorial(len(p)) - lexicographic_index(p) - 1
like image 108
Gareth Rees Avatar answered Sep 28 '22 08:09

Gareth Rees


There is a very clean way to do this based on the factorial number system and Lehmer codes. The idea is to assign a numeric code to each possible permutation that encodes the order in which the values occur (the Lehmer code). You can then convert the Lehmer code into a number that determines the index of the permutation in the list of all permutations (this uses the factorial number system). Given the index of the permutation, you can then compute (n! - 1) and subtract out the index to determine how many more permutations there are.

If you're curious how to do this, I have an implementation of this algorithm that lets you map from permutations to indices or vice-versa. I also gave a talk on how to do this; the details are in the latter half of the slides.

Hope this helps!

like image 25
templatetypedef Avatar answered Sep 28 '22 07:09

templatetypedef