子集合加总问题(Subset sum problem)

两年前在作一个ERP项目时,有个客户提出,根据订单给出的每一个货品的数量(库里存的有每一个货品最小包装的重量,体积和长宽高),输入包装箱的承重和体积和长宽高,而后我须要大家提供一个包装方案,要求结果是用最少的纸箱来包装这些货物并给出包装步骤,最好能用3D图表示。当时我就蒙了,这算法要是靠外包公司写,不知道写不写的出来,因而给客户扯了个相似的背包问题,而后告诉它这是NP彻底问题,很是复杂,货物不少的时候你的服务器不必定能承受计算的压力。而后客户也蒙了,再而后这个需求就取消了。其实,这个究竟是不是背包问题,是否是属于NP彻底问题,我也不敢讲,没仔细的推导过。当时只是以为场景相似,因此就举了个这样的例子给他。如今得回顾一下这个问题,看看靠本身得知识能不能把这个问题推导至背包问题。可是要说背包问题,仍是先说子集合加总问题吧。
程序员


给一个整数集合,问是否存在某个非空子集,使得子集内中的数字和为0?例:给定集合{−7, −3, −2, 5, 8},答案是存在,由于子集{−3, −2, 5}的数字和是0。这个问题被证实是属于NP彻底问题。若是把这个问题看成一个特例,那范围更广的问题就是 “给一个整数集合A,问是否存在某个非空子集s,使得子集内中的数字和为B” ,当咱们把B设为0时,就是咱们上面的那个问题了。而第二个问题,属于背包问题的一个特例。
算法

子集特定合问题的最简单算法(把全部的子集所有列出来,而后对每个子集作运算)时间复杂度是(2N),由于有2N个非空子集,而后对于每个子集,咱们最多会作N次加法运算。数组

有人搞出另一个算法,这个算法的时间复杂度是(2N/2 )  (Horowitz and Sahni ,1972服务器

对他们在1972年提出的这个算法的简单解释以下,学习

将任意给定元素的集合N分红平分红两个集合,每一个集合有N/2个元素,而后计算出每一个集合的子集的和加总结果,得出两个列表,对这两个列表用比较排序法,而后咱们再把两个列表的元素相加,看是否有其中一对能获得要求的结果。这个加法运算是从第一个列表的头和第二个列表的尾开始。spa


经过动态规划,还能搞出一个伪多项式时间复杂度的算法,对于断定是否有子集和为0的算法的大概的解释是排序

序列为 x1, ..., xn递归

A为集合中正数的总和it

B为集合中负数的总和基础

一个方法Q(i, s) 输出是否有子集(x1, ..., xi )的和等于s。

当s=0时,这个方法给出的结果时子集i的和是否等于0

那很明显,当s < A 或者 s > B 时 ,Q(i, s)输出否,也就是两个集合不符合这个条件的均可以不被计算和存储。

构造一个数组来保存符合1 ≤ i ≤ N 和 A ≤ s ≤ B Q(i,s )结果。

递归的把结果放到这个数组中,当i=1 , A ≤ s ≤ 

Q(1,s) := (x1 == s)

当i >1时

(,s) := i  − 1, sor (xi == sor i  − 1, s − xi)   for A ≤ s ≤ B

当咱们每次往数组中插入值的时候,咱们都已知Q的值,因此最终算法的时间复杂度是 − ) ). 也就是当计算全部的值的时间复杂度是(k),总体算法的复杂度是k+2) ,可是由于A,B值的大小问题,这个算法不符合时间复杂度理论的多项式算法。


Wiki上面还有个线性时间的相似问题的算法,有些改动,可是那个须要时间参透,本身都没太看明白的东西仍是不要摆出来了。


小总结,研究这些问题,或者,学习这些问题的分析方法和分析思路是很是有好处的,作为一个什么都干的程序员,和客户谈需求的时候必需要掌握一些问题的类型,或者能快速的将客户的问题概括到某个已被证实复杂度的问题上(每每客户提出的问题都是这类问题的某个特例),如此才可有理由的答应或者拒绝客户提出的需求。并且,在处理问题的时候,也能快速的想出比较合适的算法来实现某些功能。总之,要想提升代码的质量和水平,基础仍是要扎实的学的。

相关文章
相关标签/搜索