Учитывая список элементов в лексикографическом порядке (например, ['a', 'b', 'c', 'd']), найдите n-ю перестановку - Среднее время решения?

Я наткнулся на этот вопрос интервью:

Учитывая список элементов в лексикографическом порядке (т.е. ['a', 'b', 'c', 'd']), найдите n-ю перестановку

Я попробовал это сам, и мне потребовалось около 30 минут, чтобы решить. (В итоге я получил ~ 8-9 строк решения на Python). Просто любопытно - сколько времени "должно" пройти, чтобы решить эту проблему? Я слишком долго?


person jasonbogd    schedule 21.07.2011    source источник
comment
Здесь версия С# (просто к вашему сведению): " title="как найти перестановку заданной строки с ее рангом"> stackoverflow.com/questions/6799696/   -  person Dreamer    schedule 01.08.2014


Ответы (3)


9 мин, включая тест

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)
person Karoly Horvath    schedule 22.07.2011
comment
он начинается с 1 (первая перестановка), поэтому n -= 1 и <=. Кстати, вы имеете в виду math.factorial(len(li)), верно? - person Karoly Horvath; 22.07.2011
comment
можете ли вы объяснить часть внутри цикла? d = n/f n-= d*f - person piotr; 22.07.2011
comment
если у вас есть список типа [1,2,3,4], то для последних 3 элементов их 3! перестановки, если n‹=3! первый элемент будет 1, если 3!‹n‹=2*3! 2 и так далее. как только вы получили первый элемент, вычтите количество перестановок, которые вы использовали для этого элемента, удалите его из списка и начните снова. - person Karoly Horvath; 22.07.2011
comment
Я надеялся, что кто-то укажет, что уничтожение исходного списка, вероятно, плохая идея, но да ладно.. - person Karoly Horvath; 23.07.2011
comment
Да, я также был обеспокоен тем, что первоначальный список уничтожается. Я предполагаю, что вместо того, чтобы делать del(li[d]), вы можете использовать объединение массивов, чтобы создать новый список.. - person jasonbogd; 24.07.2011
comment
делать li=list(li) в начале, вероятно, проще - person Karoly Horvath; 24.07.2011
comment
Договорились о сроках. Недавно столкнулся с вариантом этого вопроса здесь: projecteuler.net/index.php?section= проблемы&id=24 - person James; 07.08.2011

На всякий случай, если кто-то заинтересован в решении, которое может найти «i-ю» перестановку, когда вы смотрите на «перестановки r-длины» (как представлено аргументом r 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

Например:

>>> 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)

Включая более полный тест:

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)

Здесь были использованы некоторые части ответа Кароли Хорват.

person MSeifert    schedule 09.09.2017

Возможно, я что-то упустил, я думал, что мы можем легко найти это с помощью nth из рецептов itertools и перестановки:

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()
person sunqiang    schedule 26.07.2011
comment
это очень медленно, если n велико, вам нужно сгенерировать n перестановок, и только 1 из них будет использоваться. изначально был именно такой пост, но потом ОП объяснил, что задача состоит в том, чтобы сделать это без помощи itertools, а затем постер удалил его. оставьте это здесь, это прекрасное решение для небольших n-s. - person Karoly Horvath; 07.08.2011