web-dev-qa-db-fra.com

Comment générer toutes les permutations d'une liste en Python

Comment générer toutes les permutations d'une liste en Python, indépendamment du type des éléments de cette liste?

Par exemple:

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]
481
Ricardo Reyes

_ {À partir de Python 2.6} _ (et si vous êtes sur Python 3), vous disposez d'un outil bibliothèque standard pour cela: itertools.permutations .

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

Si vous utilisez un ancien Python (<2.6) pour une raison quelconque ou êtes simplement curieux de savoir comment cela fonctionne, voici une approche intéressante, tirée de http://code.activestate.com/ recettes/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:]

Quelques approches alternatives sont répertoriées dans la documentation de itertools.permutations. En voici un:

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

Et un autre, basé sur 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)
371
Eli Bendersky

Et dans Python 2.6 à partir de:

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

(retourné en tant que générateur. Utilisez list(permutations(l)) pour revenir en tant que liste.)

322
Brian

Le code suivant avec Python 2.6 et supérieur UNIQUEMENT

Commencez par importer itertools:

import itertools

Permutation (ordre importe):

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

Combinaison (l'ordre importe PAS):

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

Produit cartésien (avec plusieurs itérables):

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

Produit cartésien (avec un itératif et lui-même):

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)]
254
e-satis
def permutations(head, tail=''):
    if len(head) == 0: print tail
    else:
        for i in range(len(head)):
            permutations(head[0:i] + head[i+1:], tail+head[i])

appelé comme:

permutations('abc')
36
kx2k

Cette solution implémente un générateur, pour éviter de conserver toutes les permutations en mémoire:

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
21
Ricardo Reyes
#!/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])

Sortie:

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

Comme j'échange le contenu de la liste, il faut un type de séquence mutable en entrée. Par exemple. perm(list("ball")) fonctionnera et perm("ball") ne fonctionnera pas car vous ne pouvez pas modifier une chaîne. 

Cette implémentation Python est inspirée de l’algorithme présenté dans le livre Computer Algorithms de Horowitz, Sahni et Rajasekeran .

19
Silveira Neto

Le code suivant est une permutation sur place d'une liste donnée, implémentée en tant que générateur. Etant donné qu'elle ne renvoie que des références à la liste, celle-ci ne doit pas être modifiée en dehors du générateur ..__ La solution est non récursive et utilise donc une mémoire faible. Fonctionne bien aussi avec plusieurs copies d’éléments de la liste de saisie.

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 __== '__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
14
Ber

Une manière assez évidente à mon avis pourrait aussi être:

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
13
tzwenn

Dans un style fonctionnel

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

Le résultat:

[[0, 1, 2], [1, 0, 2], [1, 2, 0], [0, 2, 1], [2, 0, 1], [2, 1, 0]]
11
Paolo
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

Sortie:

[
    [1, 2.0, 'three'], 
    [1, 'three', 2.0], 
    [2.0, 1, 'three'], 
    [2.0, 'three', 1], 
    ['three', 1, 2.0], 
    ['three', 2.0, 1]
]
10
zmk

J'ai utilisé un algorithme basé sur le système de nombres factoriels - Pour une liste de longueur n, vous pouvez assembler chaque permutation, élément par élément, en choisissant parmi les éléments restants à chaque étape. Vous avez n choix pour le premier élément, n-1 pour le second et un seul pour le dernier, vous pouvez donc utiliser les chiffres d'un nombre dans le système de nombres factoriels comme index. Ainsi, les nombres de 0 à n! -1 correspondent à toutes les permutations possibles dans l'ordre lexicographique.

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

sortie:

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

Cette méthode est non-récursive, mais elle est légèrement plus lente sur mon ordinateur et xrange génère une erreur lorsque n! est trop grand pour être converti en un entier long (n = 13 pour moi). C’était suffisant quand j’en avais besoin, mais ce n’est pas un itertools.permutations à long terme.

8
timeeeee

Notez que cet algorithme a une complexité temporelle n factorial, où n est la longueur de la liste d'entrée

Imprimez les résultats sur le pouce:

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

Exemple: 

permutation([1,2,3])

Sortie:

