Hoe genereer je alle permutaties van een lijst?

Hoe genereer je alle permutaties van een lijst in Python, onafhankelijk van het type elementen in die lijst?

Bijvoorbeeld:

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]

Antwoord 1, autoriteit 100%

Er is hiervoor een functie in de standaardbibliotheek: itertools.permutations.

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

Als je het om de een of andere reden zelf wilt implementeren of gewoon nieuwsgierig bent naar hoe het werkt, is hier een leuke benadering, overgenomen van 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:]

Een aantal alternatieve benaderingen staan vermeld in de documentatie van itertools.permutations. Hier is er een:

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

En nog een, gebaseerd op 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)

Antwoord 2, autoriteit 56%

En vanaf Python 2.6en later:

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

(teruggegeven als een generator. Gebruik list(permutations(l))om terug te keren als een lijst.)


Antwoord 3, autoriteit 48%

ALLEEN de volgende code met Python 2.6 en hoger

Importeer eerst itertools:

import itertools

Permutatie (volgorde is belangrijk):

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

Combinatie (volgorde maakt niet uit):

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

Cartesiaans product (met verschillende 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)]

Cartesiaans product (met één iterabel en zichzelf):

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

Antwoord 4, autoriteit 8%

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

aangeroepen als:

permutations('abc')

Antwoord 5, autoriteit 6%

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

Uitvoer:

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

Omdat ik de inhoud van de lijst verwissel, is een veranderlijk sequentietype als invoer vereist. bijv. perm(list("ball"))werkt en perm("ball")niet omdat je een string niet kunt veranderen.

Deze Python-implementatie is geïnspireerd op het algoritme dat wordt gepresenteerd in het boek Computer Algorithms van Horowitz, Sahni en Rajasekeran.


Antwoord 6, autoriteit 4%

Deze oplossing implementeert een generator, om te voorkomen dat alle permutaties in het geheugen worden vastgehouden:

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

Antwoord 7, autoriteit 3%

In een functionele stijl

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

Het resultaat:

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

Antwoord 8, autoriteit 3%

De volgende code is een in-place permutatie van een gegeven lijst, geïmplementeerd als een generator. Omdat het alleen verwijzingen naar de lijst retourneert, mag de lijst niet buiten de generator worden gewijzigd.
De oplossing is niet-recursief en gebruikt daarom weinig geheugen. Werkt ook goed met meerdere exemplaren van elementen in de invoerlijst.

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

Antwoord 9, autoriteit 2%

Een vrij voor de hand liggende manier zou naar mijn mening ook kunnen zijn:

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

Antwoord 10, autoriteit 2%

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

Uitvoer:

[
    [1, 2.0, 'three'], 
    [1, 'three', 2.0], 
    [2.0, 1, 'three'], 
    [2.0, 'three', 1], 
    ['three', 1, 2.0], 
    ['three', 2.0, 1]
]

Antwoord 11

Ik heb een algoritme gebruikt op basis van het faculteitsnummersysteem– Voor een lijst met lengte n, je kunt elke permutatie item voor item samenstellen, door te selecteren uit de items die in elke fase zijn achtergelaten. Je hebt n keuzes voor het eerste item, n-1 voor het tweede en slechts één voor het laatste, dus je kunt de cijfers van een getal in het faculteitsnummersysteem als index gebruiken. Op deze manier komen de getallen 0 tot en met n!-1 overeen met alle mogelijke permutaties in lexicografische volgorde.

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

uitvoer:

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

Deze methode is niet-recursief, maar is iets langzamer op mijn computer en xrange geeft een foutmelding als n! is te groot om te worden geconverteerd naar een C-lang geheel getal (n=13 voor mij). Het was genoeg toen ik het nodig had, maar het is bij lange na geen itertools.permutaties.


Antwoord 12

Regelmatige implementatie (geen opbrengst – doet alles in het geheugen):

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

Opbrengstimplementatie:

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]

Het basisidee is om alle elementen in de array voor de 1e positie te doorlopen, en dan in de 2e positie alle andere elementen te doorlopen zonder het gekozen element voor de 1e, enz. U kunt dit doen met recursie, waarbij het stopcriterium een array van 1 element bereikt – in welk geval je die array retourneert.

voer hier de afbeeldingsbeschrijving in


Antwoord 13

Merk op dat dit algoritme een n factorialtijdcomplexiteit heeft, waarbij nde lengte is van de invoerlijst

