【问题标题】:Replace Nested For Loops... or not是否替换嵌套的 For 循环...
【发布时间】:2010-10-03 16:29:12
【问题描述】:

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

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)

这种循环嵌套是一件坏事吗?如果是这样,什么是完成我正在做的事情的更好方法?

【问题讨论】:

  • 试着想出一个比蛮力更好的算法。也许是递归或分而治之
  • @Perpetual,在我看来,既然你必须产生所有的可能性,那么它是否是蛮力几乎没有什么区别。当您可以最小化您的需求时,非暴力算法是很好的,但我认为情况并非如此。递归使您的源代码更小,但仅此而已。

标签: python loops for-loop nested-loops


【解决方案1】:
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 变量。对于任意数量的字符,它仍然只有两个 fors,您不必重复自己。并且可读性很好。 .

【讨论】:

  • 语法错误...您缺少一个冒号。
  • 这应该提到它需要python 2.6,否则这是我要发布的解决方案。 :)
  • “可读性很强”? - 我仍然认为原版更容易理解,但我是恐龙,我从未见过 itertools,所以我不会 -1 你:-)。
  • 当我看到关键字的两个明显嵌套的副本时,你能解释一下为什么它满足“没有嵌套的”吗?
  • 其实我想我会做一个性能检查,看看 itertools 有多快。结果令人惊讶 - 在 Python 2.6.1 (Windows) 下,itertools 慢得多(2.16 而不是 0.45 time.clock() 单位)。没有输出并在脚本内每次运行五次以消除启动时间。
【解决方案2】:

我将把我的答案作为可读性最强、可扩展性最低的答案提交:)

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

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

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

【讨论】:

  • 我喜欢...就像一个隐形的定时炸弹。
  • 它涵盖了所有单词,注意“字符”列表中的空字符串。
  • 您的解决方案更具可读性,但会产生不同的输出:stackoverflow.com/questions/482146/…
  • 是的,它会生成每个长度
【解决方案3】:

首先为程序员写作,其次为计算机写作。
如果它是清晰易懂的,那么它是正确的。

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

【讨论】:

  • 不是每个程序员都是理论家。我们大多数人都有工作要在某个日期之前完成,并且不受 CPU 的约束。在这种情况下,mgb 是正确的,您应该为程序员编写。 OP 的算法并非如此,但这并不意味着 mgb 是错误的,尽管可能没有帮助。
【解决方案4】:

我不认为这是一件坏事,只要您理解(并记录 :-)它。我不怀疑可能有更 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.84 相比需要大约 2.25 个时间单位,所以它快了 26%。我认为这可能保证使用它(同样,只要它清楚地记录了它试图实现的目标)。

【讨论】:

  • 你可以通过简单的a+b而不是'%s%s' % (a, b)来节省相当多的时间
【解决方案5】:

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

【讨论】:

    【解决方案6】:

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

    【讨论】:

      【解决方案7】:

      它不能完全回答问题,但这将返回nth 组合,以获得给定的最大长度和要使用的字母表中的字符:

      #!/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 的长度。非递归方法也可能有意义。

      【讨论】:

        猜你喜欢
        • 2019-06-06
        • 2016-07-14
        • 2020-06-03
        • 2018-05-21
        • 1970-01-01
        • 2021-08-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多