我正在寻找有关以下问题的帮助。我有一个小程序,它是更大程序的一部分,我需要以与itertools相同的方式遍历1到10(可能或多或少)的数字数组的每个组合。但是,由于我有一定的限制,因此有必要跳过大量这些组合以节省时间,因为这可能会变得非常大。
这是我的程序
combination = [-1, -1, -1, -1]
len_combination = len(combination)
max_at_index = [0, 2, 2, 1, 2, 1, 2, 1, 3, 1]
len_index = len(max_at_index)
end = 0
def skip(depth):
combination[depth] = combination[depth] + 1
if combination[depth] == len_index:
combination[depth] = 0
for x in range(0, len_index):
if combination[:depth + 1].count(x) > max_at_index[x]:
return True
return False
for i in range(0, len_index):
if skip(0):
continue
for j in range(0, len_index):
if skip(1):
continue
for k in range(0, len_index):
if skip(2):
continue
for l in range(0, len_index):
if skip(3):
continue
print(combination)
此示例有4个项目,每个项目从0到9(从[0,0,0,0]到[9、9、9、9])循环。但是我的变量max_at_index限制了数组中每个索引处允许的值计数。在这里,我们可以使用0 0s,2 1s,2 2s,1 3s等。这很好,我什至可以扩展或缩小max_at_index数组。
我不知道怎么做是使嵌套的for循环递归,这样我就可以扩大或缩小组合的大小以包含更多或更少的元素。
谢谢。
编辑: 根据要求,对我的逻辑进行了一些解释
请考虑以下费用清单
[
[1, 2, 3, 4, 5, 6, 0, 8, 9],
[10, 11, 12, 0, 14, 15, 16, 17, 18, 19],
[0, 21, 22, 23, 24, 25, 26, 27, 28, 29],
[30, 0, 32, 33, 34, 35, 0, 37, 38, 0]
]
从每个数组中选择一个数字时,我必须生成尽可能最小的总数。
我也已经弄清楚了这部分。如果我将每个可能的组合从0,0,0,0到9,9,9,9循环,我可以测试一下是否满足上述条件。我只需要避免循环每个组合,因为它们中的大多数将无用,而且会变得很大
答案 0 :(得分:3)
我认为这是一种可能的实现方式:
def bounded_comb(max_at_index, n):
yield from _bounded_comb_rec(max_at_index, n, [0] * len(max_at_index), [])
def _bounded_comb_rec(max_at_index, n, counts, current):
# If we have enough elements finish
if len(current) >= n:
yield tuple(current)
else:
# For each index and max
for idx, m in enumerate(max_at_index):
# If the max has not been reached
if m > counts[idx]:
# Add the index
counts[idx] += 1
current.append(idx)
# Produce all combinations
yield from _bounded_comb_rec(max_at_index, n, counts, current)
# Undo add the index
current.pop()
counts[idx] -= 1
# Test
max_at_index = [0, 2, 1, 3]
n = 4
print(*bounded_comb(max_at_index, n), sep='\n')
输出:
(1, 1, 2, 3)
(1, 1, 3, 2)
(1, 1, 3, 3)
(1, 2, 1, 3)
(1, 2, 3, 1)
(1, 2, 3, 3)
(1, 3, 1, 2)
(1, 3, 1, 3)
(1, 3, 2, 1)
(1, 3, 2, 3)
(1, 3, 3, 1)
(1, 3, 3, 2)
(1, 3, 3, 3)
(2, 1, 1, 3)
(2, 1, 3, 1)
(2, 1, 3, 3)
(2, 3, 1, 1)
(2, 3, 1, 3)
(2, 3, 3, 1)
(2, 3, 3, 3)
(3, 1, 1, 2)
(3, 1, 1, 3)
(3, 1, 2, 1)
(3, 1, 2, 3)
(3, 1, 3, 1)
(3, 1, 3, 2)
(3, 1, 3, 3)
(3, 2, 1, 1)
(3, 2, 1, 3)
(3, 2, 3, 1)
(3, 2, 3, 3)
(3, 3, 1, 1)
(3, 3, 1, 2)
(3, 3, 1, 3)
(3, 3, 2, 1)
(3, 3, 2, 3)
(3, 3, 3, 1)
(3, 3, 3, 2)
答案 1 :(得分:1)
这是尝试,我限制构造一个值池以从(select_from
)中进行选择,然后构建combinations
:
from itertools import chain, combinations
max_at_index = [0, 2, 2, 1, 2, 1, 2, 1, 3, 1]
select_from = list(chain.from_iterable(n * [i] for i, n in enumerate(max_at_index)))
# [1, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 8, 9]
for comb in combinations(select_from, 4):
print(comb)
这会产生已排序的组合。如果您还需要所有排列,则需要在之后进行(我在这里使用set
'seen
'以避免重复):
from itertools import chain, combinations, permutations
seen_comb = set()
select_from = list(chain.from_iterable(n * [i] for i, n in enumerate(max_at_index)))
for comb in combinations(select_from, 4):
sorted_comb = tuple(sorted(comb))
if sorted_comb in seen_comb:
continue
seen_comb.add(sorted_comb)
seen_perm = set()
for perm in permutations(comb):
if perm in seen_perm:
continue
seen_perm.add(perm)
print(perm)
答案 2 :(得分:1)
我不想表现出任何幻想,但想为您提供递归循环的最简单答案(因为那是您的问题)
combination = [-1, -1, -1, -1]
len_combination = len(combination)
max_depth = 3
max_at_index = [0, 2, 2, 1, 2, 1, 2, 1, 3, 1]
len_index = len(max_at_index)
end = 0
def skip(depth):
combination[depth] = combination[depth] + 1
if combination[depth] == len_index:
combination[depth] = 0
for x in range(0, len_index):
if combination[:depth + 1].count(x) > max_at_index[x]:
return True,combination # Needs to return the state of combination
return False,combination # Needs to return the state of combination
def loop(depth,combination):
if depth == max_depth:
boolean, combination = skip(depth)
if not(boolean):
print (combination)
return combination
else:
for i in range(0, len_index):
boolean, combination = skip(depth)
if not(boolean):
loop(depth+1,combination)
loop(0,combination)
答案 3 :(得分:1)
sympy
还提供了您需要的一切:
from sympy.utilities.iterables import multiset_permutations
max_at_index = [0, 2, 2, 1, 2, 1, 2, 1, 3, 1]
m_set = {i: n for i, n in enumerate(max_at_index) if n != 0}
for perm in multiset_permutations(m_set, 4):
print(perm)
此数据所基于的数据类型是multiset(即元素可能会多次出现但顺序无关紧要的集合)。 sympy
中有一个用于这种数据结构的函数:sympy.utilities.iterables.multiset
from itertools import chain
from sympy.utilities.iterables import multiset
max_at_index = [0, 2, 2, 1, 2, 1, 2, 1, 3, 1]
m_set = multiset(chain.from_iterable(n * [i] for i, n in enumerate(max_at_index)))
# {1: 2, 2: 2, 3: 1, 4: 2, 5: 1, 6: 2, 7: 1, 8: 3, 9: 1}
实际上multiset
仅返回dict
;因此,这更简单:
m_set = {i: n for i, n in enumerate(max_at_index) if n != 0}
# {1: 2, 2: 2, 3: 1, 4: 2, 5: 1, 6: 2, 7: 1, 8: 3, 9: 1}
幸运的是,sympy
还具有permute和combine这些多集的方法,而不会产生任何重复:
from sympy.utilities.iterables import multiset_permutations
for perm in multiset_permutations(m_set, 4):
print(perm)
为了帮助并行化,首先计算组合可能会有所帮助:
from sympy.utilities.iterables import multiset_combinations, multiset_permutations
for comb in multiset_combinations(m_set, 4):
print()
for perm in multiset_permutations(comb):
print(perm)
产生(在每个新组合之后都添加一个空格)
[1, 1, 2, 2]
[1, 2, 1, 2]
[1, 2, 2, 1]
[2, 1, 1, 2]
[2, 1, 2, 1]
[2, 2, 1, 1]
[1, 1, 2, 3]
[1, 1, 3, 2]
[1, 2, 1, 3]
[1, 2, 3, 1]
[1, 3, 1, 2]
[1, 3, 2, 1]
[2, 1, 1, 3]
[2, 1, 3, 1]
[2, 3, 1, 1]
[3, 1, 1, 2]
[3, 1, 2, 1]
[3, 2, 1, 1]
...
[8, 8, 8, 9]
[8, 8, 9, 8]
[8, 9, 8, 8]
[9, 8, 8, 8]