[英]Efficient way to find partial permutations of list equal to a specific sum
我有一组浮点值,我需要所有可能的排列,它们加在一起会产生一个特定的数字。 此外,我需要所有排列恰好包含八个值。
因此,例如,我有以下一组值:
[0.5, 0.8, 1, 1.5, 2, 2.5, 3, 5, 7.5, 10, 12.5, 15, 17.5, 20, 25, 30]
我需要得到加起来为 20 的 8 个元素的所有排列。
有效的代码如下:
N = [0.5, 0.8, 1, 1.5, 2, 2.5, 3, 5, 7.5, 10, 12.5, 15, 17.5, 20, 25, 30]
target = 20
accum = 0
for i1 in N:
accum += i1
for i2 in N:
accum += i2
for i3 in N:
accum += i3
for i4 in N:
accum += i4
for i5 in N:
accum += i5
for i6 in N:
accum += i6
for i7 in N:
accum += i7
for i8 in N:
accum += i8
if accum > target:
accum -= i8
break
elif accum == target:
print(i1, i2, i3, i4, i5, i6, i7, i8)
accum -= i8
break
accum -= i8
accum -= i7
accum -= i6
accum -= i5
accum -= i4
accum -= i3
accum -= i2
accum -= i1
一些结果是这样给出的:
0.5 0.5 0.5 0.5 0.5 0.5 2 15 0.5 0.5 0.5 0.5 0.5 0.5 15 2 0.5 0.5 0.5 0.5 0.5 1.5 1 15 0.5 0.5 0.5 0.5 0.5 1.5 15 1 0.5 0.5 0.5 0.5 0.5 2 0.5 15 0.5 0.5 0.5 0.5 0.5 2 3 12.5 0.5 0.5 0.5 0.5 0.5 2 12.5 3 0.5 0.5 0.5 0.5 0.5 2 15 0.5 0.5 0.5 0.5 0.5 0.5 2.5 2.5 12.5 0.5 0.5 0.5 0.5 0.5 2.5 5 10 0.5 0.5 0.5 0.5 0.5 2.5 7.5 7.5 0.5 0.5 0.5 0.5 0.5 2.5 10 5 0.5 0.5 0.5 0.5 0.5 2.5 12.5 2.5 0.5 0.5 0.5 0.5 0.5 3 2 12.5
稍后我可以使用 excel 过滤结果。 我不需要复杂的代码,我只需要最终的结果集。 可以看出,这段代码效率极低,因为它需要大量的时间来处理,并且还会给出大量的重复值。 我没有深厚的编程知识,但我知道递归方法效率更高。 我怎样才能得到我需要的结果?
这是一个递归解决方案。 效率增益取决于:
在递归调用中,不要遍历整个列表,只遍历列表的剩余部分。
无需从头开始对每个组合进行完全求和以对其进行测试,只需在迭代时从目标中减去值即可。
在这种情况下,可能会进一步提高效率,这取决于列表仅包含正数这一事实,因此如果特定值会超出目标,那么您不需要进行递归调用( elif val < target
测试)。 如果没有这个事实,那部分代码将需要是无条件的,然后会花费更长的时间。
测试用例在一台旧机器上耗时 0.04 秒。
def yield_combos(N, num, target):
for i, val in enumerate(N):
if num == 1:
if val == target:
yield [val]
elif val < target:
for j in yield_combos(N[i:], num - 1, target - val):
yield [val] + j
N = [0.5, 0.8, 1, 1.5, 2, 2.5, 3, 5, 7.5, 10, 12.5, 15, 17.5, 20, 25, 30]
target = 20
num = 8
for combo in yield_combos(N, num, target):
print(f"combo={combo} length={len(combo)} total={sum(combo)}")
这给出了:
combo=[0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 2, 15] length=8 total=20.0
combo=[0.5, 0.5, 0.5, 0.5, 0.5, 1, 1.5, 15] length=8 total=20.0
combo=[0.5, 0.5, 0.5, 0.5, 0.5, 2, 3, 12.5] length=8 total=20.0
combo=[0.5, 0.5, 0.5, 0.5, 0.5, 2.5, 2.5, 12.5] length=8 total=20.0
combo=[0.5, 0.5, 0.5, 0.5, 0.5, 2.5, 5, 10] length=8 total=20.0
combo=[0.5, 0.5, 0.5, 0.5, 0.5, 2.5, 7.5, 7.5] length=8 total=20.0
combo=[0.5, 0.5, 0.5, 0.5, 0.5, 5, 5, 7.5] length=8 total=20.0
combo=[0.5, 0.5, 0.5, 0.5, 1, 1, 1, 15] length=8 total=20.0
combo=[0.5, 0.5, 0.5, 0.5, 1, 1.5, 3, 12.5] length=8 total=20.0
combo=[0.5, 0.5, 0.5, 0.5, 1, 2, 2.5, 12.5] length=8 total=20.0
combo=[0.5, 0.5, 0.5, 0.5, 1, 2, 5, 10] length=8 total=20.0
combo=[0.5, 0.5, 0.5, 0.5, 1, 2, 7.5, 7.5] length=8 total=20.0
... etc ...
以上是在允许重复元素的基础上。 如果不是,则在递归调用中使用N[i+1:]
而不是N[i:]
,尽管在这种情况下没有针对目标 20 的解决方案。
另请注意,这仅按照输入列表中索引增加的顺序给出每个解决方案(在这种情况下,值增加,因为输入列表恰好是排序的),而不是它们的每个排列,基于额外排列是“重复的”的理解应该避免该问题所暗示的价值观”。 但是,如果需要额外的排列,那么最有效的做法是最初完全按照上述方式进行搜索(而不是在递归调用中循环遍历整个列表),然后仅排列解决方案的元素作为最后一步。
import itertools
def func(list, size_perm, target):
#Generate all permutations
perms = itertools.permutations(list, size_perm)
#iterate of all permutations and print only if sum == target
for item in perms:
if sum(item) == target:
print(item)
N = [0.5, 0.8, 1, 1.5, 2, 2.5, 3, 5, 7.5, 10, 12.5, 15, 17.5, 20, 25, 30]
func(N, 8, 20)
不太确定这是否是最有效的方法,但这应该有效。 让我知道它是否不起作用。
最简单的方法是使用itertools
和列表推导:
import itertools
# Inputs
N = [0.5, 0.8, 1, 1.5, 2, 2.5, 3, 5, 7.5, 10, 12.5, 15, 17.5, 20, 25, 30]
target = 20
# Operation
list_sum=[item for item in itertools.product(N, repeat=8) if sum(item)==target]
# Printing
print(list_sum)
要查看您得到的 output 的种类,如果仅使用 2 个元素,则得到以下 output:
[(2.5, 17.5), (5, 15), (7.5, 12.5), (10, 10), (12.5, 7.5), (15, 5), (17.5, 2.5)]
这不是最快的解决方案(递归 function 更佳)。 请注意,我提供了所有可能的排列,因为我假设元素的顺序可能很重要。 否则,可以减少执行时间。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.