【问题标题】:Python: nested 'for' loopsPython:嵌套的“for”循环
【发布时间】:2017-06-05 11:56:01
【问题描述】:

我想遍历所有 n 位数字,这样数字的第二个数字总是小于或等于第一个数字,第三个数字小于或等于第二个数字等等。我可以通过编写一个可怕的代码来实现如:

for i in range(10):
    for j in range(i+1):
        for k in range(j+1):

等等,但是对于 10 位数字,我的代码开始看起来很糟糕,而且要写很多东西,如果我想赞扬其中的一些,缩进会变得很糟糕。有没有一种简洁明了的方法?

编辑:只是为了让人们知道我为什么要为此烦恼,https://projecteuler.net/problem=74 让我检查从 1 到 100 万的数字。不幸的是,这并不像我想象的那么简单——带前导零的数字与内部带零的数字的处理方式不同,因此必须执行一些额外的魔法。无论如何,感谢所有有见地的建议。

【问题讨论】:

  • @iFlo 某种通过神秘魔法工作的古老野兽。
  • 数字的顺序重要吗?
  • 实际上,您应该按照以下方式进行:考虑您的号码,比较两个第一位数字。然后,再次调用该函数,但您已经截断了第一个数字。然后您正在检查第 2 位和第 3 位等。如果不再有两位要比较或当前比较失败,您可以停止。
  • @iFlo 我不认为你明白。我不想检查给定的数字是否遵守我的规则,我想对遵守此规则的所有 n 位数字运行一个函数,因此不要迭代所有 n 位数字并检查是否这条规则正在被遵守,因为它需要很长时间。
  • 与您的问题不完全相关:可以使用 this trick 将多个 for 循环组合成一个循环。

标签: python for-loop nested


【解决方案1】:

可以使用itertools:

>>> for comb in itertools.combinations_with_replacement(range(9, -1, -1), 3):
        print comb

(9, 9, 9)
(9, 9, 8)
(9, 9, 7)
(9, 9, 6)
...
(4, 0, 0)
(3, 3, 3)
(3, 3, 2)
(3, 3, 1)
(3, 3, 0)
(3, 2, 2)
(3, 2, 1)
(3, 2, 0)
(3, 1, 1)
(3, 1, 0)
(3, 0, 0)
(2, 2, 2)
(2, 2, 1)
(2, 2, 0)
(2, 1, 1)
(2, 1, 0)
(2, 0, 0)
(1, 1, 1)
(1, 1, 0)
(1, 0, 0)
(0, 0, 0)

或者递归,追加越来越多的数字直到足够,这可以更直接地产生int对象而不是数字元组(不确定这是否是你真正需要的):

def build(enough, prefix=0):
    if prefix >= enough:
        print(prefix)
        return
    for digit in range(prefix % 10 + 1) if prefix else range(1, 10):
        build(enough, prefix * 10 + digit)

演示(注意它省略了“000”,不确定你是否想要那个):

>>> n = 3
>>> build(10**(n-1))
100
110
111
200
210
211
220
221
222
300
310
311
320
321
322
330
331
332
333
400
410
411
420

【讨论】:

  • 这完全符合我的要求。非常感谢。
【解决方案2】:

这是一种使用itertools的方法:

from itertools import combinations_with_replacement

N = 3

for kji in combinations_with_replacement((str(i) for i in range(10)), N):
    print(''.join(reversed(kji)))

请注意,顺序与您原来的方法不同。

我最近有一个simliar question...

【讨论】:

  • 谢谢 Hiro,正如 Stefan 上面指出的那样,这正是我想要的方式。
  • @098799:是的,他打败了我,我更喜欢他对索引顺序的处理......
  • @hiroprotagonist 您可以将range(10) 替换为map(str, range(10)。将使以后的使用更简单,更快。
  • @StefanPochmann:感谢您的建议!更新。 (我更喜欢生成器表达式而不是map...)
  • @hiroprotagonist 或'0123456789' :-)。更短更清晰,虽然我不太喜欢硬编码这样的东西......
【解决方案3】:

一种简单的递归方法

def ordered_digits_generator(numDigits,min=1,max=9):
    for first in range(min,max+1):
        if numDigits == 1:
             yield first
        else:
             addend = first*10**(numDigits-1)
             for rest in ordered_digits(numDigits-1,min=0,max=first):
                 yield addend+rest

然后通过以下方式调用:

for number in ordered_digits_generator(10):
    print number

按预期工作。

数学家的方法

itertools 包已经具有基本上已经实现此递归的逻辑。大概比第一种方法更好,经过大量测试。所以我们可以这样使用它:

import itertools
def ordered_digits_combo(numDigits):
    exponent = [10**i for i in range(0,numDigits)]

    for subset in itertools.combinations(range(0,numDigits+9),numDigits):
        if subset[numDigits-1]>numDigits-1:
            v = 0
            for i in range(0,numDigits):
                v += exponent[i]*(subset[i]-i)
            yield v

给定{0,1,...,n+8} 的有序子集a[0]<a[1]<...<a[n-1],我们选择右边第i 位等于a[i]-i 的数字。我们必须排除 a[n-1]==n-1 的情况,因为它由一个全零的数字组成。

【讨论】:

    【解决方案4】:

    我按照最初的评论实施了@iFlo 的建议。它的效率不是很高,但肯定不需要很长时间。

    def digit_test(n):
        while n > 9:
            if (n % 100 / 10) < (n % 10): return False
            n /= 10
        return True
    
    # under a second to construct a list of all numbers below 1000000 meeting the criteria
    candidates = [x for x in xrange(1,1000000) if digit_test(x)]
    
    # should be 8001 elements, consistent with other algorithms
    print len(candidates)
    

    【讨论】:

      【解决方案5】:

      我可能会递归地实现这个:

      def generate(max, digits):
          for d in range(max + 1):
              if digits == 1:
                  yield d
              else:
                  first = d * 10**(digits-1)
                  for n in generate(d, digits - 1):
                      yield first + n
      

      输出:

      In : list(generate(3, 3))
      Out:
      [0,
       100,
       110,
       111,
       200,
       210,
       211,
       220,
       221,
       222,
       300,
       310,
       311,
       320,
       321,
       322,
       330,
       331,
       332,
       333]
      

      【讨论】:

        猜你喜欢
        • 2012-06-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-25
        • 2019-04-15
        • 2020-10-09
        • 2021-08-21
        • 2017-08-27
        相关资源
        最近更新 更多