【问题标题】:Python - Advanced Parametric nested loopsPython - 高级参数嵌套循环
【发布时间】:2019-10-02 18:40:38
【问题描述】:

我在参数嵌套循环上找到了很好的答案 - Parametric nested loops in Python

不过,我的要求有点难

问题

这是 3 深度嵌套循环(我想要具有 n 深度的函数):

for i in range(100 + 1):
    for j in range(i, 100 + 1):
        for k in range(j, 100 + 1):
            -> need to retrieve [i,j,k]

请注意,每个循环的起点都是动态的,并且会随着每个父循环而变化

【问题讨论】:

  • 我不太明白,为什么不print(i, j, k)
  • @olinox14 问题是关于任意深度嵌套,3深度代码就是一个例子。

标签: python loops recursion nested


【解决方案1】:

这是一种使用默认参数的递归方法。下面的编号点是指代码中编号的cmets。

  1. (基础)深度为零。我们完成了,所以产生组合,comb
  2. (inductive)深度至少为 1。对于范围内的每个 x,使用 x 作为嵌套范围的新起点委托给递归生成器,并将 x 附加到组合中,@ 987654325@.
def nested_range (depth = 0, start = 0, end = 1, comb = ()):
  if depth == 0:
    yield comb                  #1
  else:
    for x in range(start, end): #2
      yield from nested_range(depth - 1, x, end, comb + (x,))

这是一个嵌套范围,深度为三 (3) 级 -

for p in nested_range (3, 0, 4):
  print(p)

# (0, 0, 0)
# (0, 0, 1)
# (0, 0, 2)
# (0, 0, 3)
# (0, 1, 1)
# (0, 1, 2)
# (0, 1, 3)
# (0, 2, 2)
# (0, 2, 3)
# (0, 3, 3)
# (1, 1, 1)
# (1, 1, 2)
# (1, 1, 3)
# (1, 2, 2)
# (1, 2, 3)
# (1, 3, 3)
# (2, 2, 2)
# (2, 2, 3)
# (2, 3, 3)
# (3, 3, 3)

这个实现是一个总函数,当depth = 0时提供一个有效的结果-

for p in nested_range (0, 0, 4):
  print(p)

# ()

为了更好地衡量,这里是嵌套范围的输出,深度为五 (5) 级 -

for p in nested_range (5, 0, 3):
  print(p)

# (0, 0, 0, 0, 0)
# (0, 0, 0, 0, 1)
# (0, 0, 0, 0, 2)
# (0, 0, 0, 1, 1)
# (0, 0, 0, 1, 2)
# (0, 0, 0, 2, 2)
# (0, 0, 1, 1, 1)
# (0, 0, 1, 1, 2)
# (0, 0, 1, 2, 2)
# (0, 0, 2, 2, 2)
# (0, 1, 1, 1, 1)
# (0, 1, 1, 1, 2)
# (0, 1, 1, 2, 2)
# (0, 1, 2, 2, 2)
# (0, 2, 2, 2, 2)
# (1, 1, 1, 1, 1)
# (1, 1, 1, 1, 2)
# (1, 1, 1, 2, 2)
# (1, 1, 2, 2, 2)
# (1, 2, 2, 2, 2)
# (2, 2, 2, 2, 2)

【讨论】:

    【解决方案2】:

    这是一种迭代方法:

    def iterate(max_range, dim):
        if dim == 0:  #handle edge case
            yield from iter(())
        elif dim == 1:
            yield [0]
        else:
            fields = [0]*dim
            while True:
                yield fields
                fields[-1] += 1
                for i in reversed(range(1, dim)):
                    if fields[i] >= max_range:
                        fields[i - 1] += 1
                        fields[i] = min(fields[i - 1], max_range -1)
                if fields[0] == max_range:
                    break
    

    一个例子:

    for i in iterate(4, 3):
        print(i)
    

    给予:

    [0, 0, 0]
    [0, 0, 1]
    [0, 0, 2]
    [0, 0, 3]
    [0, 1, 1]
    [0, 1, 2]
    [0, 1, 3]
    [0, 2, 2]
    [0, 2, 3]
    [0, 3, 3]
    [1, 1, 3]
    [1, 2, 2]
    [1, 2, 3]
    [1, 3, 3]
    [2, 2, 3]
    [2, 3, 3]
    [3, 3, 3]
    

    编辑: 为 max_value 和嵌套级别添加了单独的参数

    【讨论】:

    • 我认为这不符合 OP 的要求。嵌套级别和最大范围是两个独立的变量。
    • 不错的更新,但仍然存在一个问题。如果dim = 0 你的程序抛出一个错误而不是返回一个有效的结果。有关更多详细信息,请参阅 GreenCloak 答案中的 cmets。
    • 如果您认为 dim = 0 作为有效输入,那么您是对的。但是检查 dim == 0 并返回一个空的迭代器或在这种情况下认为有意义的任何内容应该不难。
    【解决方案3】:

    这就是我会使用递归的类型 - 正如我看到您在标签中指出的那样。像这样的东西,例如:

    def iterate(depth, i=0, maxrange=101):
        if depth <= 0:
            return (yield ())
        for j in range(i, maxrange):                    # for each value of j...
            if depth == 1:                              # base case:
                yield (j,)                              #    return a 1-tuple
            else:                                       # recursive case:
                for k in iterate(depth-1, j, maxrange): #    get a generator for the next level of recursion
                    yield (j,) + k                      #    yield all (depth+1)-tuples from prepending j to the next layer of recursion
    

    当被称为iterate(3) 时应该产生什么

    [(0,0,0), (0,0,1), (0,0,2), ..., (0,0,100), (0,1,1), ..., (0,100,100), (1,1,1), ..., (99,99,99), (100,100,100)]
    

    【讨论】:

    • 第一行有语法错误,缺少:。当指定depth=0 时,程序会给出错误的结果。
    • 感谢您指出这一点,我已尽力解决它
    • 它很接近,但共域(可能的输出)有点偏离。您可能想要 return (yield ()) 否则 depth = 0 给出 no 结果而不是 empty 结果。要理解为什么这是一个问题,想象一下如果 subtract(5,5) 什么都不返回 (None) 而不是 0,即“空”数字。取决于subtract 输出的函数必须检查它是否接收到一个值。通过返回一个“空”值,subtract 的调用者可以保证始终返回一个结果。
    • @user633183 谢谢,我已经相应地更改了它。返回tuple() 也可以吗?
    • return (yield tuple()) 是等价的,是的。
    【解决方案4】:

    这可以递归地完成(正如您已经猜到的),例如这样:

    def nest_gen(count, start=0, end=101):
        if count < 1:
            return
        elif count == 1:
            yield from ((r,) for r in range(start, end))
            return
    
        for i in range(start, end):
            yield from ((i,) + r for r in nest_gen(count - 1, i, end))
    
    
    print(tuple(nest_gen(6, end=5)))
    

    【讨论】:

    • 我更喜欢这个解决方案而不是我的答案 - 它的逻辑完全相同,但更 Pythonic
    • 非全功能。 count = 0 时导致堆栈溢出。
    • 不客气,但 codomain 仍然不是全部。有关详细信息,请参阅我在 Green Cloak 的帖子上发表的评论。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-21
    • 2017-04-24
    • 1970-01-01
    • 1970-01-01
    • 2012-11-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多