Druk de resultaten af tijdens het hardlopen:

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

Voorbeeld:

permutation([1,2,3])

Uitvoer:

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

Antwoord 14

Je kunt inderdaad het eerste element van elke permutatie herhalen, zoals in het antwoord van twenn. Het is echter efficiënter om deze oplossing op deze manier te schrijven:

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

Deze oplossing is ongeveer 30 % sneller, blijkbaar dankzij de recursie die eindigt op len(elements) <= 1in plaats van 0.
Het is ook veel geheugenefficiënter, omdat het een generatorfunctie gebruikt (via yield), zoals in de oplossing van Riccardo Reyes.


Antwoord 15

Dit is geïnspireerd op de Haskell-implementatie met behulp van lijstbegrip:

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

Antwoord 16

Voor prestaties, een numpy oplossing geïnspireerd door 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, :]

Het kopiëren van grote blokken geheugen bespaart tijd –
het is 20x sneller dan 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

Antwoord 17

Disclaimer: vormeloze plug door pakketauteur. 🙂

Het trotter-pakket verschilt van de meeste implementaties doordat het pseudolijsten genereert die niet permutaties bevatten maar eerder mappings tussen permutaties en respectievelijke posities in een volgorde beschrijven, waardoor het mogelijk is om met zeer grote ‘lijsten’ van permutaties te werken, zoals getoond in deze demodie vrij onmiddellijke bewerkingen en opzoekingen uitvoert in een pseudo-lijst die alle permutaties van de letters in het alfabet ‘bevat’, zonder meer geheugen of verwerking te gebruiken dan een typische webpagina .

In ieder geval kunnen we het volgende doen om een lijst met permutaties te genereren.

import trotter
my_permutations = trotter.Permutations(3, [1, 2, 3])
print(my_permutations)
for p in my_permutations:
    print(p)

Uitvoer:

Een pseudo-lijst met 6 3-permutaties van [1, 2, 3].
[1, 2, 3]
[1, 3, 2]
[3, 1, 2]
[3, 2, 1]
[2, 3, 1]
[2, 1, 3]

Antwoord 18

EEN ANDERE BENADERING (zonder libs)

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

Invoer kan een tekenreeks of een lijst zijn

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

Antwoord 19

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)

Antwoord 20

Hier is een algoritme dat op een lijst werkt zonder nieuwe tussenliggende lijsten te maken, vergelijkbaar met de oplossing van Ber op https://stackoverflow.com/ een/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

Je kunt de code hier zelf uitproberen: http://repl.it/J9v


Antwoord 21

De schoonheid van recursie:

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

Antwoord 22

Dit algoritme is het meest effectief, het vermijdt het doorgeven en manipuleren van arrays in recursieve aanroepen, werkt 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()

Gebruik:

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)

Antwoord 23

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

Antwoord 24

Genereer alle mogelijke permutaties

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

Testgevallen:

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

Antwoord 25

Om jullie mogelijke uren aan zoeken en experimenteren te besparen, is hier de niet-recursieve permutatie-oplossing in Python die ook werkt met Numba (vanaf 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]]

Om een indruk te geven van de prestaties:

%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

Gebruik deze versie dus alleen als je het vanuit de njitted-functie moet aanroepen, anders geef je de voorkeur aan itertools-implementatie.


Antwoord 26

Ik zie een veeliteratie binnen deze recursieve functies, niet echt purerecursie…

dus voor degenen onder u die zich niet aan een enkele lus kunnen houden, hier is een grove, totaal onnodige volledig recursieve oplossing

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

Antwoord 27

Een andere oplossing:

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

Antwoord 28

Hoe dan ook, we zouden de sympybibliotheek kunnen gebruiken, ook ondersteuning voor multiset permutaties

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

Antwoord is sterk geïnspireerd door Verkrijg alle permutaties van een numpy array


Antwoord 29

Dit is de asymptotisch optimale manier O(n*n!) om permutaties te genereren na de initiële sortering.

Er zijn n! hoogstens permutaties en hasNextPermutation(..) draait in O(n) tijdcomplexiteit

In 3 stappen,

  1. Zoek de grootste j zodat a[j] kan worden vergroot
  2. Verhoog a[j] met de kleinst haalbare hoeveelheid
  3. Vind lexicografisch de minste manier om de nieuwe a[0..j] uit te breiden
'''
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}")

Antwoord 30

Mijn Python-oplossing:

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

Other episodes