web-dev-qa-db-fra.com

Trouver toutes les permutations possibles d'une chaîne donnée en python

J'ai une ficelle. Je veux générer toutes les permutations à partir de cette chaîne en modifiant l'ordre des caractères qu'elle contient. Par exemple, dites:

x='stack'

ce que je veux, c'est une liste comme celle-ci,

l=['stack','satck','sackt'.......]

Actuellement, je parcours la liste des acteurs, choisis 2 lettres au hasard, les transposant pour former une nouvelle chaîne et l'ajoutant à la définition de la distribution de l. Sur la base de la longueur de la chaîne, je calcule le nombre de permutations possibles et continue les itérations jusqu'à ce que la taille définie atteigne la limite .. Il doit exister un meilleur moyen de le faire. 

61
Nihar Sarangi

Le module itertools a une méthode utile appelée permutations (). La documentation dit:

itertools.permutations (iterable [ r])

Renvoie les permutations successives des éléments de longueur dans l'itérable.

Si r n'est pas spécifié ou est None, alors r est défini par défaut sur la longueur du fichier itérables et toutes les permutations de pleine longueur possibles sont générées.

Les permutations sont émises dans l'ordre de tri lexicographique. Donc, si l'entrée iterable est trié, les n-uplets de permutation seront produits en triés ordre.

Vous devrez cependant joindre vos lettres permutées sous forme de chaînes.

>>> from itertools import permutations
>>> perms = [''.join(p) for p in permutations('stack')]
>>> perms

['stack', 'stakc', 'stcak', 'stcka', 'stkac', 'stkca', 'satck', 'satkc', 'sactk', 'sackt', 'saktc', 'sakct', 'sctak', 'sctka', 'scatk', 'scakt', 'sckta', 'sckat', 'sktac', 'sktca', 'skatc', 'skact', 'skcta', 'skcat', 'tsack', 'tsakc', 'tscak', 'tscka', 'tskac', 'tskca', 'tasck', 'taskc', 'tacsk', 'tacks', 'taksc', 'takcs', 'tcsak', 'tcska', 'tcask', 'tcaks', 'tcksa', 'tckas', 'tksac', 'tksca', 'tkasc', 'tkacs', 'tkcsa', 'tkcas', 'astck', 'astkc', 'asctk', 'asckt', 'asktc', 'askct', 'atsck', 'atskc', "atcsk", "atcks", "atksc", "atkcs", "acstk", "acskt", "actk", 'actks', 'ackst', 'ackts', 'akstc', 'aksct', 'aktsc', 'aktcs', 'akcst', 'akcts', 'cstak', 'cstka', 'csatk', 'csakt', 'cskta', "cskat", "ctsak", "ctska", "ctask", "ctaks", "ctksa", "ctkas", 'castk', 'caskt', 'catsk', 'catks', 'cakst', 'cakts', 'cksta', 'cksat', 'cktsa', 'cktas', 'ckast', 'ckats', 'kstac', 'kstca', 'ksatc', 'ksact', 'kscta', 'kscat', 'ktsac', 'ktsca', 'ktasc', 'ktacs', 'ktcsa', 'ktcas', 'kastc', 'kasct', 'katsc', 'katcs', 'kacst', 'kacts', 'kcsta', 'kcsat', 'kctsa', 'kctas', 'kcast', 'kcats']

Si vous vous trouvez troublé par des doublons, essayez d’ajuster vos données dans une structure ne contenant pas de doublons comme un set:

>>> perms = [''.join(p) for p in permutations('stacks')]
>>> len(perms)
720
>>> len(set(perms))
360

Merci à @pst d'avoir signalé que ce n'est pas ce que nous considérions traditionnellement comme une transtypage, mais plutôt comme un appel au constructeur set().

106
machine yearning

Vous pouvez obtenir tous les N! permutations sans beaucoup de code

def permutations(string, step = 0):

    # if we've gotten to the end, print the permutation
    if step == len(string):
        print "".join(string)

    # everything to the right of step has not been swapped yet
    for i in range(step, len(string)):

        # copy the string (store as array)
        string_copy = [character for character in string]

        # swap the current index with the step
        string_copy[step], string_copy[i] = string_copy[i], string_copy[step]

        # recurse on the portion of the string that has not been swapped yet (now it's index will begin with step + 1)
        permutations(string_copy, step + 1)
