4

I stumbled across this interview question:

Given a list of elements in lexicographical order (i.e. ['a', 'b', 'c', 'd']), find the nth permutation

I tried it myself, and it took me about ~30 minutes to solve. (I ended up with a ~8-9 line solution in Python). Just curious -- how long "should" it take to solve this type of problem? Am I taking too long?

Paul Sonier
  • 38,903
  • 3
  • 77
  • 117
jasonbogd
  • 2,451
  • 4
  • 31
  • 42
  • Here c# version (just FYI): http://stackoverflow.com/questions/6799696/how-to-find-permutation-of-a-given-string-with-its-rank/25082541#25082541 – Dreamer Aug 01 '14 at 14:51

3 Answers3

12

9 min, including test

import math

def nthperm(li, n):
    n -= 1
    s = len(li)
    res = []
    if math.factorial(s) <= n:
        return None
    for x in range(s-1,-1,-1):
        f = math.factorial(x)
        d = n / f
        n -= d * f
        res.append(li[d])
        del(li[d])
    return res

#now that's fast...
nthperm(range(40), 123456789012345678901234567890)
Karoly Horvath
  • 94,607
  • 11
  • 117
  • 176
1

Just in case someone is interested in a solution that can find the "i-th" permutation when you look at the "r-length-permutations" (as represented by the r argument of itertools.permutations):

from math import factorial

def ith_permutation(i, seq, r=None):
    li = list(seq)
    length = len(li)

    if r is None:
        r = length
    res = []
    current_factorial = factorial(length) // factorial(length - r)

    if current_factorial <= i:
        raise ValueError('out of range')

    for x in range(length, length-r, -1):
        current_factorial //= x
        div, mod = divmod(i, current_factorial)
        i = mod
        res.append(li[div])
        del(li[div])

    return res

For example:

>>> ith_permutationutation(10, [0, 1, 2, 3, 4], 2)
[2, 3]

>>> # correctness check:
>>> from itertools import permutations
>>> list(permutations([0, 1, 2, 3, 4], 2))[10]
(2, 3)

Including a more complete test:

s = range(8)
for n in range(len(s)):
    for idx, item in enumerate(permutations(s, n)):
        assert list(item) == ith_permutation(idx, s, n)

Some parts of Karoly Horvath's answer were used here.

MSeifert
  • 145,886
  • 38
  • 333
  • 352
0

Perhaps I am missing something, I thought we can find it easily with nth from itertools Recipes and permutations:

from itertools import permutations, islice
def nth(iterable, n, default=None):
    "Returns the nth item or a default value"
    return next(islice(iterable, n, None), default)
def main():
    data = ['a', 'b', 'c', 'd']
    n = 7  # 0 indexed
    print nth(permutations(data), n)
if __name__ == "__main__":
    main()
sunqiang
  • 6,422
  • 1
  • 32
  • 32
  • 5
    it's extremely slow if n is large, you have to generate n permutations and only 1 of them is going to be used. originatlly there was a post exactly like this but then the OP explained that the task is to do it without itertools help, and then the poster deleted it. leave it here, it's a perfectly fine solution for small n-s. – Karoly Horvath Aug 07 '11 at 09:34