Пошук усіх можливих перестановок заданого рядка в python


89

У мене є струна. Я хочу згенерувати всі перестановки з цього рядка, змінивши порядок символів у ньому. Наприклад, скажіть:

x='stack'

я хочу такий список,

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

В даний час я переглядаю список акторів рядка, довільно обираючи 2 букви і транспонуючи їх, щоб сформувати новий рядок, і додаю його до встановленого складу літери l. Виходячи з довжини рядка, я обчислюю кількість можливих перестановок і продовжую ітерації, поки встановлений розмір не досягне межі. Для цього повинен бути кращий спосіб.

Відповіді:


143

Модуль itertools має корисний метод, який називається перестановками (). У документації сказано:

itertools.permutations (iterable [, r])

Повертає послідовні перестановки довжини r елементів у ітерабель.

Якщо r не вказано або не має значення None, тоді за замовчуванням r генерується довжина ітерабельного і всі можливі перестановки повної довжини.

Перестановки видаються у лексикографічному порядку сортування. Отже, якщо вхідний ітерабель відсортовано, кортежі перестановок будуть створені у відсортованому порядку.

Вам доведеться об'єднати ваші переставлені літери як рядки.

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

['стек', '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 ']

Якщо вас турбують дублікати, спробуйте вписати дані в структуру без дублікатів, як set:

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

Дякуємо @pst за те, що він зазначив, що це не те, що ми традиційно вважаємо типовим актором, а більше заклик до set()конструктора.


3
Ніт: set(...)не "кидає". Швидше за все, він генерує (і видає) набір, що представляє вхідну колекцію: після створення він не має ніякого зв'язку з вхідною колекцією (і є іншим об'єктом, а не просто іншим видом).

@pst: Хм, я, як правило, не погоджуюсь. Я знаю в Аді чи Паскалі, що акторський склад - це лише новий вид на ті самі біти. Однак, принаймні, з точки зору C, кастинг є відповідним терміном, незалежно від того, змінюєте ви базову структуру даних. Це просто посилається на явне перетворення типу. Будь ласка, поясніть моє непорозуміння, якщо можете.
машина тужить

1
Набір шрифтів . Хоча, як ви зазначаєте, це може відрізнятися від простого погляду, я люблю намагатися тримати концепції розділеними, щоб уникнути плутанини. Я повинен був прямо згадати "примус" у своєму першому коментарі, хоча я б просто розглянув функцію set: list -> set.

1
Я вважаю bool, це функція, яка оцінює значення bool (True / False) залежно від вводу. Я вважаю, що використання "акторського складу" тут хибне та оманливе ...

1
Як цікаве оновлення, документація з тих пір була змінена: Вбудована функція bool () може бути використана для перетворення будь-якого значення в логічне значення , зокрема, а не конвертування. Це сталося у наступному випуску цієї дискусії, що змусило мене повірити, що це обговорення призвело до зміни документів!
машина прагне

45

Ви можете отримати всі N! перестановки без великого коду

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)

хороший. Працює ідеально
kishorer747

1
Я лише трохи його змінив, нам не потрібно міняти
місцями

4
Час роботи - O (n!), Тому що існує n! перестановки.
Аспен

Чому ви використовуєте step == len(string)замість step == len(string) - 1?
tulians

Тому що тоді 2 останні елементи ніколи не поміняли б місцями. Спробуйте "abc", доки b і c не обміняються.
Роман Різен

14

Ось ще один спосіб перестановки рядка з мінімальним кодом. Ми в основному створюємо цикл, а потім продовжуємо міняти місцями два символи одночасно. Усередині циклу буде рекурсія. Зверніть увагу, ми друкуємо лише тоді, коли індексатори досягають довжини нашого рядка. Приклад: ABC i для нашої вихідної точки та наш параметр рекурсії j для нашого циклу

ось наочна довідка про те, як це працює зліва направо зверху вниз (це порядок перестановки)

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

кодекс :

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)

5
Можливо, було б корисно згадати, що це основа парадигми відслідковування .
AruniRC

Більше інформації, однакові / подібні коди: geeksforgeeks.org/... Мені більше подобається ваш приклад, хоча з графічним прикладом;)
CTS_AE

8

Користувачі Stack Overflow вже опублікували кілька сильних рішень, але я хотів показати ще одне рішення. Цей я вважаю більш інтуїтивним