30
illerucis

Les utilisateurs de débordement de pile ont déjà publié des solutions solides, mais je voulais montrer une autre solution. Celui-ci je trouve plus intuitif

L'idée est que pour une chaîne donnée: on peut récurse par l'algorithme (pseudo code):

permutations = char + permutations (string - char) pour char dans la chaîne

J'espère que ça aide quelqu'un!

def permutations(string):
    """Create all permutations of a string with non-repeating characters
    """
    permutation_list = []
    if len(string) == 1:
        return [string]
    else:
        for char in string:
            [permutation_list.append(char + a) for a in permutations(string.replace(char, ""))]
    return permutation_list
6
BushMinusZero

Voici une autre approche différente de celle affichée par @Adriano et @illerucis. Cela a un meilleur temps d'exécution, vous pouvez le vérifier vous-même en mesurant le temps:

def removeCharFromStr(str, index):
    endIndex = index if index == len(str) else index + 1
    return str[:index] + str[endIndex:]

# 'ab' -> a + 'b', b + 'a'
# 'abc' ->  a + bc, b + ac, c + ab
#           a + cb, b + ca, c + ba
def perm(str):
    if len(str) <= 1:
        return {str}
    permSet = set()
    for i, c in enumerate(str):
        newStr = removeCharFromStr(str, i)
        retSet = perm(newStr)
        for elem in retSet:
            permSet.add(c + elem)
    return permSet

Pour une chaîne arbitraire "dadffddxcf", il a fallu 1,336 seconde pour la bibliothèque de permutation, 9,125 secondes pour cette implémentation et 16,357 secondes pour les versions de @ Adriano et @illerucis. Bien sûr, vous pouvez toujours l'optimiser.

5
Rooky

Voici une fonction simple pour renvoyer des permutations uniques:

def permutations(string):
    if len(string) == 1:
        return string

    recursive_perms = []
    for c in string:
        for perm in permutations(string.replace(c,'',1)):
            revursive_perms.append(c+perm)

    return set(revursive_perms)
5
ArashkG

itertools.permutations est bon, mais il ne traite pas bien les séquences qui contiennent des éléments répétés. En effet, en interne, elle permute les index de séquence et ignore les valeurs des éléments de séquence.

Bien sûr, il est possible de filtrer la sortie de itertools.permutations dans un ensemble pour éliminer les doublons, mais cela perd du temps à générer ces doublons, et s'il y a plusieurs éléments répétés dans la séquence de base, il y aura lots de doublons. . De plus, utiliser une collection pour conserver les résultats gaspille de la RAM, annulant ainsi l'avantage d'utiliser un itérateur.

Heureusement, il existe des approches plus efficaces. Le code ci-dessous utilise l'algorithme du mathématicien indien du 14ème siècle, Narayana Pandita, qui peut être trouvé dans l'article de Wikipedia sur Permutation . Cet ancien algorithme est toujours l’un des moyens les plus rapides connus pour générer des permutations dans l’ordre. Il est assez robuste, car il gère correctement les permutations contenant des éléments répétés.

def lexico_permute_string(s):
    ''' Generate all permutations in lexicographic order of string `s`

        This algorithm, due to Narayana Pandita, is from
        https://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order

        To produce the next permutation in lexicographic order of sequence `a`

        1. Find the largest index j such that a[j] < a[j + 1]. If no such index exists, 
        the permutation is the last permutation.
        2. Find the largest index k greater than j such that a[j] < a[k].
        3. Swap the value of a[j] with that of a[k].
        4. Reverse the sequence from a[j + 1] up to and including the final element a[n].
    '''

    a = sorted(s)
    n = len(a) - 1
    while True:
        yield ''.join(a)

        #1. Find the largest index j such that a[j] < a[j + 1]
        for j in range(n-1, -1, -1):
            if a[j] < a[j + 1]:
                break
        else:
            return

        #2. Find the largest index k greater than j such that a[j] < a[k]
        v = a[j]
        for k in range(n, j, -1):
            if v < a[k]:
                break

        #3. Swap the value of a[j] with that of a[k].
        a[j], a[k] = a[k], a[j]

        #4. Reverse the tail of the sequence
        a[j+1:] = a[j+1:][::-1]

for s in lexico_permute_string('data'):
    print(s)

sortie

aadt
aatd
adat
adta
atad
atda
daat
data
dtaa
taad
tada
tdaa

