Как сгенерировать все перестановки списка?

Как вы генерируете все перестановки списка в Python независимо от типа элементов в этом списке?

Например:

permutations([])
[]

permutations([1])
[1]

permutations([1, 2])
[1, 2]
[2, 1]

permutations([1, 2, 3])
[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]

person Ricardo Reyes    schedule 19.09.2008    source источник
comment
Я согласен с принятым рекурсивным ответом - СЕГОДНЯ. Однако это по-прежнему остается огромной проблемой информатики. Принятый ответ решает эту проблему с экспоненциальной сложностью (2 ^ N N = len (list)) Решите ее (или докажите, что не можете) за полиномиальное время :) См. Задачу коммивояжера   -  person FlipMcF    schedule 26.03.2009
comment
@FlipMcF Это будет сложно решить за полиномиальное время, учитывая, что факториальное время требуется даже для того, чтобы просто перечислить выходные данные ... так что нет, это невозможно.   -  person Thomas    schedule 03.04.2013
comment
@FlipMcF: нет, на самом деле это не так: а) только для поиска оптимального решения, а не достаточно хороших решений, которые достаточно хороши для реальных целей, и б) мы не нужно расширять все узлы в пространстве поиска, т.е. все перестановки; вот что такое эвристические алгоритмы, такие как A *   -  person smci    schedule 16.01.2021


Ответы (34)


Для этого в стандартной библиотеке есть функция: itertools.permutations.

import itertools
list(itertools.permutations([1, 2, 3]))

Если по какой-то причине вы хотите реализовать это самостоятельно или вам просто интересно узнать, как это работает, вот один хороший подход, взятый из http://code.activestate.com/recipes/252178/:

def all_perms(elements):
    if len(elements) <=1:
        yield elements
    else:
        for perm in all_perms(elements[1:]):
            for i in range(len(elements)):
                # nb elements[0:1] works in both string and list contexts
                yield perm[:i] + elements[0:1] + perm[i:]

Несколько альтернативных подходов перечислены в документации itertools.permutations. Вот один из них:

def permutations(iterable, r=None):
    # permutations('ABCD', 2) --> AB AC AD BA BC BD CA CB CD DA DB DC
    # permutations(range(3)) --> 012 021 102 120 201 210
    pool = tuple(iterable)
    n = len(pool)
    r = n if r is None else r
    if r > n:
        return
    indices = range(n)
    cycles = range(n, n-r, -1)
    yield tuple(pool[i] for i in indices[:r])
    while n:
        for i in reversed(range(r)):
            cycles[i] -= 1
            if cycles[i] == 0:
                indices[i:] = indices[i+1:] + indices[i:i+1]
                cycles[i] = n - i
            else:
                j = cycles[i]
                indices[i], indices[-j] = indices[-j], indices[i]
                yield tuple(pool[i] for i in indices[:r])
                break
        else:
            return

И еще один, основанный на itertools.product:

def permutations(iterable, r=None):
    pool = tuple(iterable)
    n = len(pool)
    r = n if r is None else r
    for indices in product(range(n), repeat=r):
        if len(set(indices)) == r:
            yield tuple(pool[i] for i in indices)