Ідея полягає в тому, що для даного рядка: ми можемо повторити за алгоритмом (псевдокодом):

перестановки = char + перестановки (рядок - char) для char в рядку

Сподіваюся, це комусь допоможе!

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, "", 1))]
    return permutation_list

4
Це не буде працювати у випадках, коли є символи, що повторюються (str.replace). Напр .: rqqx
санджей

Використання: [permutation_list.append (char + a) для перестановок in (string.replace (char, "", 1))]
user3761855

7

Ось проста функція для повернення унікальних перестановок:

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)

6
1. У вас є помилка: revursive_perms-> recursive_perms. 2. Це заощадило б оперативну пам'ять і час, якби recursive_permsце набір, а не список, який ви перетворюєте на набір у операторі return. 3. Ефективніше було б використовувати нарізання рядків замість того, .replaceщоб побудувати аргумент до рекурсивного виклику permutations. 4. Це не гарна ідея використовувати stringяк ім'я змінної, тому що це затінює назву стандартного stringмодуля.
PM 2Кольцо

5

Ось інший підхід, відмінний від того, що розміщували @Adriano та @illerucis. Це має кращий час роботи, ви можете перевірити це самостійно, вимірявши час:

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

Для довільного рядка "dadffddxcf" знадобилося 1,1336 с для бібліотеки перестановок, 9,125 с для цієї реалізації та 16,357 с для версії @ Adriano та @illerucis. Звичайно, ви все ще можете його оптимізувати.


4

itertools.permutationsце добре, але він погано має справу з послідовностями, які містять повторювані елементи. Це тому, що внутрішньо він переставляє індекси послідовності і не враховує значення елементів послідовності.

Звичайно, можна фільтрувати вихідні дані itertools.permutationsнабору для усунення дублікатів, але це все одно витрачає час на створення цих дублікатів, і якщо в базовій послідовності є кілька повторюваних елементів, дублікатів буде багато . Крім того, використання колекції для зберігання результатів витрачає оперативну пам’ять, заперечуючи перевагу використання ітератора.

На щастя, існують більш ефективні підходи. У наведеному нижче коді використовується алгоритм індійського математика XIV століття Нараяна Пандіта, який можна знайти в статті Вікіпедії про Пермутацію . Цей стародавній алгоритм досі є одним із найшвидших відомих способів генерувати перестановки в порядку, і він досить надійний, оскільки належним чином обробляє перестановки, що містять повторювані елементи.

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)

вихід

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

Звичайно, якщо ви хочете зібрати отримані рядки у список, ви можете це зробити

list(lexico_permute_string('data'))

або в останніх версіях Python:

[*lexico_permute_string('data')]

Красиво пояснений.
LMAO

2

чому ви не просто робите:

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

ви не отримуєте дублікатів, як бачите:

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

4
Ні, ви завжди отримуєте дублікати (або гірше), якщо у вас є дві або більше однакових букв. Так було в прикладі @ machineyearning, оскільки він використовував слово стеки замість стека . Це означає: Ваше рішення працює лише для слів із унікальними символами.
Ерік

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

2
Чи можете ви пояснити, чому ваше рішення краще, ніж уже надане?
Ноель Відмер

Я не сказав, що моє рішення краще за інші. Я просто надав своє рішення для цього.
Шрівастава


1

Ось трохи вдосконалена версія коду illerucis для повернення списку всіх перестановок рядка sз різними символами (не обов'язково в лексикографічному порядку сортування), без використання 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

Ще одна ініціатива та рекурсивне рішення. Ідея полягає в тому, щоб вибрати літеру як стержень, а потім створити слово.

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

Вихід:

abc
acb
bac
bca
cab
cba

0

Ось справді проста версія генератора:

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)

Думаю, це не так вже й погано!


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

Це один із способів генерувати перестановки з рекурсією, ви можете легко зрозуміти код, взявши рядки 'a', 'ab' & 'abc' як вхідні дані.

Ви отримуєте все N! перестановки з цим, без дублікатів.


0

Кожен любить запах власного коду. Просто поділившись тим, який я вважаю найпростішим:

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

Ця програма не усуває дублікати, але, на мою думку, це один з найбільш ефективних підходів:

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

Простіше рішення з використанням перестановок.

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

Все можливе слово зі стеком

from itertools import permutations
for i in permutations('stack'):
    print(''.join(i))
permutations(iterable, r=None)

Повертає послідовні перестановки довжини r елементів у ітерабель.