Bien sûr, si vous souhaitez rassembler les chaînes cédées dans une liste, vous pouvez le faire.

list(lexico_permute_string('data'))

ou dans les versions récentes de Python:

[*lexico_permute_string('data')]
3
PM 2Ring

pourquoi ne faites-vous pas simple:

from itertools import permutations
perms = [''.join(p) for p in permutations(['s','t','a','c','k'])]
print perms
print len(perms)
print len(set(perms))

vous n'obtenez aucun doublon comme vous pouvez le voir:

 ['stack', 'stakc', 'stcak', 'stcka', 'stkac', 'stkca', 'satck', 'satkc', 
'sactk', 'sackt', 'saktc', 'sakct', 'sctak', 'sctka', 'scatk', 'scakt', 'sckta',
 'sckat', 'sktac', 'sktca', 'skatc', 'skact', 'skcta', 'skcat', 'tsack', 
'tsakc', 'tscak', 'tscka', 'tskac', 'tskca', 'tasck', 'taskc', 'tacsk', 'tacks', 
'taksc', 'takcs', 'tcsak', 'tcska', 'tcask', 'tcaks', 'tcksa', 'tckas', 'tksac', 
'tksca', 'tkasc', 'tkacs', 'tkcsa', 'tkcas', 'astck', 'astkc', 'asctk', 'asckt', 
'asktc', 'askct', 'atsck', 'atskc', 'atcsk', 'atcks', 'atksc', 'atkcs', 'acstk', 
'acskt', 'actsk', 'actks', 'ackst', 'ackts', 'akstc', 'aksct', 'aktsc', 'aktcs', 
'akcst', 'akcts', 'cstak', 'cstka', 'csatk', 'csakt', 'cskta', 'cskat', 'ctsak', 
'ctska', 'ctask', 'ctaks', 'ctksa', 'ctkas', 'castk', 'caskt', 'catsk', 'catks', 
'cakst', 'cakts', 'cksta', 'cksat', 'cktsa', 'cktas', 'ckast', 'ckats', 'kstac', 
'kstca', 'ksatc', 'ksact', 'kscta', 'kscat', 'ktsac', 'ktsca', 'ktasc', 'ktacs', 
'ktcsa', 'ktcas', 'kastc', 'kasct', 'katsc', 'katcs', 'kacst', 'kacts', 'kcsta', 
'kcsat', 'kctsa', 'kctas', 'kcast', 'kcats']
    120
    120
    [Finished in 0.3s]
2
Vincenzo

Voici une autre façon de faire la permutation de chaîne avec un code minimal . Nous créons essentiellement une boucle, puis nous continuons à échanger deux caractères à la fois, Dans la boucle, nous aurons la récursivité. Notez que nous imprimons uniquement lorsque les indexeurs atteignent la longueur de notre chaîne . Exemple: ABC I pour notre point de départ et notre paramètre récursivité J pour notre boucle. 

voici une aide visuelle comment ça marche de gauche à droite de haut en bas (est l'ordre de permutation)

 enter image description here

le code :

def permute(data, i, length): 
    if i==length: 
        print(''.join(data) )
    else: 
        for j in range(i,length): 
            #swap
            data[i], data[j] = data[j], data[i] 
            permute(data, i+1, length) 
            data[i], data[j] = data[j], data[i]  


string = "ABC"
n = len(string) 
data = list(string) 
permute(data, 0, n)
2
grepit

Voici une version légèrement améliorée du code de illerucis pour renvoyer une liste de toutes les permutations d'une chaîne s avec des caractères distincts (pas nécessairement dans l'ordre de tri lexicographique), sans utiliser itertools:

def get_perms(s, i=0):
    """
    Returns a list of all (len(s) - i)! permutations t of s where t[:i] = s[:i].
    """
    # To avoid memory allocations for intermediate strings, use a list of chars.
    if isinstance(s, str):
        s = list(s)

    # Base Case: 0! = 1! = 1.
    # Store the only permutation as an immutable string, not a mutable list.
    if i >= len(s) - 1:
        return ["".join(s)]

    # Inductive Step: (len(s) - i)! = (len(s) - i) * (len(s) - i - 1)!
    # Swap in each suffix character to be at the beginning of the suffix.
    perms = get_perms(s, i + 1)
    for j in range(i + 1, len(s)):
        s[i], s[j] = s[j], s[i]
        perms.extend(get_perms(s, i + 1))
        s[i], s[j] = s[j], s[i]
    return perms
