Есть ли способ эффективно перебирать «вложенные» комбинации в Python? - PullRequest
0 голосов
/ 18 июня 2019

Я не уверен, как определить проблему, которую хочу решить, но из комбинации цифр, например:

(4, 3, 2)

Я хочу создать итератор, который обрабатывает все «вложенные» комбинации этих чисел. Под этим я подразумеваю, что он повторяется:

(0, 0, 0), (0, 1, 0), (0, 2, 0), (0, 3, 0), (0, 1, 1), (0, 1, 2) , (0, 2, 1), (0, 2, 2), ...

(1, 0, 0), (1, 1, 0), (1, 2, 0), (1, 3, 0), (1, 1, 1), (1, 1, 2) , (1, 2, 1), (1, 2, 2), ...

...

(4, 0, 0), (4, 1, 0), (4, 2, 0), (4, 3, 0), (4, 1, 1), (4, 1, 2) , (4, 2, 1), (4, 2, 2), ...

Предпочтительно он также может быть ограничен максимальной суммой емкости во время генерации комбинаций (то есть сумма (комбинация) <емкость). </p>

Я создал рекурсивный алгоритм, который генерирует эти комбинации, но он очень медленный и надеюсь, что есть более эффективный метод.

import numpy as np 

def combinations(current, c, c_set):
    c_rec = c.copy()
    if(current == 0):
        while(c_rec[current] + 1 <= numbers[current] and c_rec[current] + 1 < capacity):
            c_rec[current] += 1
            c_set.append(c_rec.copy())

    while(c_rec[current] + 1 <= numbers[current] and c_rec[current] + 1 < capacity):
        c_rec[current] += 1
        combinations(current - 1, c_rec, c_set)
        c_set.append(c_rec)

numbers = (4,3,2)
n = len(numbers)
capacity = 7
c_init = np.zeros(n)
c_set = [c_init]            
combinations(n - 1, c_init, c_set)

Ответы [ 3 ]

1 голос
/ 18 июня 2019

Вы можете использовать itertools.product для этого

from itertools import product

li = [4, 3, 2]

#Create a list of ranges
res = [range(item+1) for item in li]
#[range(0, 5), range(0, 4), range(0, 3)]

#Calculate cartesian product between elements of each list
prod = product(*res)

#Iterate over the elements
for item in prod:
    print(item)

Выход будет

(0, 0, 0)
(0, 0, 1)
(0, 0, 2)
(0, 1, 0)
(0, 1, 1)
...
(1, 0, 0)
(1, 0, 1)
(1, 0, 2)
(1, 1, 0)
(1, 1, 1)
...
(2, 0, 0)
(2, 0, 1)
(2, 0, 2)
(2, 1, 0)
(2, 1, 1)
.....
(3, 0, 0)
(3, 0, 1)
(3, 0, 2)
(3, 1, 0)
(3, 1, 1)
.....
0 голосов
/ 18 июня 2019

Вы можете использовать рекурсию с генератором:

start = (4, 3, 2)
def groups(d):
  yield d
  for i, [a, b] in enumerate(zip(d, start)):
    if a < b:
      yield from groups(tuple([*d[:i], d[i]+1, *d[i+1:]]))

result = set(groups((0, 0, 0)))

Выход:

[(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 1, 0), (0, 1, 1), (0, 1, 2), (0, 2, 0), (0, 2, 1), (0, 2, 2), (0, 3, 0), (0, 3, 1), (0, 3, 2), (1, 0, 0), (1, 0, 1), (1, 0, 2), (1, 1, 0), (1, 1, 1), (1, 1, 2), (1, 2, 0), (1, 2, 1), (1, 2, 2), (1, 3, 0), (1, 3, 1), (1, 3, 2), (2, 0, 0), (2, 0, 1), (2, 0, 2), (2, 1, 0), (2, 1, 1), (2, 1, 2), (2, 2, 0), (2, 2, 1), (2, 2, 2), (2, 3, 0), (2, 3, 1), (2, 3, 2), (3, 0, 0), (3, 0, 1), (3, 0, 2), (3, 1, 0), (3, 1, 1), (3, 1, 2), (3, 2, 0), (3, 2, 1), (3, 2, 2), (3, 3, 0), (3, 3, 1), (3, 3, 2), (4, 0, 0), (4, 0, 1), (4, 0, 2), (4, 1, 0), (4, 1, 1), (4, 1, 2), (4, 2, 0), (4, 2, 1), (4, 2, 2), (4, 3, 0), (4, 3, 1), (4, 3, 2)]
0 голосов
/ 18 июня 2019

Возможно, я не до конца понял ваш вопрос, но разве не решит вашу проблему простая вложенная структура цикла for?

for x in range(4):
    for y in range(3):
        for z in range(2):
            print((x, y, z))
...