代码之家  ›  专栏  ›  技术社区  ›  Chad Larson

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

  •  3
  • Chad Larson  · 技术社区  · 9 年前

    有人对使用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 回复  |  直到 9 年前
        1
  •  3
  •   Community CDub    4 年前

    这确实是 subset sum problem :

    在里面 computer science 这个 子集和问题 complexity theory cryptography 问题是:给定一组(或多组)整数,是否存在和为零的非空子集?例如,给定集合{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完成 ,在一般情况下,无法使用Python或其他方式快速解决此问题:

    尽管NP完全问题的任何给定解都可以很快(在多项式时间内)得到验证,但没有已知的有效方法可以首先找到解;事实上,NP完全问题最显著的特点是不知道它们的快速解。也就是说,使用任何当前已知的方法解决问题所需的时间 algorithm 随着问题规模的增长,增长速度非常快。因此,确定是否有可能快速解决这些问题,称为 P versus NP problem ,是校长之一 unsolved problems in computer science 今天

        2
  •  0
  •   Mazdak    9 年前

    正如@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 .