1
Adriano
def permute(seq):
    if not seq:
        yield seq
    else:
        for i in range(len(seq)):
            rest = seq[:i]+seq[i+1:]
            for x in permute(rest):
                yield seq[i:i+1]+x

print(list(permute('stack')))
1
Srivastava
1
Brian Cain

Solution plus simple utilisant des permutations.

from itertools import permutations

def stringPermutate(s1):
    length=len(s1)
    if length < 2:
        return s1

    perm = [''.join(p) for p in permutations(s1)]

    return set(perm)
0
Nelson Guest

Voici une version très simple du générateur:

def find_all_permutations(s, curr=[]):
    if len(s) == 0:
        yield curr
    else:
        for i, c in enumerate(s):
            for combo in find_all_permutations(s[:i]+s[i+1:], curr + [c]):
                yield "".join(combo)

Je pense que ce n'est pas si mal!

0
Gritty Kitty
from itertools import permutations
perms = [''.join(p) for p in permutations('ABC')]

perms = [''.join(p) for p in permutations('stack')]
0
Satish Kumar
def permute_all_chars(list, begin, end):

    if (begin == end):
        print(list)
        return

    for current_position in range(begin, end + 1):
        list[begin], list[current_position] = list[current_position], list[begin]
        permute_all_chars(list, begin + 1, end)
        list[begin], list[current_position] = list[current_position], list[begin]


given_str = 'ABC'
list = []
for char in given_str:
    list.append(char)
permute_all_chars(list, 0, len(list) -1)
0
def perm(string):
   res=[]
   for j in range(0,len(string)):
       if(len(string)>1):
           for i in perm(string[1:]):
               res.append(string[0]+i)
       else:
           return [string];
       string=string[1:]+string[0];
   return res;
l=set(perm("abcde"))

C'est un moyen de générer des permutations avec récursivité. Vous pouvez facilement comprendre le code en prenant les chaînes 'a', 'ab' & 'abc' comme entrée.

Vous obtenez tout N! permutations avec cela, sans doublons.

0
Jasser
def f(s):
  if len(s) == 2:
    X = [s, (s[1] + s[0])]
      return X
else:
    list1 = []
    for i in range(0, len(s)):
        Y = f(s[0:i] + s[i+1: len(s)])
        for j in Y:
            list1.append(s[i] + j)
    return list1
s = raw_input()
z = f(s)
print z
0
Ritabrata Sanyal

Ce programme n'élimine pas les doublons, mais je pense que c'est l'une des approches les plus efficaces:

s=raw_input("Enter a string: ")
print "Permutations :\n",s
size=len(s)
lis=list(range(0,size))
while(True):
    k=-1
    while(k>-size and lis[k-1]>lis[k]):
        k-=1
    if k>-size:
        p=sorted(lis[k-1:])
        e=p[p.index(lis[k-1])+1]
        lis.insert(k-1,'A')
        lis.remove(e)
        lis[lis.index('A')]=e
        lis[k:]=sorted(lis[k:])
        list2=[]
        for k in lis:
                list2.append(s[k])
        print "".join(list2)
    else:
                break
0
Nagaraju Chukkala

Tout le monde aime l'odeur de leur propre code. Juste partager celui que je trouve le plus simple:

def get_permutations(Word):
    if len(Word) == 1:
        yield Word

    for i, letter in enumerate(Word):
        for perm in get_permutations(Word[:i] + Word[i+1:]):
            yield letter + perm
0
r_2

Encore une autre initiative et solution récursive. L'idée est de sélectionner une lettre en tant que pivot, puis de créer un mot.

# for a string with length n, there is a factorial n! permutations
alphabet = 'abc'
starting_perm = ''
# with recursion
def premuate(perm, alphabet):
    if not alphabet: # we created one Word by using all letters in the alphabet
        print(perm + alphabet)
    else:
        for i in range(len(alphabet)): # iterate over all letters in the alphabet
            premuate(perm + alphabet[i], alphabet[0:i] + alphabet[i+1:]) # chose one letter from the alphabet

# call it            
premuate(starting_perm, alphabet)

Sortie:

abc
acb
bac
bca
cab
cba
0
Faroq AL-Tam