子集和问题的完全多项式近似方案算法

子集和问题的完全多项式近似方案

2012年 2月

一、 作业背景:

1、 子集和问题简介

子集和问题的一个实例是一个对(S,t),其中S 是一个正整数的集合{x 1, x 2, x 3,..., x n },t 为一个正整数。这个判定问题是判定是否存在S 的一个子集,使得其中的数加起来恰为目标值t 。这个问题是NP 完全的。

与此判定问题相联系的最优化问题常常出现于实际应用中。在这种最优化问题中,希望找到

{x1,x2……xn }的一个子集,是其中元素相加之和尽可能地大,但不能大于t 。例如, 假设我们有一辆能装不多于t 磅重的货的卡车,并有n 个不同的盒子要装运,其中第i 个的重量为xi 磅。要在不超过重量极限的前提下,将货尽可能的装满卡车。

2、 开发平台

Eclipse + Jdk 1.7

二、 问题分析:

子集和问题实际上是背包问题的特例, 就是背包问题中项的值和他们的大小相同。课本上给的算法实质就是个动态规划的方法。按照它给的伪代码实现之后,可以得出一个精确解。后来参考了算法导论书中对子集和采取的完全多项式时间近似方案来进行处理。

(一),给出一个指数时间的准确算法

假设对S 的每个子集S ’,都计算出S ’中所有元素的和。接着,在所有元素和不超过t 的子集中选择 其和最接近t 的那个子集。显然,这一算法将返回最优解。但它可能需要指数级的时间。为了实现这个算法,可以采用一个迭代过程:第i 轮迭代中,计算L +x 的所有子集的元素和,计算的基础是{x 1, x 2, x 3,..., x i -1}的所有子集和。在此计算过程中,一旦某个特定的子集S ’的和超过了t, 就没有必要再对它进行处理了,因为S ’的超集都不会成为最优解。下面给出这一策略的一个实现。

EXACT-SUBSET-SUM 的输入为一个集合S={x 1, x 2, x 3,..., x n }和一个目标值t ;这个过程以迭代的方式计算列表L i , 其中列出了{x 1, x 2, x 3,..., x i }的所有子集的和,这些和值都不超过目标值t 。接着,它返回L n 中的最大值。

如果L 是一个由正整数所构成的表,x 是一个正整数,我们用L +x 来表示通过对2中每个元素增i

