替换嵌套的循环...或不

时间:2009-01-27 02:27:28

标签: python loops for-loop nested-loops

我有一个循环遍历一系列四个(或更少)字符串的脚本。例如:

aaaa
aaab
aaac
aaad

如果能够使用嵌套的for循环实现它,那么:

chars = string.digits + string.uppercase + string.lowercase

for a in chars:
    print '%s' % a   
    for b in chars:
        print '%s%s' % (a, b)
        for c in chars:
            print '%s%s%s' % (a, b, c)
            for d in chars:
                print '%s%s%s%s' % (a, b, c, d)

这种循环是一种坏事,如果是这样,那么什么是更好的方式来完成我正在做的事情?

7 个答案:

答案 0 :(得分:15)

import string
import itertools

chars = string.digits + string.letters
MAX_CHARS = 4
for nletters in range(MAX_CHARS):
    for word in itertools.product(chars, repeat=nletters + 1):
        print (''.join(word))

这将打印您正在寻找的所有 15018570 字词。如果您想要更多/更少的单词,只需更改MAX_CHARS变量即可。对于任意数量的字符,它仍然只有两个for s,你不必重复自己。而且很可读。

答案 1 :(得分:6)

我要将我的答案提交为最具可读性和最不可扩展性的答案:)

import string
chars = [''] + list(string.lowercase)

strings = (a+b+c+d for a in chars
                   for b in chars
                   for c in chars
                   for d in chars)

for string in strings:
    print string

编辑:实际上,这是不正确的,因为它会产生所有长度<4的字符串的重复。从chars数组中删除空字符串只会生成4个字符串。

通常我会删除这个答案,但如果你需要生成相同长度的字符串,我仍然会喜欢它。

答案 2 :(得分:4)

首先为程序员写 - 计算机第二 如果明白且明显地理解那么它是正确的。

如果速度很重要且编译器无论如何都不优化它,如果你测量它并且这是问题 - 那么想想一个更快更聪明的方法!

答案 3 :(得分:3)

如果您了解(并记录:-),我认为这不是一件坏事。我不怀疑可能有更多的pythonic方式或聪明的解决方案(使用lambdas或诸如此类别)但我总是喜欢可读性而不是聪明。

由于您必须生成1,2-,3-和4个字符“单词”的所有可能性,因此此方法与任何方法一样好。我不确定你需要多长时间才能有效地生成(非常大致)1400万行输出(但可能每个解决方案都有这个问题)。

预先计算公共前缀可能会提高速度,但您最好先测量它以进行检查(始终检查,从不假设):

chars = string.digits + string.uppercase + string.lowercase
for a in chars:
    print a
    for b in chars:
        ab = '%s%s' % (a, b)
        print ab
        for c in chars:
            abc = '%s%s' % (ab, c)
            print abc
            for d in chars:
                print '%s%s' % (abc, d)
编辑:我实际上做了一些基准测试(使用Windows-Python 2.6.1) - 这个版本需要大约2.25个时间单位,而原始的2.84个,所以它快了26%。我认为这可能值得使用(再次,只要它清楚地记录了它试图实现的目标)。

答案 4 :(得分:2)

@nosklo's@Triptych's解决方案会产生不同的结果:

>>> list(map(''.join, itertools.chain.from_iterable(itertools.product("ab", 
...     repeat=r) for r in range(4)))) # @nosklo's 
['', 'a', 'b', 'aa', 'ab', 'ba', 'bb', 'aaa', 'aab', 'aba', 'abb', 'baa', 
 'bab', 'bba', 'bbb']
>>> ab = ['']+list("ab")
>>> list(map(''.join, (a+b+c for a in ab for b in ab for c in ab)))  
['', 'a', 'b', 'a', 'aa', 'ab', 'b', 'ba', 'bb', 'a', 'aa', 'ab', 'aa', 
 'aaa', 'aab', 'ab', 'aba', 'abb', 'b', 'ba', 'bb', 'ba', 'baa', 'bab', 
 'bb',  'bba', 'bbb']

这里修改了@ Triptych的解决方案,产生与@nosklo相同的输出:

>>> ab = "ab"
>>> list(map(''.join, itertools.chain([''], ab, (a+b for a in ab for b in ab),
...     (a+b+c for a in ab for b in ab for c in ab))))
['', 'a', 'b', 'aa', 'ab', 'ba', 'bb', 'aaa', 'aab', 'aba', 'abb', 'baa', 
 'bab', 'bba', 'bbb']

答案 5 :(得分:1)

有许多算法可用于生成集合的每个排列。你想要的是一个相关的问题,但不是直接的分类。 Suggested Reading

答案 6 :(得分:1)

它并没有完全回答这个问题,但是这将返回给定最大长度的n组合以及要使用的字母表中的字符:

#!/usr/bin/python

def nth_combination(n, maxlen=4, alphabet='abc'):
    """
    >>> print ','.join(nth_combination(n, 1, 'abc') for n in range(3))
    a,b,c
    >>> print ','.join(nth_combination(n, 2, 'abc') for n in range(12))
    a,aa,ab,ac,b,ba,bb,bc,c,ca,cb,cc
    >>> import string ; alphabet = string.ascii_letters + string.digits
    >>> print ','.join(nth_combination(n, 4, alphabet) for n in range(16))
    a,aa,aaa,aaaa,aaab,aaac,aaad,aaae,aaaf,aaag,aaah,aaai,aaaj,aaak,aaal,aaam
    >>> print ','.join(nth_combination(n, 4, alphabet)
    ...                for n in range(0, 14000000, 10**6))
    a,emiL,iyro,mKz2,qWIF,u8Ri,zk0U,Dxav,HJi9,LVrM,P7Ap,UjJ1,YvSE,2H1h
    """
    if maxlen == 1:
        return alphabet[n]
    offset, next_n = divmod(n, 1 + len(alphabet)**(maxlen-1))
    if next_n == 0:
        return alphabet[offset]
    return alphabet[offset] + nth_combination(next_n-1, maxlen-1, alphabet)

if __name__ == '__main__':
    from doctest import testmod
    testmod()

当然,只有当你需要随机访问这些组合而不是总是遍历它们时,这当然才有意义。

如果maxlen很高,则可以实现某些速度优化,例如通过删除字符串连接并在递归的每个级别重新计算alphabetmaxlen-1的长度。非递归方法也可能有意义。