实验报告
课程名称:算法设计与分析实验名称:解0-1背包问题任课教师:王锦彪专业:计算机应用技术班级: 2011 学号: ****** 姓名:严焱心完成日期: 2011年11月
一、实验目的:
掌握动态规划、贪心算法、回溯法、分支限界法的原理,并能够按其原理编程实现解决0-1背包问题,以加深对上述方法的理解。
二、实验内容及要求:
1. 要求分别用动态规划、贪心算法、回溯法和分支限界法求解0-1背包问题;
2. 要求显示结果。
三、实验环境和工具:
操作系统:Windows7
开发工具:Eclipse3.7.1 jdk6
开发语言:Java
四、实验问题描述:
0/1背包问题:现有n 种物品,对1<=i<=n ,第i 种物品的重量为正整数W i ,价值为正整数V i ,背包能承受的最大载重量为正整数C ,
现要求找出这n 种物品的一个子集,使得子集中物品的总重量不超过C 且总价值尽量大。
动态规划算法描述: 根据问题描述,可以将其转化为如下的约束条件和目标函数:
⎪⎩⎪⎨⎧≤≤∈≤∑∑==)1}(1,0{C max 1
1n i x x w x v i
n
i i i n
i i
i
寻找一个满足约束条件,并使目标函数式达到最大的解向量),......,,,(321n x x x x X =,使得C 1∑=≤n i i i x w ,而且∑=n
i i i x v 1达到最大。
0-1背包问题具有最优子结构性质。
假设),......,,,(321n x x x x 是所给的问题的一个最优解,则),......,,(32n x x x 是下面问题的一个最优解:∑∑==⎪⎩⎪⎨⎧≤≤∈-≤n i i i i
n
i i i x v n i x x w x w 22
11max )2}(1,0{C 。
如果不是的话,设),......,,(32n y y y 是这个问题的一个最优解,则∑∑==>n i n i i i i i x v y v 22
,且∑=≤+
n i i i y w x w 211C 。
因此,∑∑∑====+>+n i i i n i n i i i i i x v x v x v y v x v 1
221111,这说明),........,,,(321n y y y x 是所给
的0-1背包问题比),........,,,(321n x x x x 更优的解,从而与假设矛盾。
按照上面的情况,可以得到递推公式:设最优值为m(i,j)。
⎩⎨⎧+-+++=}),1(),,1(max{),1(),(i i v w j i m j i m j i m j i m i
i w w ≥<≤j j 0
⎩⎨⎧=n v j n m 0),( i
i w w ≥<≤j j 0 贪心算法描述:计算每种物品单位重量的价值;将尽可能多的单位重量价值最高的物品装入背包;如果单位重量价值最高的物品全部装入背包后,背包的总重量小于c ,则选择单位重量次高的物品并尽可能多的装入背包;依次进行下去,直到背包装满为止。
回溯算法描述:回溯法从根结点出发,以深度优先的方式搜索整个解空间。
开始结点成为一个活结点,同时也成为当前的扩展结点。
在当前的扩展结点处,搜索向纵深方向移至一个新结点。
新结点就成为一个新的活结点,并成为当前扩展结点。
如果在当前的扩
展结点处不能再向纵深方向移动,则当前扩展结点就成为死结点。
此时往回移动至最近的一个活结点处,并使这个活结点成为当前的扩展结点。
回溯法递归地在解空间中搜索,直至找到所要求的解或解空间中已没有活结点时为止。
针对所给问题,定义问题的解空间;确定易于搜索的解空间结构;以深度优先的方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索。
分支限界算法描述:分支限界法以广度优先的方式搜索问题的解空间树,每一个活结点只有一次机会成为扩展节点。
活结点一旦成为扩展节点,就一次性产生其所有儿子节点,那些导致不可行解或导致非最优解的儿子节点被舍去,其余儿子节点被加入到活结点表中。
此后,从活结点表中取下一节点成为当前扩展节点,并重复上述节点扩展过程。
这个过程一直持续到找到所需的解或活结点表为空时为止。
五、实验原理、结果与结论:
1.1、动态规划求解0-1背包问题
实验结果:
实验原理:动态规划方法建立在最优原则的基础上,以空间换取时间,将一个问题的解决方案视为一系列决策的结果。
在动态规划中,还要看每个最优决策序列中是否包含一个最优子序列。
即无论过程的初始状态和初始决策是什么,其余的决策必须相对于初始决策所产生的状态构成一个最优决策。
如果把第i个物品装入背包,则背包中物
品的价值就等于把前i-1个物品装入容量为i w j -的背包中的价值加
上第i 个物品的价值i v ;如果第i 个物品没有装入背包,则背包中物
品的价值就是等于把前i-1个物品装入容量为j 的背包中所取得的价值。
取二者中价值较大者作为把前i 个物品装入容量为j 的背包中的最优解。
2.1、贪心算法求解背包问题:
实验结果:
实验原理:贪心算法也要求问题具有最有子结构性质。
0-1背包问题不能用贪心算法来求解。
在选择物品)1(n i i ≤≤装入背包时,可以选择一部分,而不一定要全部装入背包。
贪心法得不到最优解,无法保证最终能将背包装满,部分闲置的背包容量使背包单位重量的价值降低了。
3.1、回溯法求解背包问题:
实验结果:
实验原理:用回溯法解0-1背包问题时,用到状态空间树。
在搜索状态空间树时,只要其左儿子结点是一个可行结点,搜索就进入其左子树。
当右子树有可能包含最优解时才进入右子树搜索,否则将右子树剪去。
计算右子树中解的上界将剩余物品依其单位重量价值排序,
然后依次装入物品,直至装不下时,再装入该物品的一部分而装满背包。
由此得到的价值是右子树中解的上界,用此值来剪枝。
在搜索状态空间树时,由函数Backtrack控制。
在函数中是利用递归调用的方法实现了空间树的搜索。
4.1、分支界限法求解背包问题:
实验结果:
实验原理:在类Knap中有四个函数:上界函数Bound (),计算节点所对应价值的上界;函数AddLiveNode()是将一个新的活结点插入到子集树和优先队列中;KnapsackF()实施对子集树的优先队列式分支界限搜索。
其中假定物品依其单位价值从大到小已经排好序。
cw 是该结点的重量;cp是该结点的价值;up是价值上界。
算法的while 循环不断扩展结点,直到子集树的一个叶结点成为扩展结点为止。
此时优先队列中所有活结点的价值上界均不超过该叶结点的价值。
因此该叶结点相应的解为问题的最优解。
在while循环内部,算法首先检查当前扩展结点的左儿子结点的可行性。
如果该左儿子结点是可行结点,则将它加入到子集树和活结点优先队列中。
当前扩展结点的右儿子结点一定是可行结点,仅当右儿子结点满足上界约束时才将它加入子集树和活结点优先队列。
六、时空效率分析:
动态规划法:由于函数Knapsack中有一个两重for循环,所以时间复杂度为O[nc]。
空间复复杂度也是O[nc],即O(nc)。
而
Traceback 需要O(n)。
贪心算法: 算法的时间上界O(nlogn)。
回溯法:上界函数Bound 需要O(n)时间,在最坏情况下有)2(n O 个右儿子结点需要计算上界,所以解0-1背包问题的回溯算法所需要的计算时间为)2(n n O 。
分支限界法:计算上界的函数Bound 需要O(n)的时间,而且在最坏情况下有)2(n O 个结点需要计算上界,所以在最坏情况下的时间复杂度为)2(n n O 。
四种算法比较:
从计算复杂性理论看,背包问题是NP 完全问题。
回溯法和分枝限界法等可以得到问题的最优解,可是计算时间太慢;动态规划法也可以得到最优解,当n m 2 时,算法需要)2(n n O 的计算时间,计算速度慢;采用贪心算法,不一定是最优解。
以上几种方法中回溯法、动态规划法、贪心法都广泛地应用到不同的实际问题中,并在应用中不断地改进。
七、实验总结:
在做本次实验之前,自己对动态规划、贪心、回溯法、分支限界法的原理不是非常的理解,花了很多时间看了课本上的相关内容。
不过那是C++代码,有些封装好的方法在Java里好像没能找到对应的方法,所以只能自己编写同功能的对应方法。
同时课本所提供的代码也是不能直接翻译过来用,当你懂得算法的基本原理后,你会发现数组下标会出错,课本所提供的代码数组下标一般都是从1开始,而我们输入的数据数组下标默认都是从0开始,所以在参考课本所提供的代码的同时,必须结合算法的实际情况对代码中的相关变量进行修改,这样才能充分利用课本所提供的代码完成本次实验。
通过本次试验,自己基本上掌握上述算法解0-1背包问题的原理,达到实验的目的。