2,3,5,9},则L+2={3,4,5,7,11}加x 而导出的整数列表。例如, 如果L ={1,。我们也对集合应用这

个记号,因而 S +x ={s +x :x ∈S }

我们还用到一个辅助过程MERGE-LISTS (L ,L ’),返回对它的两个已排序的输入列表L 和L ’合并及删除重复值后所产生的排序列表。

EXACT-SUBSET-SUM (S,t )

1 n ← |S|

2 L0←

3 for I ← 1 to n

do L i ←MERGE-LIST(L i -1, L i +x )

remove from L i every element that is greater than t

return the largest element in L n

即P =P i -1 P i +x i , 可以通过对i 的归纳来证明,表L i 是一个包含P i 中所有值不大于t 的元素有序表。因为L i 的长度可大至2,一般来说EXACT-SUBSET-SUM 是一个指数时间的算法。

(二),完全多项式时间近似方案

对子集和问题我们可以导出一个完全多项式近似方案,在每个列表上L i 被创建后,对它进行”修整”,具体思想是如果其中有两个值比较接近,那么处于寻找近似解的目的,没有必要同时保存这两个数。更准确的说,我们采用一个修正参数δ,满足0

y ≤z ≤y (1+δ)

可以将这样一个z 看成是新表L ' 中代表y 的。每个y 都有一个满足不等式的z 的替换。如:

L ={10,11,12,15,20,21,22,23,24,29}

则可以休整L 而得

L ' ={10,12,15,20,23,29}

其中被删除的值11由10来代表,被删除的值21和22由20代表,被删除的值24由23代表。表的休整版本中的元素也是原表的元素,对一个表加以休整后,可以大大减少表中的元素,同时还可以在表中为每个人被从该表中删除的元素保留一个与其很接近的且略小一些的代表值。

在给定L 和δ的情况下,在时间O(m)内休整一个输入表L =,

假定L 已经排成单调递增序。该过程的输入是一个休整过的有序表。

TRIM (L ,δ){

1 m←|L |

2 L ’ ←

3 last←y 1

4 for i←2 to m

5 do if y i >last.(1+δ)

6 Then append y i onto the end of L ’

7 last←y i

Return L ’

L 的元素被按照单调递增次序加以扫描,而一个数被加入返回的列表L ’中,仅当它是L 的第一个元素,或者如果它不能由最近被放入L ’的数来代表。

给定过程TRIM 后,可以如下构造近似方案。这个过程的输入为包含n 个整数的集合S={x 1, x 2, x 3,..., x n }(以任意次序放置),一个目标整数t,. 以及一个“近似参数”ε,此处:它返回一个值z ,该值落在最优解的 1+ε倍内。

APPROX-SUBSET-SUM (S, t, ∈)

1. n ←|S|

2. L 0←

3. for i ← to n

4. do L i ←MERGE-LISTS(L i -1,L i -1+x i )

5. L i ←TRIM(L i , ∈/2n )

6. Remove from L i every element that is greater than t

7. let z be the largest value in L n

8. return z

第2行将表L 0初始化为仅包含元素0的一个表。第3~ 6行间for 循环的效果是:将L i 作为

一个包含集合P i 的适当休整版本(去掉大于t 的元素)来计算. 因为L i 是从L i -1构造出来的,故必须保证重复的休整不会引入太多的不准确性。

例子如下:

S={104,102,201,101}

且t=308,ε=0.40.修整参数δ为 ε/8=0.05。、APPROX-SUBSET-SUM 在所指示的各行上计算出如下值: 第2行:L 0=

第4行: L 1=

第5行:L 1=

第6行:L 1=

第4行:L 2=

**

第5行:L 2=

第6行:L 2=

第4行:L 3=

第5行:L 3=

第6行:L 3=

第4行:L 4=

第5行:L 4=

第6行:L 4=

该返回值

z *=302作为答案。由于数据很短,所以我们可以看出最优答案为:307=104+102+101,而302是在 =40%内的。实际上,是在其 2% 之内。

关于APPROX-SUBSET-SUM 是关于子集和问题的一个完全多项式时间近似方案的证明,参见《算法导论》 第864页定理35.8。

(三),相关研究

为了做比较分析,在能求出准确解答的情况下,我们还用回溯法写了求精确解的方法。虽然能得出全部解答,但是如果子集项数稍微多一点基本上就得不出答案了。。。

代码如下:

public class SumOfSub {

private int w []; //用来存放子集 private int m ; //用来存放子集和t

private int x []; //用来判断该子集是否能用来构成目标子集和

public SumOfSub(int w[], int m, int n){ this . w = w; this . m = m; this . x = new int [n]; } public void computeSumOfSub(int s, int k, int r){ x [k] = 1; if (s+w [k] == m ){ printResult(k); } else if (s+w [k]+w [k+1]

//仍有别的可能可以继续求出别的方案, 不要当前的w[k]还有没有别的可能, 前提是已经排好序了

if (s+r-w [k]>=m && s+w [k+1]

}

效果如图:

x [k] = 0; computeSumOfSub(s, k+1, r-w [k]); } } public void printResult(int k){ for (int i=0; i

用动态规划的方案来解决这个问题代码如下:

Public class DP{

int [][] v={{0,0}};//用来放v[i][j]从x0到xi 中选出能组成j 的最优组合,这里是尽可能接近j

int left = t;// 将目标子集和大小付给剩下来要达到的大小 for (int i = 1; i left) { System. out .print("****"); v[i][j] = v[i - 1][j]; } else { // 如果当前的项与从前i-1项选出达到j-1的最优组合的和比v[i-1][j]的大 if (s[i] + v[i - 1][j - 1] > v[i - 1][j]) { v[i][j] = v[i - 1][j - 1]+s[i]; left -= s[i]; } else {

} } } } } } v[i][j] = v[i - 1][j]; System. out .println(" 动态规划" ); int j=t; int [] inOrout={}; for (int i=0;i

由于在java 中数组下表一直报错,在c++中修改代码之后得出效果如下:

三、 算法设计与实现:

核心代码如下:

Vector andSort(Vector s,int x,double e,int t){

//添加新元素到表中 int n=s.size(); for (int i=0;i vs=new Vector(); for (int index=0;index(1+e)*(vs.get(vs.size()-1)) && s.get(index)=0 && s.get(j)>temp){ } s.remove(j+1); s.add(j+1, temp); //s.add(j+1, s.get(j)); s.remove(j+1); s.add(j+1,s.get(j)); j--; s.add(s.get(i)+x);

把加入数据的过程和排序过程以及去掉相似项的过程都展示出来了。、

四、 心得总结

1. 递归方法不是解决算法效率问题的好方法。

2. 用java 实现算法的时候对泛型熟悉点时发现写代码还是比较简单的。当然,前提是算法的思

想要理解透彻。

3. 界面的实现用纯手工的方式比较麻烦。界面与后台代码的联络呼应没有很好的解决,就只好

用static 方法和变量来做了。

4. 什么事情还是要早作准备,不要临时抱佛脚。做一件事还是要认真做好。世上无难事,只要

focus ,而且focus 时间久点,应该没有太大问题。

...

五、 参考文献

1. 算法设计技巧与分析

2. 算法导论

子集和问题的完全多项式近似方案

2012年 2月

一、 作业背景:

1、 子集和问题简介

子集和问题的一个实例是一个对(S,t),其中S 是一个正整数的集合{x 1, x 2, x 3,..., x n },t 为一个正整数。这个判定问题是判定是否存在S 的一个子集,使得其中的数加起来恰为目标值t 。这个问题是NP 完全的。

与此判定问题相联系的最优化问题常常出现于实际应用中。在这种最优化问题中,希望找到

{x1,x2……xn }的一个子集,是其中元素相加之和尽可能地大,但不能大于t 。例如, 假设我们有一辆能装不多于t 磅重的货的卡车,并有n 个不同的盒子要装运,其中第i 个的重量为xi 磅。要在不超过重量极限的前提下,将货尽可能的装满卡车。

2、 开发平台

Eclipse + Jdk 1.7

二、 问题分析:

子集和问题实际上是背包问题的特例, 就是背包问题中项的值和他们的大小相同。课本上给的算法实质就是个动态规划的方法。按照它给的伪代码实现之后,可以得出一个精确解。后来参考了算法导论书中对子集和采取的完全多项式时间近似方案来进行处理。

(一),给出一个指数时间的准确算法

假设对S 的每个子集S ’,都计算出S ’中所有元素的和。接着,在所有元素和不超过t 的子集中选择 其和最接近t 的那个子集。显然,这一算法将返回最优解。但它可能需要指数级的时间。为了实现这个算法,可以采用一个迭代过程:第i 轮迭代中,计算L +x 的所有子集的元素和,计算的基础是{x 1, x 2, x 3,..., x i -1}的所有子集和。在此计算过程中,一旦某个特定的子集S ’的和超过了t, 就没有必要再对它进行处理了,因为S ’的超集都不会成为最优解。下面给出这一策略的一个实现。

EXACT-SUBSET-SUM 的输入为一个集合S={x 1, x 2, x 3,..., x n }和一个目标值t ;这个过程以迭代的方式计算列表L i , 其中列出了{x 1, x 2, x 3,..., x i }的所有子集的和,这些和值都不超过目标值t 。接着,它返回L n 中的最大值。

如果L 是一个由正整数所构成的表,x 是一个正整数,我们用L +x 来表示通过对2中每个元素增i

2,3,5,9},则L+2={3,4,5,7,11}加x 而导出的整数列表。例如, 如果L ={1,。我们也对集合应用这

个记号,因而 S +x ={s +x :x ∈S }

我们还用到一个辅助过程MERGE-LISTS (L ,L ’),返回对它的两个已排序的输入列表L 和L ’合并及删除重复值后所产生的排序列表。

EXACT-SUBSET-SUM (S,t )

1 n ← |S|

2 L0←

3 for I ← 1 to n

do L i ←MERGE-LIST(L i -1, L i +x )

remove from L i every element that is greater than t

return the largest element in L n

即P =P i -1 P i +x i , 可以通过对i 的归纳来证明,表L i 是一个包含P i 中所有值不大于t 的元素有序表。因为L i 的长度可大至2,一般来说EXACT-SUBSET-SUM 是一个指数时间的算法。

(二),完全多项式时间近似方案

对子集和问题我们可以导出一个完全多项式近似方案,在每个列表上L i 被创建后,对它进行”修整”,具体思想是如果其中有两个值比较接近,那么处于寻找近似解的目的,没有必要同时保存这两个数。更准确的说,我们采用一个修正参数δ,满足0

y ≤z ≤y (1+δ)

可以将这样一个z 看成是新表L ' 中代表y 的。每个y 都有一个满足不等式的z 的替换。如:

L ={10,11,12,15,20,21,22,23,24,29}

则可以休整L 而得

L ' ={10,12,15,20,23,29}

其中被删除的值11由10来代表,被删除的值21和22由20代表,被删除的值24由23代表。表的休整版本中的元素也是原表的元素,对一个表加以休整后,可以大大减少表中的元素,同时还可以在表中为每个人被从该表中删除的元素保留一个与其很接近的且略小一些的代表值。

在给定L 和δ的情况下,在时间O(m)内休整一个输入表L =,

假定L 已经排成单调递增序。该过程的输入是一个休整过的有序表。

TRIM (L ,δ){

1 m←|L |

2 L ’ ←

3 last←y 1

4 for i←2 to m

5 do if y i >last.(1+δ)

6 Then append y i onto the end of L ’

7 last←y i

Return L ’

L 的元素被按照单调递增次序加以扫描,而一个数被加入返回的列表L ’中,仅当它是L 的第一个元素,或者如果它不能由最近被放入L ’的数来代表。

给定过程TRIM 后,可以如下构造近似方案。这个过程的输入为包含n 个整数的集合S={x 1, x 2, x 3,..., x n }(以任意次序放置),一个目标整数t,. 以及一个“近似参数”ε,此处:它返回一个值z ,该值落在最优解的 1+ε倍内。

APPROX-SUBSET-SUM (S, t, ∈)

1. n ←|S|

2. L 0←

3. for i ← to n

4. do L i ←MERGE-LISTS(L i -1,L i -1+x i )

5. L i ←TRIM(L i , ∈/2n )

6. Remove from L i every element that is greater than t

7. let z be the largest value in L n

8. return z

第2行将表L 0初始化为仅包含元素0的一个表。第3~ 6行间for 循环的效果是:将L i 作为

一个包含集合P i 的适当休整版本(去掉大于t 的元素)来计算. 因为L i 是从L i -1构造出来的,故必须保证重复的休整不会引入太多的不准确性。

例子如下:

S={104,102,201,101}

且t=308,ε=0.40.修整参数δ为 ε/8=0.05。、APPROX-SUBSET-SUM 在所指示的各行上计算出如下值: 第2行:L 0=

第4行: L 1=

第5行:L 1=

第6行:L 1=

第4行:L 2=

**

第5行:L 2=

第6行:L 2=

第4行:L 3=

第5行:L 3=

第6行:L 3=

第4行:L 4=

第5行:L 4=

第6行:L 4=

该返回值

z *=302作为答案。由于数据很短,所以我们可以看出最优答案为:307=104+102+101,而302是在 =40%内的。实际上,是在其 2% 之内。

关于APPROX-SUBSET-SUM 是关于子集和问题的一个完全多项式时间近似方案的证明,参见《算法导论》 第864页定理35.8。

(三),相关研究

为了做比较分析,在能求出准确解答的情况下,我们还用回溯法写了求精确解的方法。虽然能得出全部解答,但是如果子集项数稍微多一点基本上就得不出答案了。。。

代码如下:

public class SumOfSub {

private int w []; //用来存放子集 private int m ; //用来存放子集和t

private int x []; //用来判断该子集是否能用来构成目标子集和

public SumOfSub(int w[], int m, int n){ this . w = w; this . m = m; this . x = new int [n]; } public void computeSumOfSub(int s, int k, int r){ x [k] = 1; if (s+w [k] == m ){ printResult(k); } else if (s+w [k]+w [k+1]

//仍有别的可能可以继续求出别的方案, 不要当前的w[k]还有没有别的可能, 前提是已经排好序了

if (s+r-w [k]>=m && s+w [k+1]

}

效果如图:

x [k] = 0; computeSumOfSub(s, k+1, r-w [k]); } } public void printResult(int k){ for (int i=0; i

用动态规划的方案来解决这个问题代码如下:

Public class DP{

int [][] v={{0,0}};//用来放v[i][j]从x0到xi 中选出能组成j 的最优组合,这里是尽可能接近j

int left = t;// 将目标子集和大小付给剩下来要达到的大小 for (int i = 1; i left) { System. out .print("****"); v[i][j] = v[i - 1][j]; } else { // 如果当前的项与从前i-1项选出达到j-1的最优组合的和比v[i-1][j]的大 if (s[i] + v[i - 1][j - 1] > v[i - 1][j]) { v[i][j] = v[i - 1][j - 1]+s[i]; left -= s[i]; } else {

} } } } } } v[i][j] = v[i - 1][j]; System. out .println(" 动态规划" ); int j=t; int [] inOrout={}; for (int i=0;i

由于在java 中数组下表一直报错,在c++中修改代码之后得出效果如下:

三、 算法设计与实现:

核心代码如下:

Vector andSort(Vector s,int x,double e,int t){

//添加新元素到表中 int n=s.size(); for (int i=0;i vs=new Vector(); for (int index=0;index(1+e)*(vs.get(vs.size()-1)) && s.get(index)=0 && s.get(j)>temp){ } s.remove(j+1); s.add(j+1, temp); //s.add(j+1, s.get(j)); s.remove(j+1); s.add(j+1,s.get(j)); j--; s.add(s.get(i)+x);

把加入数据的过程和排序过程以及去掉相似项的过程都展示出来了。、

四、 心得总结

1. 递归方法不是解决算法效率问题的好方法。

2. 用java 实现算法的时候对泛型熟悉点时发现写代码还是比较简单的。当然,前提是算法的思

想要理解透彻。

3. 界面的实现用纯手工的方式比较麻烦。界面与后台代码的联络呼应没有很好的解决,就只好

用static 方法和变量来做了。

4. 什么事情还是要早作准备,不要临时抱佛脚。做一件事还是要认真做好。世上无难事,只要

focus ,而且focus 时间久点,应该没有太大问题。

...

五、 参考文献

1. 算法设计技巧与分析

2. 算法导论


相关内容

  • 北大算法分析与设计课件8
  • 第8 章近似算法 8.1 近似算法及其近似比 8.2 多机调度问题 8.2.1 贪心的近似算法 822改进的贪心近似算法8.2.2 8.3 货郎问题 8.3.1 最邻近法 8.3.2 最小生成树法 833最小权匹配法8.3.3 8.4 背包问题 8.4.1 841一个简单的贪心算法 8.4.2 多项 ...

  • 组合优化问题简介
  • 在管理科学.计算机科学.分子物理学和生物学以及超大规模集成电路(VLSI)设计.代码设计.图象处理和电子工程等科技领域中,存在着大量组合优化问题.其中许多问题如货郎担问题.图着色问题.设备布局问题以及布线问题等,至今没有找到有效的多项式时间算法.这些问题巳被证明是NP完全问题[1]. 用最优算法如线 ...

  • matlab中回溯算法
  • 第 4 章 回溯 寻找问题的解的一种可靠的方法是首先列出所有候选解,然后依次检查每一个,在检查完所有或部分候选解后,即可找到所需要的解.理论上,当候选解数量有限并且通过检查所有或部分候选解能够得到所需解时,上述方法是可行的.不过,在实际应用中,很少使用这种方法,因为候选解的数量通常都非常大(比如指数 ...

  • 第二章 通信网的拓扑结构
  • 第二章 通信网拓扑结构 通信网的拓扑结构是很基本,也是很重要的问题.拓扑结构是通信网规划和设计的第一层次问题.通信网的拓扑结构可以用图论的模型来代表,主要的问题为最短径和网络流量问题.本章还介绍了线性规划问题的基本概念和方法及网络优化结构模型. §2.1图论基础 图论是应用数学的一个分支,本节介绍它 ...

  • 复合函数高阶导数的整数拆分算法[1]
  • 第39卷.第1期 2009年1月PERIODICALOF 中国海洋大学学报 OCEAN 39(1).149-152Jan.,2009 UNIVERSITYOFCHINA 复合函数高阶导数的整数拆分算法. 刘毅敏1一,唐功友1,张 勇3 (1.中国海洋大学信息科学与工程学院,山东青岛266100:2. ...

  • 城市燃气管网的风险识别
  • 第27卷第7期油气储运 城市燃气管网的风险识别* 刘俊娥" (北京物资学院信息学院) 贾增科 (河北工程大学经济管理学院) 郭章林 (华北科技学院土木系) 张宇兰 (邯郸市煤气公司) 刘俊娥贾增科等:城市燃气管网的风险识别,油气储运,2008,27(7)11-14,59. 摘 要 鉴于风险 ...

  • 粗糙集算法
  • DUFE 管理科学与工程研究方法概论 学号: 专业: 姓名: 粗糙集理论 一.粗糙集的来源与发展 智能信息处理是当前信息科学理论和应用研究中的一个热点领域.由于计算机科学与技术的发展,特别是计算机网络的发展,每日每时为人们提供了大量的信息.信息量的不断增长,对信息分析工具的要求也越来越高,人们希望自 ...

  • 对三种典型分布式任务分配算法的分析_何炎祥
  • 第18卷 第11期 1997年11月小型微型计算机系统M IN I -M I CR O SY ST EM S Vo l. 18N o. 11No v . 1997 对三种典型分布式任务分配算法的分析 何炎祥 罗先林 吴 思 彭堂玉 祝向幸 (武汉大学计算机科学与技术学院 武汉430072) * 摘 ...

  • 基于矩阵分析的融合算法在证据理论中的应用
  • 基于矩阵分析的融合算法在证据理论中的 应用 作者:季明昌 聂荣军 [摘 要]直接采用证据推理组合公式计算传感器信息融合结果,计算量和计算延时会随着发现目标的个数增加而增加,采用两个证据组合的递推计算的方式计算融合结果,提出一种基于矩阵分析的融合算法,利用了matlab 软件及C 语言编程对该方法进行 ...