[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]
7
Chen Xie

On peut en effet parcourir le premier élément de chaque permutation, comme dans la réponse de tzwenn; Je préfère écrire cette solution de cette façon:

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

Cette solution est environ 30% plus rapide, apparemment grâce à la récursivité se terminant par len(elements) <= 1 au lieu de 0. Elle est également beaucoup plus efficace en termes de mémoire, car elle utilise une fonction de générateur (via yield), comme dans la solution de Riccardo Reyes.

6
Eric O Lebigot

Ceci est inspiré de l'implémentation Haskell utilisant la compréhension de liste: 

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
5
piggybox

Pour la performance, une solution numpy inspirée de 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, :]

Copier de grands blocs de mémoire fait gagner du temps - 20 fois plus rapide que 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
4
B. M.

Pardonnez mon analphabétisme python car je ne proposerai pas la solution en python . Comme je ne sais pas quelle méthode python 2.6 utilise pour générer les permutations et que celui d’éliben ressemble à la génération de permutation de Johnson-Trotter, vous pouvez rechercher un articlein Wikipedia sur Les permutations et leur génération qui ressemble assez à une fonction non encombrée dans papier de Myrvold et Ruskey .

Il me semble que cela pourrait être utilisé dans un générateur de la même manière que dans d'autres réponses pour réduire considérablement les besoins en mémoire. Rappelez-vous simplement que les permutations ne seront pas dans l'ordre lexicographique.

3
Eonwe
from __future__ import print_function

def perm(n):
    p = []
    for i in range(0,n+1):
        p.append(i)
    while True:
        for i in range(1,n+1):
            print(p[i], end=' ')
        print("")
        i = n - 1
        found = 0
        while (not found and i>0):
            if p[i]<p[i+1]:
                found = 1
            else:
                i = i - 1
        k = n
        while p[i]>p[k]:
            k = k - 1
        aux = p[i]
        p[i] = p[k]
        p[k] = aux
        for j in range(1,(n-i)/2+1):
            aux = p[i+j]
            p[i+j] = p[n-j+1]
            p[n-j+1] = aux
        if not found:
            break

perm(5)
3
Adrian Statescu
def pzip(c, seq):
    result = []
    for item in seq:
        for i in range(len(item)+1):
            result.append(item[i:]+c+item[:i])
    return result


def perm(line):
    seq = [c for c in line]
    if len(seq) <=1 :
        return seq
    else:
        return pzip(seq[0], perm(seq[1:]))
3
manish kumar

Voici un algorithme qui fonctionne sur une liste sans créer de nouvelles listes intermédiaires similaires à la solution de Ber sur 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

Vous pouvez essayer le code par vous-même ici: http://repl.it/J9v

3
cdiggins

La beauté de la récursion:

>>> 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']
3
darxtrix

Cet algorithme est le plus efficace, il évite le passage de tableaux et la manipulation dans les appels récursifs, fonctionne en 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()

Usage:

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

Générer toutes les permutations possibles

J'utilise 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

Cas de test:

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

UNE AUTRE APPROCHE (sans bibliothèque)

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

L'entrée peut être une chaîne ou une liste

print(permutation('abcd'))
print(permutation(['a', 'b', 'c', 'd']))
1
Tatsu

Je vois un beaucoup d'itération qui se passe dans ces fonctions récursives, pas exactement pur récursivité ...

donc, pour ceux qui ne peuvent pas même suivre une seule boucle, voici une solution totalement récursive, totalement inutile et totalement inutile.

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])
1
Karo Castro-Wunsch

Une autre solution:

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])
1
anhldbk

Pour vous faire économiser des heures possibles de recherche et d'expérimentation, voici la solution de permutaions non récursive en Python qui fonctionne également avec Numba (à partir de v. 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]]

Pour donner une impression de performance:

%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

Utilisez donc cette version uniquement si vous devez l’appeler à partir de la fonction njitted, sinon préférez l’implémentation d’itertools.

1
Anatoly Alekseev
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')

Sortie: ['abc', 'acb', 'bac', 'bca', 'cab', 'cba']

0

Utiliser 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]]

0
Hello.World

Ma solution 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") )
0
abelenky