person Eli Bendersky    schedule 19.09.2008
comment
Это и другие рекурсивные решения имеют потенциальную опасность съесть всю оперативную память, если перестановочный список достаточно велик. - person Boris Gorelik; 27.05.2009
comment
Они также достигают предела рекурсии (и умирают) с большими списками - person dbr; 09.06.2009
comment
bgbg, dbr: используется генератор, поэтому сама функция не занимает память. Вам остается только решить, как использовать итератор, возвращаемый all_perms (скажем, вы можете записывать каждую итерацию на диск и не беспокоиться о памяти). Я знаю, что это старый пост, но я пишу его для всех, кто его сейчас читает. Также сейчас лучшим способом было бы использовать itertools.permutations (), как указали многие. - person Jagtesh Chadha; 02.05.2011
comment
Не только генератор . Он использует вложенные генераторы, каждый из которых уступает предыдущему в стеке вызовов, если это не ясно. Он использует O (n) памяти, что хорошо. - person cdunn2001; 19.07.2011
comment
Это решение дает 12 перестановок вместо обычных 6 для [1, 2, 3]. - person Eric O Lebigot; 29.05.2012
comment
PS: Я исправил это с for i in range(len(elements)) вместо for i in range(len(elements)+1). Фактически, выделенный элемент elements[0:1] может находиться в len(elements) разных позициях, в результате, а не len(elements)+1. - person Eric O Lebigot; 29.05.2012
comment
Это решение намного быстрее, чем решение tzwenn или мое, которое основано на последовательном выборе первого элемента выходной перестановки и рекурсивном добавлении перестановок оставшихся элементов (фактор 3 или 4 для элементов). Однако я еще не уверен, почему. - person Eric O Lebigot; 29.05.2012
comment
PS: это решение намного быстрее, потому что перестановка N элементов требует только вычислений всех перестановок N-1 элементов один раз (по сравнению с N раз для наших решений). - person Eric O Lebigot; 05.07.2012
comment
В python 2.7 это не работает для различных вариантов списков - person holroy; 04.11.2015
comment
К сожалению, мы не можем использовать параметр размера только для генерации перестановок заданного размера. - person Pol Dellaiera; 23.12.2016
comment
yield perm[:i] + elements[0:1] + perm[i:] может быть yield perm[:i] + elements[0] + perm[i:]? - person Jack; 04.06.2018
comment
list (set (itertools.permutations ([1, 1, 2])), если у вас есть повторяющиеся элементы в списке. - person Aakash Saxena; 08.10.2018
comment
@BorisGorelik. В настоящее время я использую itertools.permutations, но он приводит к сбою моего экземпляра на AWS, поскольку в нем более 10 элементов. Как преодолеть этот вызов? - person Sade; 19.05.2021

И в Python 2.6 и новее:

import itertools
itertools.permutations([1,2,3])

(возвращается как генератор. Используйте list(permutations(l)) для возврата в виде списка.)

person Brian    schedule 19.09.2008
comment
Обратите внимание, что существует параметр r, например itertools.permutations([1,2,3], r=2), который сгенерирует все возможные перестановки, выбрав 2 элемента: [(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)] - person toto_tico; 24.08.2017

Следующий код ТОЛЬКО для Python 2.6 и выше

Сначала импортируйте itertools:

import itertools

Перестановка (порядок имеет значение):

print list(itertools.permutations([1,2,3,4], 2))
[(1, 2), (1, 3), (1, 4),
(2, 1), (2, 3), (2, 4),
(3, 1), (3, 2), (3, 4),
(4, 1), (4, 2), (4, 3)]

Комбинация (порядок НЕ имеет значения):

print list(itertools.combinations('123', 2))
[('1', '2'), ('1', '3'), ('2', '3')]

Декартово произведение (с несколькими итерациями):

print list(itertools.product([1,2,3], [4,5,6]))
[(1, 4), (1, 5), (1, 6),
(2, 4), (2, 5), (2, 6),
(3, 4), (3, 5), (3, 6)]

Декартово произведение (с одним итератором и самим собой):

print list(itertools.product([1,2], repeat=3))
[(1, 1, 1), (1, 1, 2), (1, 2, 1), (1, 2, 2),
(2, 1, 1), (2, 1, 2), (2, 2, 1), (2, 2, 2)]
person e-satis    schedule 04.10.2008
comment
+1! Ссылка на документы: docs.python.org/2/library/itertools.html # itertools.permutations - person Pramod; 30.01.2013
comment
`print list (itertools.permutations ([1,2,3,4], 2)) ^` SyntaxError: invalid syntax` Только начинаю использовать VS Code Что я сделал не так? Указатель указывает под буквой t списка - person Doochz; 08.06.2020
comment
Скобки @gus отсутствуют для print: print (list (itertools.permutations ([1,2,3,4], 2))) Я думаю, это из-за разных версий Python. - person giammi56; 15.10.2020

def permutations(head, tail=''):
    if len(head) == 0:
        print(tail)
    else:
        for i in range(len(head)):
            permutations(head[:i] + head[i+1:], tail + head[i])

называется как:

permutations('abc')
person kx2k    schedule 12.10.2011
comment
Зачем печатать tail, а затем возвращать None? Почему бы вместо этого не вернуть хвост? Почему все равно ничего не вернуть? - person bugmenot123; 27.11.2017
comment
@ bugmenot123 вам, вероятно, нужны все последние хвосты, а не только хвост, это легко сделать, добавив параметр perms=[] к функции, добавив к нему каждый print и получив последний return perms - person Alex Moore-Niemi; 03.01.2021

#!/usr/bin/env python

def perm(a, k=0):
   if k == len(a):
      print a
   else:
      for i in xrange(k, len(a)):
         a[k], a[i] = a[i] ,a[k]
         perm(a, k+1)
         a[k], a[i] = a[i], a[k]

perm([1,2,3])

Выход:

[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 2, 1]
[3, 1, 2]

Поскольку я меняю местами содержимое списка, в качестве входных данных требуется изменяемый тип последовательности. Например. perm(list("ball")) будет работать, а perm("ball") - нет, потому что вы не можете изменить строку.

Эта реализация Python основана на алгоритме, представленном в книге Компьютерные алгоритмы Горовица, Сахни и Раджасекерана.

person Silveira Neto    schedule 14.08.2012
comment
Я предполагаю, что k - это длина или перестановки. Для k = 2 выходов [1, 2, 3]. Разве это не должно быть (1, 2) (1, 3) (2, 1) (2, 3) (3, 1) (3, 2) ?? - person Konstantinos Monachopoulos; 25.02.2019
comment
k - это индекс элемента, который вы хотите поменять местами - person sf8193; 09.05.2020
comment
NameError: имя 'xrange' не определено - person Pathros; 04.04.2021
comment
7 лет спустя, как мне вернуть список списков всех переставленных списков? Кроме того, можно ли это делать итеративно? - person mLstudent33; 24.04.2021

Это решение реализует генератор, чтобы избежать хранения всех перестановок в памяти:

def permutations (orig_list):
    if not isinstance(orig_list, list):
        orig_list = list(orig_list)

    yield orig_list

    if len(orig_list) == 1:
        return

    for n in sorted(orig_list):
        new_list = orig_list[:]
        pos = new_list.index(n)
        del(new_list[pos])
        new_list.insert(0, n)
        for resto in permutations(new_list[1:]):
            if new_list[:1] + resto <> orig_list:
                yield new_list[:1] + resto
person Ricardo Reyes    schedule 19.09.2008

В функциональном стиле

def addperm(x,l):
    return [ l[0:i] + [x] + l[i:]  for i in range(len(l)+1) ]

def perm(l):
    if len(l) == 0:
        return [[]]
    return [x for y in perm(l[1:]) for x in addperm(l[0],y) ]

print perm([ i for i in range(3)])

Результат:

[[0, 1, 2], [1, 0, 2], [1, 2, 0], [0, 2, 1], [2, 0, 1], [2, 1, 0]]
person Paolo    schedule 30.06.2013

Следующий код представляет собой перестановку заданного списка на месте, реализованную как генератор. Поскольку он возвращает только ссылки на список, список не следует изменять вне генератора. Решение нерекурсивное, поэтому использует мало памяти. Также хорошо работать с несколькими копиями элементов в списке ввода.

def permute_in_place(a):
    a.sort()
    yield list(a)

    if len(a) <= 1:
        return

    first = 0
    last = len(a)
    while 1:
        i = last - 1

        while 1:
            i = i - 1
            if a[i] < a[i+1]:
                j = last - 1
                while not (a[i] < a[j]):
                    j = j - 1
                a[i], a[j] = a[j], a[i] # swap the values
                r = a[i+1:last]
                r.reverse()
                a[i+1:last] = r
                yield list(a)
                break
            if i == first:
                a.reverse()
                return

if __name__ == '__main__':
    for n in range(5):
        for a in permute_in_place(range(1, n+1)):
            print a
        print

    for a in permute_in_place([0, 0, 1, 1, 1]):
        print a
    print
person Ber    schedule 20.09.2008

На мой взгляд, вполне очевидный способ:

def permutList(l):
    if not l:
            return [[]]
    res = []
    for e in l:
            temp = l[:]
            temp.remove(e)
            res.extend([[e] + r for r in permutList(temp)])

    return res
person tzwenn    schedule 31.03.2011

list2Perm = [1, 2.0, 'three']
listPerm = [[a, b, c]
            for a in list2Perm
            for b in list2Perm
            for c in list2Perm
            if ( a != b and b != c and a != c )
            ]
print listPerm

Выход:

[
    [1, 2.0, 'three'], 
    [1, 'three', 2.0], 
    [2.0, 1, 'three'], 
    [2.0, 'three', 1], 
    ['three', 1, 2.0], 
    ['three', 2.0, 1]
]
person zmk    schedule 21.08.2011
comment
Хотя технически он дает желаемый результат, вы решаете что-то, что может быть O (n lg n) в O (n ^ n) - немного неэффективно для больших наборов. - person James; 22.08.2011
comment
@ Джеймс: Меня немного смущает то, что вы даете O (n log n): количество перестановок равно n !, что уже намного больше, чем O (n log n); поэтому я не вижу, как решение может быть O (n log n). Однако верно, что это решение находится в O (n ^ n), что намного больше, чем n !, как видно из приближения Стирлинга. - person Eric O Lebigot; 29.05.2012

Я использовал алгоритм, основанный на факториальной системе счисления. Для списка длины n вы можете собрать каждую перестановку элемент за элементом, выбирая из элементов, оставшихся на каждом этапе. У вас есть n вариантов для первого элемента, n-1 для второго и только один для последнего, поэтому вы можете использовать цифры числа в факториальной системе счисления в качестве индексов. Таким образом, числа от 0 до n! -1 соответствуют всем возможным перестановкам в лексикографическом порядке.

from math import factorial
def permutations(l):
    permutations=[]
    length=len(l)
    for x in xrange(factorial(length)):
        available=list(l)
        newPermutation=[]
        for radix in xrange(length, 0, -1):
            placeValue=factorial(radix-1)
            index=x/placeValue
            newPermutation.append(available.pop(index))
            x-=index*placeValue
        permutations.append(newPermutation)
    return permutations

permutations(range(3))

выход:

[[0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1], [2, 1, 0]]

Этот метод нерекурсивен, но на моем компьютере он немного медленнее, и xrange выдает ошибку, когда n! слишком велик для преобразования в длинное целое число C (для меня n = 13). Этого было достаточно, когда мне это было нужно, но это далеко не itertools.permutations.

person timeeeee    schedule 08.08.2013
comment
Привет, добро пожаловать в Stack Overflow. Хотя публикация метода грубой силы имеет свои достоинства, если вы не думаете, что ваше решение лучше, чем принятое решение, вам, вероятно, не следует публиковать его (особенно по старому вопросу, на который уже есть так много ответов). - person Hannele; 09.08.2013
comment
На самом деле я искал небиблиотечный подход грубой силы, так что спасибо! - person Jay Taylor; 01.07.2016
comment
Я тоже нашел это полезным! - person user3347814; 11.10.2020

Штатная реализация (без выхода - все сделаю в памяти):

def getPermutations(array):
    if len(array) == 1:
        return [array]
    permutations = []
    for i in range(len(array)): 
        # get all perm's of subarray w/o current item
        perms = getPermutations(array[:i] + array[i+1:])  
        for p in perms:
            permutations.append([array[i], *p])
    return permutations

Реализация доходности:

def getPermutations(array):
    if len(array) == 1:
        yield array
    else:
        for i in range(len(array)):
            perms = getPermutations(array[:i] + array[i+1:])
            for p in perms:
                yield [array[i], *p]

Основная идея состоит в том, чтобы перебрать все элементы в массиве для 1-й позиции, а затем во 2-й позиции перебрать все остальные элементы без выбранного элемента для 1-й и т. Д. Вы можете сделать это с помощью рекурсии , где критерием остановки является массив из 1 элемента - и в этом случае вы возвращаете этот массив.

введите описание изображения здесь

person Maverick Meerkat    schedule 04.01.2020
comment
У меня это не работает _ ›ValueError: операнды не могут транслироваться вместе с shape (0,) (2,) для этой строки: perms = getPermutations(array[:i] + array[i+1:]) - person RK1; 06.02.2020
comment
@ RK1 что было введено? - person Maverick Meerkat; 07.02.2020
comment
Я передаю массив numpy _ ›getPermutations(np.array([1, 2, 3])), я вижу, что он работает для списка, просто запутался, так как аргумент func равен array :) - person RK1; 07.02.2020
comment
@ RK1 рад, что это работает :-) list - это ключевое слово в python, поэтому обычно не рекомендуется называть ваш параметр ключевым словом, так как оно будет затенять его. Поэтому я использую слово «массив», так как это фактическая функциональность списка, который я использую - как их массив. Думаю, если бы я написал документацию, я бы ее прояснил. Также я считаю, что основные вопросы собеседования следует решать без внешних пакетов, таких как numpy. - person Maverick Meerkat; 07.02.2020
comment
Ха-ха, это правда, да, пытался использовать его с numba, и сильно пожадничал со скоростью, поэтому пытался использовать его исключительно с numpy массивами - person RK1; 07.02.2020
comment
Эта звездочка должна быть в этой строке? permutations.append ([array [i], * p]) Я удалил его, и код работал нормально. - person dgundersen; 18.01.2021
comment
@dgundersen да там критично. Двоеточие нужно для распаковки массива. В противном случае вы получите очень беспорядочный результат (массив массивов ...) - person Maverick Meerkat; 18.01.2021

Обратите внимание, что этот алгоритм имеет n factorial временную сложность, где n - длина входного списка.

Распечатайте результаты на ходу:

global result
result = [] 

def permutation(li):
if li == [] or li == None:
    return

if len(li) == 1:
    result.append(li[0])
    print result
    result.pop()
    return

for i in range(0,len(li)):
    result.append(li[i])
    permutation(li[:i] + li[i+1:])
    result.pop()    

Пример:

permutation([1,2,3])

Выход:

[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]
person Chen Xie    schedule 23.01.2013

Действительно, можно перебирать первый элемент каждой перестановки, как в ответе цвенна. Однако более эффективно написать это решение следующим образом:

def all_perms(elements):
    if len(elements) <= 1:
        yield elements  # Only permutation possible = no permutation
    else:
        # Iteration over the first element in the result permutation:
        for (index, first_elmt) in enumerate(elements):
            other_elmts = elements[:index]+elements[index+1:]
            for permutation in all_perms(other_elmts): 
                yield [first_elmt] + permutation

Это решение примерно на 30% быстрее, по-видимому, благодаря рекурсии, заканчивающейся на len(elements) <= 1 вместо 0. Он также намного более эффективен с точки зрения памяти, поскольку использует функцию генератора (через yield), как в решении Риккардо Рейеса.

person Eric O Lebigot    schedule 29.05.2012

Это вдохновлено реализацией Haskell, использующей понимание списков:

def permutation(list):
    if len(list) == 0:
        return [[]]
    else:
        return [[x] + ys for x in list for ys in permutation(delete(list, x))]

def delete(list, item):
    lc = list[:]
    lc.remove(item)
    return lc
person piggybox    schedule 16.11.2013

Что касается производительности, это решение для numpy, вдохновленное Knuth, ( p22):

from numpy import empty, uint8
from math import factorial

def perms(n):
    f = 1
    p = empty((2*n-1, factorial(n)), uint8)
    for i in range(n):
        p[i, :f] = i
        p[i+1:2*i+1, :f] = p[:i, :f]  # constitution de blocs
        for j in range(i):
            p[:i+1, f*(j+1):f*(j+2)] = p[j+1:j+i+2, :f]  # copie de blocs
        f = f*(i+1)
    return p[:n, :]

Копирование больших блоков памяти экономит время - это в 20 раз быстрее, чем list(itertools.permutations(range(n)):

In [1]: %timeit -n10 list(permutations(range(10)))
10 loops, best of 3: 815 ms per loop

In [2]: %timeit -n100 perms(10) 
100 loops, best of 3: 40 ms per loop
person B. M.    schedule 24.05.2015

Отказ от ответственности: бесформенный плагин от автора пакета. :)

Пакет trotter отличается от большинства реализаций тем, что он генерирует псевдосписки, которые на самом деле не содержат перестановок, но скорее описывают сопоставления между перестановками и соответствующими позициями в упорядочении, позволяя работать с очень большими «списками» перестановок, как показано в эта демонстрация, которая выполняет довольно мгновенные операции и выполняет поиск в псевдосписке," содержащем "все перестановки букв в алфавите, без использования большего объема памяти или обработки, чем обычная веб-страница.

В любом случае, чтобы сгенерировать список перестановок, мы можем сделать следующее.

import trotter

my_permutations = trotter.Permutations(3, [1, 2, 3])

print(my_permutations)

for p in my_permutations:
    print(p)

Выход:

A pseudo-list containing 6 3-permutations of [1, 2, 3].
[1, 2, 3]
[1, 3, 2]
[3, 1, 2]
[3, 2, 1]
[2, 3, 1]
[2, 1, 3]
person Richard Ambler    schedule 21.12.2019

ДРУГОЙ ПОДХОД (без библиотек)

def permutation(input):
    if len(input) == 1:
        return input if isinstance(input, list) else [input]

    result = []
    for i in range(len(input)):
        first = input[i]
        rest = input[:i] + input[i + 1:]
        rest_permutation = permutation(rest)
        for p in rest_permutation:
            result.append(first + p)
    return result

Ввод может быть строкой или списком

print(permutation('abcd'))
print(permutation(['a', 'b', 'c', 'd']))
person Tatsu    schedule 29.03.2019
comment
Это не работает для списка с целыми числами, например. [1, 2, 3] возвращает [6, 6, 6, 6, 6, 6] - person RK1; 06.02.2020
comment
@ RK1, ты можешь попробовать это print(permutation(['1','2','3'])) - person Tatsu; 07.02.2020

Вот алгоритм, который работает со списком без создания новых промежуточных списков, подобных решению Бер на https://stackoverflow.com/a/108651/184528.

def permute(xs, low=0):
    if low + 1 >= len(xs):
        yield xs
    else:
        for p in permute(xs, low + 1):
            yield p        
        for i in range(low + 1, len(xs)):        
            xs[low], xs[i] = xs[i], xs[low]
            for p in permute(xs, low + 1):
                yield p        
            xs[low], xs[i] = xs[i], xs[low]

for p in permute([1, 2, 3, 4]):
    print p

Вы можете попробовать этот код здесь: http://repl.it/J9v

person cdiggins    schedule 06.07.2013

Красота рекурсии:

>>> import copy
>>> def perm(prefix,rest):
...      for e in rest:
...              new_rest=copy.copy(rest)
...              new_prefix=copy.copy(prefix)
...              new_prefix.append(e)
...              new_rest.remove(e)
...              if len(new_rest) == 0:
...                      print new_prefix + new_rest
...                      continue
...              perm(new_prefix,new_rest)
... 
>>> perm([],['a','b','c','d'])
['a', 'b', 'c', 'd']
['a', 'b', 'd', 'c']
['a', 'c', 'b', 'd']
['a', 'c', 'd', 'b']
['a', 'd', 'b', 'c']
['a', 'd', 'c', 'b']
['b', 'a', 'c', 'd']
['b', 'a', 'd', 'c']
['b', 'c', 'a', 'd']
['b', 'c', 'd', 'a']
['b', 'd', 'a', 'c']
['b', 'd', 'c', 'a']
['c', 'a', 'b', 'd']
['c', 'a', 'd', 'b']
['c', 'b', 'a', 'd']
['c', 'b', 'd', 'a']
['c', 'd', 'a', 'b']
['c', 'd', 'b', 'a']
['d', 'a', 'b', 'c']
['d', 'a', 'c', 'b']
['d', 'b', 'a', 'c']
['d', 'b', 'c', 'a']
['d', 'c', 'a', 'b']
['d', 'c', 'b', 'a']
person darxtrix    schedule 19.05.2014

Этот алгоритм является наиболее эффективным, он избегает передачи массивов и манипуляций при рекурсивных вызовах, работает в Python 2, 3:

def permute(items):
    length = len(items)
    def inner(ix=[]):
        do_yield = len(ix) == length - 1
        for i in range(0, length):
            if i in ix: #avoid duplicates
                continue
            if do_yield:
                yield tuple([items[y] for y in ix + [i]])
            else:
                for p in inner(ix + [i]):
                    yield p
    return inner()

Использование:

for p in permute((1,2,3)):
    print(p)

(1, 2, 3)
(1, 3, 2)
(2, 1, 3)
(2, 3, 1)
(3, 1, 2)
(3, 2, 1)
person Cmyker    schedule 31.01.2015

Сгенерируйте все возможные перестановки

Я использую python3.4:

def calcperm(arr, size):
    result = set([()])
    for dummy_idx in range(size):
        temp = set()
        for dummy_lst in result:
            for dummy_outcome in arr:
                if dummy_outcome not in dummy_lst:
                    new_seq = list(dummy_lst)
                    new_seq.append(dummy_outcome)
                    temp.add(tuple(new_seq))
        result = temp
    return result

Тестовые примеры:

lst = [1, 2, 3, 4]
#lst = ["yellow", "magenta", "white", "blue"]
seq = 2
final = calcperm(lst, seq)
print(len(final))
print(final)
person Miled Louis Rizk    schedule 19.03.2016

Чтобы сэкономить вам часы поиска и экспериментов, вот решение для нерекурсивных перестановок в Python, которое также работает с Numba (начиная с версии 0.41):

@numba.njit()
def permutations(A, k):
    r = [[i for i in range(0)]]
    for i in range(k):
        r = [[a] + b for a in A for b in r if (a in b)==False]
    return r
permutations([1,2,3],3)
[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]

Чтобы составить впечатление о производительности:

%timeit permutations(np.arange(5),5)

243 µs ± 11.1 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
time: 406 ms

%timeit list(itertools.permutations(np.arange(5),5))
15.9 µs ± 8.61 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
time: 12.9 s

Поэтому используйте эту версию только в том случае, если вам нужно вызвать ее из функции njitted, в противном случае предпочтите реализацию itertools.

person Anatoly Alekseev    schedule 14.12.2018

Я вижу, что внутри этих рекурсивных функций происходит много итераций, а не совсем чистая рекурсия ...

поэтому для тех из вас, кто не может соблюдать даже один цикл, вот грубое, совершенно ненужное полностью рекурсивное решение

def all_insert(x, e, i=0):
    return [x[0:i]+[e]+x[i:]] + all_insert(x,e,i+1) if i<len(x)+1 else []

def for_each(X, e):
    return all_insert(X[0], e) + for_each(X[1:],e) if X else []

def permute(x):
    return [x] if len(x) < 2 else for_each( permute(x[1:]) , x[0])


perms = permute([1,2,3])
person Karo Castro-Wunsch    schedule 05.08.2016

Другое решение:

def permutation(flag, k =1 ):
    N = len(flag)
    for i in xrange(0, N):
        if flag[i] != 0:
            continue
        flag[i] = k 
        if k == N:
            print flag
        permutation(flag, k+1)
        flag[i] = 0

permutation([0, 0, 0])
person anhldbk    schedule 25.03.2017
comment
NameError: имя 'xrange' не определено - person Pathros; 04.04.2021
comment
@Pathros ну, приведенный выше код предназначен для Python 2. Для Python 3 используйте range(). См. stackoverflow.com/questions/17192158/ - person anhldbk; 06.04.2021

В любом случае мы могли бы использовать библиотеку sympy, а также поддержку перестановок мультимножества

import sympy
from sympy.utilities.iterables import multiset_permutations
t = [1,2,3]
p = list(multiset_permutations(t))
print(p)

# [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]

Ответ очень вдохновлен Получить все перестановки массива numpy

person Community    schedule 07.07.2020

Это асимптотически оптимальный способ O (n * n!) Генерации перестановок после начальной сортировки.

Нет! перестановок не более, а hasNextPermutation (..) выполняется за O (n) временную сложность

За 3 шага

  1. Найдите наибольшее j такое, что a [j] можно увеличить
  2. Увеличьте a [j] на минимально возможную величину
  3. Найдите лексикографически наименьший способ расширить новый a [0..j]
'''
Lexicographic permutation generation

consider example array state of [1,5,6,4,3,2] for sorted [1,2,3,4,5,6]
after 56432(treat as number) ->nothing larger than 6432(using 6,4,3,2) beginning with 5
so 6 is next larger and 2345(least using numbers other than 6)
so [1, 6,2,3,4,5]
'''
def hasNextPermutation(array, len):
    ' Base Condition '
    if(len ==1):
        return False
    '''
    Set j = last-2 and find first j such that a[j] < a[j+1]
    If no such j(j==-1) then we have visited all permutations
    after this step a[j+1]>=..>=a[len-1] and a[j]<a[j+1]

    a[j]=5 or j=1, 6>5>4>3>2
    '''
    j = len -2
    while (j >= 0 and array[j] >= array[j + 1]):
        j= j-1
    if(j==-1):
        return False
    # print(f"After step 2 for j {j}  {array}")
    '''
    decrease l (from n-1 to j) repeatedly until a[j]<a[l]
    Then swap a[j], a[l]
    a[l] is the smallest element > a[j] that can follow a[l]...a[j-1] in permutation
    before swap we have a[j+1]>=..>=a[l-1]>=a[l]>a[j]>=a[l+1]>=..>=a[len-1]
    after swap -> a[j+1]>=..>=a[l-1]>=a[j]>a[l]>=a[l+1]>=..>=a[len-1]

    a[l]=6 or l=2, j=1 just before swap [1, 5, 6, 4, 3, 2] 
    after swap [1, 6, 5, 4, 3, 2] a[l]=5, a[j]=6
    '''
    l = len -1
    while(array[j] >= array[l]):
        l = l-1
    # print(f"After step 3 for l={l}, j={j} before swap {array}")
    array[j], array[l] = array[l], array[j]
    # print(f"After step 3 for l={l} j={j} after swap {array}")
    '''
    Reverse a[j+1...len-1](both inclusive)

    after reversing [1, 6, 2, 3, 4, 5]
    '''
    array[j+1:len] = reversed(array[j+1:len])
    # print(f"After step 4 reversing {array}")
    return True

array = [1,2,4,4,5]
array.sort()
len = len(array)
count =1
print(array)
'''
The algorithm visits every permutation in lexicographic order
generating one by one
'''
while(hasNextPermutation(array, len)):
    print(array)
    count = count +1
# The number of permutations will be n! if no duplicates are present, else less than that
# [1,4,3,3,2] -> 5!/2!=60
print(f"Number of permutations: {count}")


person Bhaskar13    schedule 02.01.2021
comment
Добро пожаловать в Stack Overflow. Дампы кода без каких-либо объяснений редко бывают полезными. Stack Overflow - это обучение, а не предоставление фрагментов для слепого копирования и вставки. Пожалуйста, отредактируйте свой вопрос и объясните, как он отвечает на заданный конкретный вопрос. См. Как ответить. Это особенно важно при ответе на старые вопросы (этому более 12 лет) существующими ответами (у этого 40). Как этот ответ улучшает то, что уже здесь? Также обратите внимание, что вопрос касается Python. Как помогает ответ на Java? - person Chris; 16.04.2021

Мое решение Python:

def permutes(input,offset):
    if( len(input) == offset ):
        return [''.join(input)]

    result=[]        
    for i in range( offset, len(input) ):
         input[offset], input[i] = input[i], input[offset]
         result = result + permutes(input,offset+1)
         input[offset], input[i] = input[i], input[offset]
    return result

# input is a "string"
# return value is a list of strings
def permutations(input):
    return permutes( list(input), 0 )

# Main Program
print( permutations("wxyz") )
person abelenky    schedule 02.03.2018

def permutation(word, first_char=None):
    if word == None or len(word) == 0: return []
    if len(word) == 1: return [word]

    result = []
    first_char = word[0]
    for sub_word in permutation(word[1:], first_char):
        result += insert(first_char, sub_word)
    return sorted(result)

def insert(ch, sub_word):
    arr = [ch + sub_word]
    for i in range(len(sub_word)):
        arr.append(sub_word[i:] + ch + sub_word[:i])
    return arr


assert permutation(None) == []
assert permutation('') == []
assert permutation('1')  == ['1']
assert permutation('12') == ['12', '21']

print permutation('abc')

Вывод: ['abc', 'acb', 'bac', 'bca', 'cab', 'cba']

person Ilgorbek Kuchkarov    schedule 12.05.2018

Использование Counter

from collections import Counter

def permutations(nums):
    ans = [[]]
    cache = Counter(nums)

    for idx, x in enumerate(nums):
        result = []
        for items in ans:
            cache1 = Counter(items)
            for id, n in enumerate(nums):
                if cache[n] != cache1[n] and items + [n] not in result:
                    result.append(items + [n])

        ans = result
    return ans
permutations([1, 2, 2])
> [[1, 2, 2], [2, 1, 2], [2, 2, 1]]

person Hello.World    schedule 08.03.2019

def permuteArray (arr):

    arraySize = len(arr)

    permutedList = []

    if arraySize == 1:
        return [arr]

    i = 0

    for item in arr:

        for elem in permuteArray(arr[:i] + arr[i + 1:]):
            permutedList.append([item] + elem)

        i = i + 1    

    return permutedList

Я намеревался не исчерпать все возможности новой линейки, чтобы сделать ее в какой-то мере уникальной.

person Dritte Saskaita    schedule 04.06.2020

на случай, если кому-то понравится этот уродливый однострочник (хотя работает только для строк):

def p(a):
    return a if len(a) == 1 else [[a[i], *j] for i in range(len(a)) for j in p(a[:i] + a[i + 1:])]
person Michael Hodel    schedule 10.12.2020

для Python мы можем использовать itertools и импортировать как перестановки, так и комбинации, чтобы решить вашу проблему

from itertools import product, permutations
A = ([1,2,3])
print (list(permutations(sorted(A),2)))
person Bharatwaja    schedule 08.09.2015

person    schedule
comment
Некоторое объяснение улучшило бы этот ответ. - person chux - Reinstate Monica; 22.10.2020