簡體   English   中英

生成字符串中所有可能的字符組合

[英]Generating all possible combinations of characters in a string

假設我有一個字符串列表:

li = ['a', 'b', 'c']

我想構建一個新列表,使得新列表的每個條目都是原始列表中選擇的 3 個條目的串聯。 請注意,可以重復選擇每個條目:

new_li=['abc', 'acb', 'bac', 'bca', 'cab', 'cba', 'aab', 'aac',....'aaa', 'bbb', 'ccc']

粗暴的方式是構造一個 3-fold 嵌套的 for 循環,並將每個 3-組合插入到新列表中。 我想知道是否有任何 Pythonic 方式來處理這個問題? 謝謝。

更新:稍后我會將新列表轉換為集合,因此順序無關緊要。

這看起來像是itertools.product的工作。

import itertools

def foo(l):
     yield from itertools.product(*([l] * 3)) 

for x in foo('abc'):
     print(''.join(x))

aaa
aab
aac
aba
abb
abc
aca
acb
acc
baa
bab
bac
bba
bbb
bbc
bca
bcb
bcc
caa
cab
cac
cba
cbb
cbc
cca
ccb
ccc

從python3.3及更高版本可以獲得yield from 對於舊版本,循環內的yield

def foo(l):
     for i in itertools.product(*([l] * 3)) :
         yield i

獲取列表的所有組合(也稱為笛卡爾積)的最佳方法是使用itertools.product使用iterable的len作為repeat參數(這與其他答案不同):

from itertools import product
li = ['a', 'b', 'c']
for comb in product(li, repeat=len(li)):
    print(''.join(comb))

或者如果你想把結果作為列表:

>>> combs = [''.join(comb) for comb in product(li, repeat=len(li))]
>>> combs
['aaa', 'aab', 'aac', 'aba', 'abb', 'abc', 'aca', 'acb', 'acc', 'baa', 
 'bab', 'bac', 'bba', 'bbb', 'bbc', 'bca', 'bcb', 'bcc', 'caa', 'cab', 
 'cac', 'cba', 'cbb', 'cbc', 'cca', 'ccb', 'ccc']

使用repeat參數比使用手動添加和解壓縮列表要簡潔一些。

import itertools
repeat=int(input("Enter length: ")
def password():
    def foo(l):
        yield from itertools.product(*([l] * repeat)))

    for x in foo('abcdefghijklmnopqrstuvwxyz'): 
        # you could also use string.ascii_lowercase or ["a","b","c"]
        print(''.join(x))

password()

這也適用於您的目的嗎?

li = ['a', 'b', 'c']

new_li = [a+b+c for a in li for b in li for c in li]

我將向您展示一種無需任何庫即可執行此操作的方法,以便您了解如何實現它背后的邏輯。

首先,我們需要了解如何在數學上實現所有組合。

讓我們看看從 ab 到長度為 '1' 的每個可能的字符組合的模式。

a
b

沒什么可看的,但從我們所見,列表中的每個字符都有一組。 讓我們將字符串長度增加到“2”,看看會出現什么模式。

aa
ab
ba
bb

因此,查看此模式,我們看到添加了一個新列。 最右邊的列與第一個示例相同,只有一組字符,但這次是循環的。 最左邊的列有 2 組字符。 是不是每增加一個新列,就會增加一組字符? 讓我們看一下,通過將字符串長度增加到'3'來找出答案。

aaa
aab
aba
abb
baa
bab
bba
bbb

我們可以看到右邊的兩列保持不變,左邊的新列每個字符有 4 個。 不是我們所期待的。 因此,每列的字符數不會增加 1,而是,如果您注意到該模式。 它實際上是按 2 的冪增加的。

第一列只有“1”組字符:2 ^ 0 = 1

第二列有 '2' 組字符:2 ^ 1 = 2

第三列有 '4' 組字符:2 ^ 2 = 4

所以這里的答案是,每添加一個新列,該列中每個字符的數量由它的 position 的冪決定,右邊的第一列是 x ^ 0,然后是 x ^ 1,然后是 x ^ 2。 .. 等等。

但是x是什么? 在示例中,我給出了 x = 2。但它總是 2 嗎? 讓我們來看看。

我現在將舉例說明范圍 ac 中每個可能的字符組合

aa
ab
ac
ba
bb
bc
ca
cb
cc

