关于python:使用嵌套for循环的组合

combination using nested for loops

本问题已经有最佳答案,请猛点这里访问。

我问题的简化版本是:我写了这个代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
string = ['A','A','A','A']
temp=string[:]

f=open('combin.txt', 'a')

for x1 in range(0,2):
    if x1==1:
        temp[0]='X'
    for x2 in range(0,2):
        if x2==1:
            temp[1]='X'
        for x3 in range(0,2):
            if x3==1:
                temp[2]='X'
            for x4 in range(0,2):
                if x4==0:
                    f.write(''.join(temp)+'
'
)
                if x4==1:
                    temp[3]='X'
                    f.write(''.join(temp)+'
'
)

结果是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
AAAA
AAAX
AAXX
AAXX
AXXX
AXXX
AXXX
AXXX
XXXX
XXXX
XXXX
XXXX
XXXX
XXXX
XXXX
XXXX

但我期望的是这些组合:美国航空航天协会AAAX阿克萨AXAAXAAAXX斧萨克斯XXAA厦华阿克萨AXXXXAXXXXAXXXXAXXXX

注意,在最终的程序中,我需要在每个if条件下做很多操作,所以它不是一个简单的字符串迭代,但首先我想让这个组合部分工作。非常感谢你的帮助。


您可以使用内置库itertools,它具有"组合"功能,或者您可以认识到您实际上是在表示二进制数:

1
2
for s in [bin(n)[2:].zfill(4) for n in xrange(2 ** 4)]:
    print s.replace('0','A').replace('1','X')

上述代码:

  • 取零(0b0000)到2^4-1(0b1111)之间的数字范围,
  • 将每个转换为字符串中的二进制表示形式(内置bin函数)
  • 剪掉前导"0b"
  • 确保生成的字符串有足够的前导零
  • 然后在for循环体中,将1和0转换为您的格式。
  • 您所做的操作可以基于范围的数量,在这种情况下,您可以将字符串格式移动到循环体中(当然,如果您知道将要执行哪种操作,这将有所帮助)

    1
    2
    3
    4
    5
    6
    7
    for n in xrange(2 ** 4):
        if n & 0b1000:    # If the first character is 'X'
            # operations here
        if n & 0b0100:    # If the second character is 'X'
            # other operations
        # ... more checks/operations ...
        print bin(n)[2:].zfill(4).replace('0','A').replace('1','X')

    以下是一种不太直观的显示组合的方法:

    1
    2
    3
    4
    5
    6
    7
    8
    def gen(s):
        if len(s) == 4:
            print s
            return
        gen(s+'A')
        gen(s+'X')
    # which you would run with:
    gen('')


    代码的问题在于,您将temp的项设置为X,但从不将它们设置回A。请参阅Moarningsun的答案,了解解决此问题的直接方法。但是,您可以将其缩短很多:您可以直接循环字符串AX中的字符,并相应地设置temp[0],而不是循环到XA,这取决于x1的值。

    1
    2
    for x1 in 'AX':
        temp[0] = x1

    你甚至可以把它变成一个大的清单理解:

    1
    2
    3
    >>> [x1+x2+x3+x4 for x1 in 'AX' for x2 in 'AX' for x3 in 'AX' for x4 in 'AX']
    ['AAAA', 'AAAX', 'AAXA', 'AAXX', 'AXAA', 'AXAX', 'AXXA', 'AXXX',
     'XAAA', 'XAAX', 'XAXA', 'XAXX', 'XXAA', 'XXAX', 'XXXA', 'XXXX']

    当然,正确的方法是使用itertools模块。但是,请注意,您要找的不是combinations,而是product

    1
    2
    3
    >>> [''.join(comb) for comb in itertools.product('AX', repeat=4)]
    ['AAAA', 'AAAX', 'AAXA', 'AAXX', 'AXAA', 'AXAX', 'AXXA', 'AXXX',
     'XAAA', 'XAAX', 'XAXA', 'XAXX', 'XXAA', 'XXAX', 'XXXA', 'XXXX']

    因此,您可以在一行可读的代码中创建"组合",然后迭代该列表,在一个更干净的循环中做您需要做的任何事情。


    如果您在实际程序中确实需要所有的for循环,您可以这样修复它:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    for x1 in range(0,2):
        if x1==0:
            temp[0]='A'
        if x1==1:
            temp[0]='X'
        for x2 in range(0,2):
            if x2==0:
                temp[1]='A'
            if x2==1:
                temp[1]='X'
            for x3 in range(0,2):
                if x3==0:
                    temp[2]='A'
                if x3==1:
                    temp[2]='X'
                for x4 in range(0,2):
                    if x4==0:
                        temp[3]='A'
                    if x4==1:
                        temp[3]='X'
                    print(''.join(temp))

    它提供以下输出:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    AAAA
    AAAX
    AAXA
    AAXX
    AXAA
    AXAX
    AXXA
    AXXX
    XAAA
    XAAX
    XAXA
    XAXX
    XXAA
    XXAX
    XXXA
    XXXX

    注意最后一个字母变化最快,因为它是由最内部的for循环设置的。