Python - Расширенные параметрические вложенные циклы - PullRequest
1 голос
/ 15 мая 2019

Я нашел отличный ответ на параметрические вложенные циклы - Параметрические вложенные циклы в 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]

Обратите внимание, что начальная точка каждого цикла является динамической и изменяется с каждым родительским циклом

Ответы [ 4 ]

2 голосов
/ 15 мая 2019

Вот рекурсивный подход с использованием аргумента по умолчанию. Пронумерованные пункты ниже относятся к пронумерованным комментариям в коде.

  1. (база) Глубина равна нулю. Мы закончили, поэтому выведите комбинацию, comb
  2. (индуктивный) Глубина не менее 1. Для каждого x в диапазоне делегируйте рекурсивному генератору, используя x в качестве новой начальной точки для вложенного диапазона, и добавьте x к комбинации, comb.
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 голосов
/ 15 мая 2019

Это та вещь, для которой я бы использовал рекурсию - как я вижу, вы заметили в своем теге.Примерно так, например:

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)]
2 голосов
/ 15 мая 2019

Вот итерационный подход:

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 и уровня вложенности

2 голосов
/ 15 мая 2019

Это можно сделать рекурсивно (как вы уже догадались), например, таким образом:

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)))
...