根据this answer,我修改了代码以适应这个问题。
请注意,数组是按降序提供给函数的。
def combinations_cutoff(array, tuple_length, s, prev_array=[], n_skips=[]):
if len(prev_array) == tuple_length:
if sum(prev_array) == s:
return [prev_array]
return []
combs = []
for i, val in enumerate(array):
prev_array_extended = prev_array.copy()
prev_array_extended.append(val)
if sum(prev_array_extended) > s:
n_skips[0] += 1
print(f"cutoff! arr={prev_array_extended}, s={sum(prev_array_extended)}, skip #{n_skips[0]}")
continue
combs += combinations_cutoff(array[i+1:], tuple_length, s, prev_array_extended, n_skips=n_skips)
return combs
def main():
n = 20
k = 3
s = 15
arr = np.arange(n)[::-1]
n_skips = [0]
gen = combinations_cutoff(arr, k, s, n_skips=n_skips)
lst = []
for c in gen:
lst.append(c)
print(c)
print(f"total solutions: {len(lst)}, skips={n_skips[0]}")
if __name__ == "__main__":
main()
输出:
cutoff! arr=[19], s=19, skip #1
cutoff! arr=[18], s=18, skip #2
cutoff! arr=[17], s=17, skip #3
cutoff! arr=[16], s=16, skip #4
cutoff! arr=[15, 14], s=29, skip #5
cutoff! arr=[15, 13], s=28, skip #6
cutoff! arr=[15, 12], s=27, skip #7
cutoff! arr=[15, 11], s=26, skip #8
cutoff! arr=[15, 10], s=25, skip #9
cutoff! arr=[15, 9], s=24, skip #10
cutoff! arr=[15, 8], s=23, skip #11
cutoff! arr=[15, 7], s=22, skip #12
cutoff! arr=[15, 6], s=21, skip #13
cutoff! arr=[15, 5], s=20, skip #14
cutoff! arr=[15, 4], s=19, skip #15
cutoff! arr=[15, 3], s=18, skip #16
cutoff! arr=[15, 2], s=17, skip #17
cutoff! arr=[15, 1], s=16, skip #18
cutoff! arr=[14, 13], s=27, skip #19
cutoff! arr=[14, 12], s=26, skip #20
cutoff! arr=[14, 11], s=25, skip #21
cutoff! arr=[14, 10], s=24, skip #22
cutoff! arr=[14, 9], s=23, skip #23
cutoff! arr=[14, 8], s=22, skip #24
cutoff! arr=[14, 7], s=21, skip #25
cutoff! arr=[14, 6], s=20, skip #26
cutoff! arr=[14, 5], s=19, skip #27
cutoff! arr=[14, 4], s=18, skip #28
cutoff! arr=[14, 3], s=17, skip #29
cutoff! arr=[14, 2], s=16, skip #30
cutoff! arr=[13, 12], s=25, skip #31
cutoff! arr=[13, 11], s=24, skip #32
cutoff! arr=[13, 10], s=23, skip #33
cutoff! arr=[13, 9], s=22, skip #34
cutoff! arr=[13, 8], s=21, skip #35
cutoff! arr=[13, 7], s=20, skip #36
cutoff! arr=[13, 6], s=19, skip #37
cutoff! arr=[13, 5], s=18, skip #38
cutoff! arr=[13, 4], s=17, skip #39
cutoff! arr=[13, 3], s=16, skip #40
cutoff! arr=[13, 2, 1], s=16, skip #41
cutoff! arr=[12, 11], s=23, skip #42
cutoff! arr=[12, 10], s=22, skip #43
cutoff! arr=[12, 9], s=21, skip #44
cutoff! arr=[12, 8], s=20, skip #45
cutoff! arr=[12, 7], s=19, skip #46
cutoff! arr=[12, 6], s=18, skip #47
cutoff! arr=[12, 5], s=17, skip #48
cutoff! arr=[12, 4], s=16, skip #49
cutoff! arr=[12, 3, 2], s=17, skip #50
cutoff! arr=[12, 3, 1], s=16, skip #51
cutoff! arr=[11, 10], s=21, skip #52
cutoff! arr=[11, 9], s=20, skip #53
cutoff! arr=[11, 8], s=19, skip #54
cutoff! arr=[11, 7], s=18, skip #55
cutoff! arr=[11, 6], s=17, skip #56
cutoff! arr=[11, 5], s=16, skip #57
cutoff! arr=[11, 4, 3], s=18, skip #58
cutoff! arr=[11, 4, 2], s=17, skip #59
cutoff! arr=[11, 4, 1], s=16, skip #60
cutoff! arr=[11, 3, 2], s=16, skip #61
cutoff! arr=[10, 9], s=19, skip #62
cutoff! arr=[10, 8], s=18, skip #63
cutoff! arr=[10, 7], s=17, skip #64
cutoff! arr=[10, 6], s=16, skip #65
cutoff! arr=[10, 5, 4], s=19, skip #66
cutoff! arr=[10, 5, 3], s=18, skip #67
cutoff! arr=[10, 5, 2], s=17, skip #68
cutoff! arr=[10, 5, 1], s=16, skip #69
cutoff! arr=[10, 4, 3], s=17, skip #70
cutoff! arr=[10, 4, 2], s=16, skip #71
cutoff! arr=[9, 8], s=17, skip #72
cutoff! arr=[9, 7], s=16, skip #73
cutoff! arr=[9, 6, 5], s=20, skip #74
cutoff! arr=[9, 6, 4], s=19, skip #75
cutoff! arr=[9, 6, 3], s=18, skip #76
cutoff! arr=[9, 6, 2], s=17, skip #77
cutoff! arr=[9, 6, 1], s=16, skip #78
cutoff! arr=[9, 5, 4], s=18, skip #79
cutoff! arr=[9, 5, 3], s=17, skip #80
cutoff! arr=[9, 5, 2], s=16, skip #81
cutoff! arr=[9, 4, 3], s=16, skip #82
cutoff! arr=[8, 7, 6], s=21, skip #83
cutoff! arr=[8, 7, 5], s=20, skip #84
cutoff! arr=[8, 7, 4], s=19, skip #85
cutoff! arr=[8, 7, 3], s=18, skip #86
cutoff! arr=[8, 7, 2], s=17, skip #87
cutoff! arr=[8, 7, 1], s=16, skip #88
cutoff! arr=[8, 6, 5], s=19, skip #89
cutoff! arr=[8, 6, 4], s=18, skip #90
cutoff! arr=[8, 6, 3], s=17, skip #91
cutoff! arr=[8, 6, 2], s=16, skip #92
cutoff! arr=[8, 5, 4], s=17, skip #93
cutoff! arr=[8, 5, 3], s=16, skip #94
cutoff! arr=[7, 6, 5], s=18, skip #95
cutoff! arr=[7, 6, 4], s=17, skip #96
cutoff! arr=[7, 6, 3], s=16, skip #97
cutoff! arr=[7, 5, 4], s=16, skip #98
[14, 1, 0]
[13, 2, 0]
[12, 3, 0]
[12, 2, 1]
[11, 4, 0]
[11, 3, 1]
[10, 5, 0]
[10, 4, 1]
[10, 3, 2]
[9, 6, 0]
[9, 5, 1]
[9, 4, 2]
[8, 7, 0]
[8, 6, 1]
[8, 5, 2]
[8, 4, 3]
[7, 6, 2]
[7, 5, 3]
[6, 5, 4]
total solutions: 19, skips=98
该函数始终返回长度为tuple_length的所有组合的列表,以prev_array开头并以array的组合结尾,总和为s。
在每一步,我们都会考虑prev_array 是否“准备就绪”(= 长度和总和正确)。如果是这样,它应该被退回。
如果是sum没有达到,并且数组已经足够长,那么它是无效的。
现在,我们留下了一个不够长的prev_array,我们现在考虑应该向其中添加哪个元素。
元素的选项由递归定义,所有array的元素,因此我们迭代它们并尝试它们。
当一个元素被试用时,以后只能尝试它后面的元素。
尝试一个元素意味着使用prev_array_extended 调用递归,即prev_array 后跟所选元素。
现在到主菜 - 截止 - 在调用递归之前,我们可以考虑 prev_array 的总和。由于array 保证是降序的[注意它是如何从main 调用的],如果候选元组的任何前缀已经通过s,那么继续检查该候选元组的可能性是没有意义的,我们可以停下来继续使用更小的值来推动prev_array的结束。
n_skips 只是一个丑陋的黑客来计算截止完成了多少次。