子集列表以添加到另一个列表中的元素

时间:2015-09-17 16:31:49

标签: python math subset-sum

有没有人对使用python解决以下问题的优雅代码和数学有一些想法?

我有两个数字列表:

A=[83.4,108,-240.2]
B=[10.3,96.7,-5.5,-20.4,30.9,2.1,-6.1,51.5,37.7,-25,-10.7,-250.4,-14.2,56.4,-11.5,163.9,-146.6,-2.6,7.9,-13.2]

我知道B可以分为三个包含B元素的列表,这三个列表一起包含B中的所有元素,但三个列表没有重叠元素。这三个列表的总和将加起来A中的三个元素。

我可以做蛮力方法,即将B的元素的所有可能组合创建为三组,但是随着B中元素的数量,可能性的数量很快就会爆炸。我还看了一下背包问题,但这似乎只需要正值。

2 个答案:

答案 0 :(得分:3)

这确实是subset sum problem

的变体
  

computer science中,子集求和问题complexity theorycryptography中的重要问题之一。问题是:给定一组(或多集)整数,是否有一个非空子集,其总和为零?例如,给定集合{-7,-3,-2,5,8},答案是,因为子集{-3,-2,5}总和为零。问题是NP-complete

     

一个等效的问题是:给定一组整数和一个整数 s ,是否有任何非空子集总和为 s

证明它是NP完全的:

  

证明某些新问题是NP完全的最简单方法是首先证明它是在NP中,然后将一些已知的NP完全问题减少到它。

它在NP中,因为它可以在多项式时间内验证:给定一个潜在的解决方案,只需将子集中的数字相加,看它们是否与A中的数字相对应。并且,您可以在多项式时间内将子集问题减少到此问题:给定集合x和目标总和s,让A = [s, sum(x) - s]B = x

它是NP-complete,在一般情况下,使用Python或其他方法无法快速解决这个问题:

  

尽管可以快速验证NP完全问题的任何给定解决方案(在多项式时间内),但首先没有已知的有效方法来定位解决方案;实际上,NP完全问题最显着的特征是没有快速解决它们的问题。也就是说,随着问题的大小增加,使用任何当前已知的algorithm解决问题所需的时间会非常快地增加。因此,确定是否有可能快速解决这些问题,称为P versus NP problem,是今天unsolved problems in computer science的主要问题之一。

答案 1 :(得分:0)

正如@Claudiu所解释的那样,这些问题是NP完整的,你无法以高效或一般的方式解决它们,但在这种情况下,作为一种特殊且不太有效的方式,您可以像itertools模块一样玩:

>>> from itertools import combinations,product,chain

>>> length=len(B)
>>> subset_lenght=[(i,j,k) for i,j,k in combinations(range(1,length),3) if i+j+k==length]
>>> all_combinations={i:combinations(B,i) for i in range(1,length-2)}
>>> for i,j,k in subset_lenght:
...     for t,p,m in product(all_combinations[i],all_combinations[j],all_combinations[k]):
...         if not set(t)&set(p)&set(m) and map(sum,(t,p,m))==A:
...            print chain.fromiterable(t,p,m)

在这种方法中,首先你需要所有可能的长度,这些总和等于你的主列表长度,为此目的你可以使用以下列表理解:

>>> [(i,j,k) for i,j,k in combinations(range(1,len(B)),3) if i+j+k==len(B)]
[(1, 2, 17), (1, 3, 16), (1, 4, 15), (1, 5, 14), (1, 6, 13), (1, 7, 12), (1, 8, 11), (1, 9, 10), (2, 3, 15), (2, 4, 14), (2, 5, 13), (2, 6, 12), (2, 7, 11), (2, 8, 10), (3, 4, 13), (3, 5, 12), (3, 6, 11), (3, 7, 10), (3, 8, 9), (4, 5, 11), (4, 6, 10), (4, 7, 9), (5, 6, 9), (5, 7, 8)]

然后你需要获得主列表的所有组合,长度为1到len(main_list)-3(在这种情况下为17,但由于范围不包含最后一个数字,我们将放置1号磨碎机)因此,由于我们需要使用这些长度来访问这些组合,我们可以使用dict理解来创建一个字典,其中分区长度为键,组合为值:

>>> all_combinations={i:combinations(B,i) for i in range(1,length-2)}

最后,您需要根据subset_lenght项目获取组合,然后选择没有任何交叉点的组合,这些总和等于A中的相应项目。