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