Якщо r не вказано або має значення None, тоді r за замовчуванням створює довжину ітерабельного і генеруються всі можливі перестановки повної довжини.

Перестановки видаються у лексикографічному порядку сортування. Отже, якщо вхідний ітерабель відсортовано, кортежі перестановок будуть створені у відсортованому порядку.

Елементи трактуються як унікальні залежно від їхнього положення, а не від їх вартості. Отже, якщо вхідні елементи унікальні, у кожній перестановці не буде повторюваних значень.


0

Це рекурсивне рішення, n!яке приймає повторювані елементи в рядку

import math

def getFactors(root,num):
    sol = []
    # return condition
    if len(num) == 1:
            return [root+num]
    # looping in next iteration
    for i in range(len(num)):  
        # Creating a substring with all remaining char but the taken in this iteration
        if i > 0:
            rem = num[:i]+num[i+1:]
        else:
            rem = num[i+1:]
        # Concatenating existing solutions with the solution of this iteration
        sol = sol + getFactors(root + num[i], rem)
    return sol

Я перевірив рішення з урахуванням двох елементів, кількість комбінацій становить n!і результат не може містити дублікатів. Тому:

inpt = "1234"
results = getFactors("",inpt)

if len(results) == math.factorial(len(inpt)) | len(results) != len(set(results)):
    print("Wrong approach")
else:
    print("Correct Approach")

-1

Ось проста і зрозуміла рекурсивна реалізація;

def stringPermutations(s):
    if len(s) < 2:
        yield s
        return
    for pos in range(0, len(s)):
        char = s[pos]
        permForRemaining = list(stringPermutations(s[0:pos] + s[pos+1:]))
        for perm in permForRemaining:
            yield char + perm

1
Слід виправити відступ. Не потрібно зберігати результати рекурсивного виклику stringPermutationsу списку - ви можете перебирати безпосередньо над ним, наприклад for perm in stringPermutations(s[:pos] + s[pos+1:]):. Крім того , ви можете спростити forцикл, використовуючи enumerateзамість range, і виключити char = s[pos]призначення: for pos, char in enumerate(s):.
PM 2Кольцо

-1

З рекурсією

# swap ith and jth character of string
def swap(s, i, j):
    q = list(s)
    q[i], q[j] = q[j], q[i]
    return ''.join(q)


# recursive function 
def _permute(p, s, permutes):
    if p >= len(s) - 1:
        permutes.append(s)
        return

    for i in range(p, len(s)):
        _permute(p + 1, swap(s, p, i), permutes)


# helper function
def permute(s):
    permutes = []
    _permute(0, s, permutes)
    return permutes


# TEST IT
s = "1234"
all_permute = permute(s)
print(all_permute)

З ітеративним підходом (за допомогою стека)

# swap ith and jth character of string
def swap(s, i, j):
    q = list(s)
    q[i], q[j] = q[j], q[i]
    return ''.join(q)


# iterative function
def permute_using_stack(s):
    stk = [(0, s)]

    permutes = []

    while len(stk) > 0:
        p, s = stk.pop(0)

        if p >= len(s) - 1:
            permutes.append(s)
            continue

        for i in range(p, len(s)):
            stk.append((p + 1, swap(s, p, i)))

    return permutes


# TEST IT
s = "1234"
all_permute = permute_using_stack(s)
print(all_permute)

З лексикографічно відсортованим

# swap ith and jth character of string
def swap(s, i, j):
    q = list(s)
    q[i], q[j] = q[j], q[i]
    return ''.join(q)


# finds next lexicographic string if exist otherwise returns -1
def next_lexicographical(s):
    for i in range(len(s) - 2, -1, -1):
        if s[i] < s[i + 1]:
            m = s[i + 1]
            swap_pos = i + 1

            for j in range(i + 1, len(s)):
                if m > s[j] > s[i]:
                    m = s[j]
                    swap_pos = j

            if swap_pos != -1:
                s = swap(s, i, swap_pos)
                s = s[:i + 1] + ''.join(sorted(s[i + 1:]))
                return s

    return -1


# helper function
def permute_lexicographically(s):
    s = ''.join(sorted(s))
    permutes = []
    while True:
        permutes.append(s)
        s = next_lexicographical(s)
        if s == -1:
            break
    return permutes


# TEST IT
s = "1234"
all_permute = permute_lexicographically(s)
print(all_permute)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.