it-swarm.com.de

Generieren aller Permutationen einer Liste in Python

Wie generieren Sie alle Permutationen einer Liste in Python, unabhängig von der Art der Elemente in dieser Liste?

Zum Beispiel:

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

Beginnend mit Python 2.6 (und wenn Sie mit Python 3 arbeiten) haben Sie ein standard-library - Werkzeug für dieses: itertools.permutations .

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

Wenn Sie aus irgendeinem Grund ein älteres Python (<2.6) verwenden oder einfach nur wissen möchten, wie es funktioniert, haben wir hier einen schönen Ansatz aus 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:]

In der Dokumentation von itertools.permutations sind einige alternative Ansätze aufgeführt. Hier ist eins:

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

Und noch eine, basierend auf 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

Und in Python 2.6 und weiter:

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

(Wird als Generator zurückgegeben. Verwenden Sie list(permutations(l)), um als Liste zurückzukehren.)

322
Brian

Der folgende Code mit Python 2.6 und höher NUR

Importieren Sie zuerst itertools:

import itertools

Permutation (Reihenfolge zählt):

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

Kombination (Reihenfolge spielt keine Rolle):

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

Kartesisches Produkt (mit mehreren Iterables):

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

Kartesisches Produkt (mit einem iterierbaren und sich selbst):

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

genannt als:

permutations('abc')
36
kx2k

Diese Lösung implementiert einen Generator, um zu vermeiden, dass alle Permutationen im Speicher gespeichert werden:

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

Ausgabe:

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

Da ich den Inhalt der Liste austausche, ist ein veränderbarer Sequenztyp als Eingabe erforderlich. Z.B. perm(list("ball")) wird funktionieren und perm("ball") nicht, weil Sie eine Zeichenfolge nicht ändern können. 

Diese Python-Implementierung basiert auf dem Algorithmus, der im Buch Computer Algorithms von Horowitz, Sahni und Rajasekeran vorgestellt wird.

19
Silveira Neto

Der folgende Code ist eine In-Place-Permutation einer gegebenen Liste, die als Generator implementiert ist. Da nur Verweise auf die Liste zurückgegeben werden, sollte die Liste nicht außerhalb des Generators geändert werden. Die Lösung ist nicht rekursiv und verwendet daher wenig Speicher. Funktioniert auch mit mehreren Kopien von Elementen in der Eingabeliste.

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

Ein ganz naheliegender Weg könnte meiner Meinung nach auch sein:

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

In einem funktionalen Stil

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

Das Ergebnis:

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

Ausgabe:

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

Ich habe einen Algorithmus verwendet, der auf dem faktoriellen Zahlensystem basiert . Für eine Liste der Länge n können Sie jedes Permutationselement nach Element zusammenstellen und aus den Elementen auswählen, die in jeder Stufe übrig sind. Sie haben n Auswahlmöglichkeiten für das erste Element, n-1 für das zweite und nur eine für das letzte Element. Sie können also die Ziffern einer Zahl im faktoriellen Zahlensystem als Indizes verwenden. Auf diese Weise entsprechen die Zahlen 0 bis n! -1 allen möglichen Permutationen in lexikographischer Reihenfolge.

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

ausgabe:

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

Diese Methode ist nicht rekursiv, aber auf meinem Computer ist sie etwas langsamer und Xrange führt zu einem Fehler, wenn n! ist zu groß, um in eine C-Ganzzahl umgewandelt zu werden (n = 13 für mich). Es war genug, als ich es brauchte, aber es ist kein Werkzeug.

8
timeeeee

Beachten Sie, dass dieser Algorithmus eine n factorial-Zeitkomplexität aufweist, wobei n die Länge der Eingabeliste ist

Drucken Sie die Ergebnisse während des Laufs:

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

Beispiel: 

permutation([1,2,3])

Ausgabe:

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

Man kann in der Tat über das erste Element jeder Permutation iterieren, wie in tzwenns Antwort; Ich ziehe es vor, diese Lösung auf diese Weise zu schreiben:

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

Diese Lösung ist etwa 30% schneller, offenbar dank der Rekursion, die bei len(elements) <= 1 statt 0..__ endet. Sie ist auch viel speichereffizienter, da sie eine Generatorfunktion (über yield) verwendet, wie bei Riccardo Reyes.

6
Eric O Lebigot

Dies basiert auf der Haskell-Implementierung mit Listenverständnis: 

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

Eine leistungsfähige Lösung, die von Knuth , (S22) inspiriert wurde:

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, :]

Das Kopieren großer Speicherblöcke spart Zeit - Es ist 20x schneller als 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.

Verzeihen Sie meinem Python-Analphabetentum, da ich die Lösung nicht in Python anbieten werde .. Da ich nicht weiß, welche Methode Python 2.6 verwendet, um die Permutationen zu generieren und Elibens Methode wie eine Johnson-Trotter-Permutationsgenerierung aussieht, könnten Sie nach einem Artikel suchenin Wikipedia auf Permutationen und ihre Generierung das sieht aus wie eine unrank Funktion in paper von Myrvold und Ruskey .

Es scheint mir, dass dies in einem Generator genauso verwendet werden könnte wie in anderen Antworten, um den Speicherbedarf erheblich zu verringern. Denken Sie daran, dass die Permutationen nicht in lexikographischer Reihenfolge vorliegen.

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

Hier ist ein Algorithmus, der auf einer Liste arbeitet, ohne neue Zwischenlisten zu erstellen, die der Lösung von Ber ähnlich sind, unter 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

Sie können den Code hier selbst ausprobieren: http://repl.it/J9v

3
cdiggins

Die Schönheit der Rekursion:

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

Dieser Algorithmus ist der effektivste. Er vermeidet die Weitergabe von Feldern und die Bearbeitung in rekursiven Aufrufen. Er funktioniert in 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()

Verwendungszweck:

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

Generieren Sie alle möglichen Permutationen

Ich benutze 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

Testfälle:

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

Ein anderer Ansatz (ohne Bibliotheken)

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

Eingabe kann eine Zeichenfolge oder eine Liste sein

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

Ich sehe ein lot von Iteration innerhalb dieser rekursiven Funktionen, nicht genau pure rekursion ...

für diejenigen von Ihnen, die sich nicht an eine einzige Schleife halten können, gibt es eine grobe, völlig unnötige, vollständig rekursive Lösung

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

Eine andere Lösung:

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

Um Ihnen möglichst viele Stunden des Suchens und Experimentierens zu ersparen, hier die nicht-rekursive Permutaions-Lösung in Python, die auch mit Numba (ab Version 0.41) funktioniert:

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

Um einen Eindruck über die Leistung zu vermitteln:

%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

Verwenden Sie diese Version also nur, wenn Sie sie von einer njitted-Funktion aufrufen müssen, ansonsten bevorzugen Sie die Implementierung von itertools.

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

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

0

Counter verwenden

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

Meine Python-Lösung:

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