查找匹配子集与子集和(Subset Sum Problem)--动态规划实现

问题和实现引自资料:https://en.wikipedia.org/wiki/Subset_sum_problem

介绍

子集和问题(英语:Subset sum problem),又称子集合加总问题,是计算复杂度理论和密码学中一个很重要的问题。问题可以描述为:给一个整数集合,问是否存在某个非空子集,使得子集内中的数字和为0。例:给定集合{−7, −3, −2, 5, 8},答案是YES,因为子集{−3, −2, 5}的数字和是0。这个问题是NP完全问题,且或许是最容易描述的NP完全问题。

一个等价的问题是:给一个整数集合和另一个整数s,问是否存在某个非空子集,使得子集中的数字和为s。子集合加总问题可以想成是背包问题的一个特例。

伪代码思路

 initialize a list S to contain one element 0.
 for each i from 1 to N do
   let T be a list consisting of xi + y, for all y in S
   let U be the union of T and S
   sort U
   make S empty 
   let y be the smallest element of U 
   add y to S 
   for each element z of U in increasing order do
      //trim the list by eliminating numbers close to one another
      //and throw out elements greater than s
     if y + cs/N < z ≤ s, set y = z and add z to S 
 if S contains a number between (1 − c)s and s, output yes, otherwise no

代码实现

在wiki提供的解法上进行略微改造,会列出所有的子集结果

import time


if __name__ == '__main__':
    x_list = [1150, 495, 995, 995, 995, 995, 100, 750, 3305, 75, 510, 3265, 2145, 1935, 140, 140, 15, 1330, 2800, 1250, 350, 850, 110]
    target = 8270
    error = 0.009

    start_time = time.time()

    S = [(0, [])]
    for x in x_list:
        T = [(x + y, y_list + [x]) for y, y_list in S]
        U = T + S
        U.sort(key=lambda a: a[0])
        y, y_list = U[0]
        S = [(y, y_list)]
        for z, z_list in U:
            if y + error * target / len(x_list) < z <= target:
                y = z
                S.append((z, z_list))
        else:
            while abs(S[-1][0] - target) < error:
                print(S.pop())
    print(time.time() - start_time)

你可能感兴趣的:(Python)