如果我們計算右邊第一列有多少個字符,每次循環時每個字符仍然只有一組,這是因為右邊的第一列總是等於 x ^ 0 和任何東西0 次方總是 1。但是如果我們查看第二列,我們會看到每個循環的每個字符 3 個。 因此,如果 x ^ 1 用於第二列,則 x = 3。對於第一個示例,我給出的范圍為 ab(范圍為 2),而第二個示例使用范圍為 ac(范圍為 3),它似乎 x 始終是組合中使用的字符長度。

識別出第一個模式后,我們可以開始構建一個 function,它可以識別每列應該代表什么。 如果我們想構建字符串長度為 3 的范圍 ab 中的每個字符組合,那么我們需要一個 function 可以理解每列中的每組字符將如下所示:[4,2,1]。

現在創建一個 function,它可以通過返回代表基於 position 的列中字符總數的數字列表來找到每列中應該有多少字符集。 我們使用權力來做到這一點。

請記住,如果我們使用 ab(2) 中的字符范圍,那么每列應該有每個集合的總共 x ^ y 個字符,其中 x 表示正在使用的字符的長度,y 表示它的列 position,其中右邊的第一列是第 0 列。

例子:

從 ['a', 'b'] 范圍內的字符串長度為 3 的字符組合將在每個集合的最左側列中總共有 4 個 a 和 b,在接下來的列中總共有 2 個 a 和 b每組和每組最后一個總共有 1 個 a 和 b。

要返回一個列表,其中包含與其列相關的字符總數,因此[4, 2, 1]我們可以這樣做

def getCharPower(stringLength, charRange):
    charpowers = []
    for x in range(0, stringLength):
            charpowers.append(len(charRange)**(stringLength - x - 1))
    return charpowers

使用上面的 function - 如果我們想創建范圍從 ab (2) 並且字符串長度為 4 的每個可能的字符組合,就像這樣

aaaa
aaab
aaba
aabb
abaa
abab
abba
abbb
baaa
baab
baba
babb
bbaa
bbab
bbba
bbbb

總共有 (8) 個 a 和 b, (4) a 和 b, (2) a 和 b, 以及 (1) a 和 b, 那么我們要返回一個 [8, 4, 2, 1]。 stringLength 是4 ,我們的 charRange 是['a', 'b'] ,我們的 function 的結果是[8, 4, 2, 1]

所以現在我們要做的就是根據返回列表中列位置的值打印出每個字符 x 次。

但是,為了做到這一點,我們需要找出每組在其列中打印了多少次。 看一下上一個組合示例右側的第一列。 盡管 a 和 b 每組只打印一次,但它循環並打印出同樣的東西 7 次(總共 8 次)。 如果字符串只有 3 個字符的長度,那么它總共循環 4 次。

這樣做的原因是因為我們字符串的長度決定了總共有多少組合。 計算出來的公式是 x ^ y = a,其中 x 等於我們的字符范圍,y 等於字符串的長度,a 等於這些規范內可能的組合總數。

所以要最終解決這個問題,我們的解決方案是弄清楚

  1. 每組有多少個字符 go 到每列
  2. 每列中的每組重復多少次

我們之前創建的 function 已經解決了我們的第一個選項。 我們的第二個選項可以通過計算 charRange ^ stringLength 找出總共有多少組合來解決。 然后通過一個循環,我們添加有多少組字符,直到在該列中達到(可能的組合總數)。 對每一列運行它,你就會得到結果。

這是解決此問題的 function

def Generator(stringLength, charRange):
    workbench = []
    results = []
    charpowers = getCharPower(stringLength, charRange)
    for x in range(0, stringLength):
            while len(workbench) < len(charRange)**stringLength:
                    for char in charRange:
                            for z in range(0, charpowers[x]):
                                    workbench.append(char)
            results.append(workbench)
            workbench = []
    results = ["".join(result) for result in list(zip(*results))]
    return results

function 將返回您提供的所有可能的字符組合和字符串長度。

解決這個問題的一種更簡單的方法是只為你的總長度運行一個 for 循環。

因此,要創建從 ab 到長度為 2 的所有可能的字符組合

characters = ['a', 'b']
for charone in characters:
    for chartwo in characters:
        print(charone+chartwo)

盡管這要簡單得多,但這是有限的。 此代碼僅適用於打印長度為 2 的每個組合。要創建更多,我們必須在每次想要更改它時手動添加另一個 for 循環。 但是,我在此代碼之前提供給您的函數將打印您給它的字符串長度的任意組合,使其 100% 具有適應性,並且是您自己手動解決此問題的最佳方法,無需任何庫。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM