繁体   English   中英

找到等于特定总和的列表部分排列的有效方法

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM