Pythonで特定の文字列のすべての可能な順列を見つける


89

文字列があります。文字列内の文字の順序を変更して、その文字列からすべての順列を生成したいと思います。たとえば、次のように言います。

x='stack'

私が欲しいのはこのようなリストです、

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

現在、文字列のリストキャストを繰り返し、2文字をランダムに選択して転置して新しい文字列を形成し、それをlのセットキャストに追加しています。文字列の長さに基づいて、可能な順列の数を計算し、設定されたサイズが制限に達するまで反復を続けます。これを行うためのより良い方法があるはずです。

回答:


143

itertoolsモジュールには、permutations()と呼ばれる便利なメソッドがあります。ドキュメントには次のように書かれています。

itertools.permutations(iterable [、r])

iterable内の要素の連続するr長の順列を返します。

rが指定されていないか、Noneの場合、rはデフォルトで反復可能の長さになり、可能なすべての完全長の順列が生成されます。

順列は辞書式順序で発行されます。したがって、入力反復可能オブジェクトがソートされている場合、順列タプルはソートされた順序で生成されます。

ただし、並べ替えられた文字を文字列として結合する必要があります。

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

重複に悩まされている場合は、set次のような重複のない構造にデータを適合させてみてください。

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

これは私たちが伝統的に型キャストとして考えていたものではなく、set()コンストラクターへの呼び出しであると指摘してくれた@pstに感謝します。


3
Nit:set(...)「キャスト」しません。むしろ、入力コレクションを表すセットを生成(および生成)します。生成されると、入力コレクションとの関連付けはありません(また、別のオブジェクトであり、単なる別のビューではありません)。

@pst:うーん、私は反対する傾向があります。私はエイダまたはパスカルで、キャストが同じビットの単なる新しいタイプビューであることを知っています。ただし、少なくともCの観点からは、データの基本構造を変更するかどうかに関係なく、キャストは適切な用語です。単に明示的な型変換を指します。できれば私の誤解を説明してください。
憧れるマシン

1
型キャスト。ご指摘のとおり、単なる見方とは異なるかもしれませんが、混乱を避けるために概念を分けておくのが好きです。最初のコメントで「強制」について明示的に言及する必要がありましたが、関数の設定を検討するだけです:リスト->設定。

1
私はそれを見て、、は入力に応じてブール値(True / False)に評価されるbool関数です。...ここに「キャスト」の使用は、スプリアスや誤解である私を見つける

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

1
少し変更しました。i== stepの場合は変数を交換する必要はありません
siraj 2015年

4
n!があるため、実行時間はO(n!)です。順列。
アスペン2016年

なぜstep == len(string)代わりに使用しているのstep == len(string) - 1ですか?
tulians 2016

その場合、最後の2つのアイテムが交換されることはありません。bとcが入れ替わるまで「abc」を試してください。
ローマンリーゼン2017

14

最小限のコードで文字列の順列を実行する別の方法を次に示します。基本的にループを作成してから、一度に2つの文字を交換し続けます。ループ内では、再帰が発生します。インデクサーが文字列の長さに達したときにのみ印刷することに注意してください。例:開始点の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ユーザーはすでにいくつかの強力なソリューションを投稿していますが、私はさらに別のソリューションを示したかったのです。これは私がより直感的だと思う

アイデアは、与えられた文字列に対して:アルゴリズム(擬似コード)によって再帰できるということです:

permutations = char + permutations(string-char)for char in string

私はそれが誰かを助けることを願っています!

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

3
これは、繰り返し文字(str.replace)がある場合には機能しません。例:rqqx
sanjay 2018

使用:[permutation_list.append(char + a)for a in permutations(string.replace(char、 ""、1))]
user3761855 2018

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_permsreturnステートメントでセットに変換するリストではなく、セットの場合、RAMと時間を節約できます。3..replaceの再帰呼び出しに対する引数を作成する代わりに、文字列スライスを使用する方が効率的ですpermutations。4.string変数名として使用することは、標準stringモジュールの名前を隠すため、お勧めできません。
PM 2Ring 2017年

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秒、@ Adrianoおよび@illerucisバージョンで16.357秒かかりました。もちろん、それでも最適化できます。


4

itertools.permutations良いですが、繰り返される要素を含むシーケンスをうまく処理しません。これは、内部的にシーケンスインデックスを並べ替え、シーケンスアイテムの値を認識しないためです。

確かに、itertools.permutationsセットを介しての出力をフィルタリングして重複を排除することは可能ですが、それでもそれらの重複を生成するのに時間が無駄になり、基本シーケンスに複数の繰り返し要素がある場合、重複がたくさんあります。また、コレクションを使用して結果を保持するとRAMが浪費され、そもそもイテレータを使用する利点が失われます。

幸いなことに、より効率的なアプローチがあります。以下のコードは、14世紀のインドの数学者ナラヤナパンディタのアルゴリズムを使用しています。これは、ウィキペディアの順列に関する記事にあります。この古代のアルゴリズムは、順列を順番に生成するための最も高速な既知の方法の1つであり、繰り返される要素を含む順列を適切に処理するという点で非常に堅牢です。

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
いいえ、同じ文字が2つ以上ある場合は、常に重複(またはさらに悪い)が発生します。@machineyearningの例では、stackの代わりにstacksという単語を使用したためです。つまり、ソリューションは、一意の文字を含む単語に対してのみ機能します。
erik 2016年

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
あなたのソリューションがすでに提供されているものよりも優れている理由を説明できますか?
ノエルウィドマー2017

私の解決策が他の解決策よりも優れているとは言いませんでした。そのためのソリューションを提供しました。
Srivastava 2018年


1

itertoolsを使用せずに、異なる文字(必ずしも辞書式順序である必要はありません)を含む文字列のすべての順列のリストを返すためのillerucisのコードのわずかに改善されたバージョンを次にs示します。

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

説明を追加してみてください。
ArunVinoth19年


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

これは、再帰を使用して順列を生成する1つの方法であり、文字列 '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

このプログラムは重複を排除しませんが、最も効率的なアプローチの1つだと思います。

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)

説明を追加してみてください。
ArunVinoth19年

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)

iterable内の要素の連続する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

2つの要素を考慮してソリューションを検証しました。組み合わせの数はで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を使用してループを簡略化し、割り当てを削除することもできます。enumeraterangechar = s[pos]for pos, char in enumerate(s):
PM 2Ring 2017年

-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)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.