【算法总结】-蛮力,贪心,动态规划 话谈背包问题

       最近在和小伙伴们研究背包问题,对于背包问题的存在,相信已经算是一个经典问题了。本篇我们则从多方面来对背包问题有一个宏观的了解。
首先我们来简要描述一下何为背包问题?

经典问题

       给定n个重量为W1,W2,W3,…,Wn,价值为V1,V2,V3…,Vn的物品和一个承重为W的背包,求这些物品中一个最有价值的子集,并且要能够装到背包中。
实际数值举例:

物品 重量 价值/美元
1 2 12
2 1 10
3 3 20
4 2 15

背包所能承载的重量W=5

一、蛮力法

       蛮力法是一种简单直接地解决问题的办法,常常直接基于问题的描述和所涉及的概念定义。
       对于蛮力法解决背包问题,其实就是我们所谓的穷举列表法,把所有的可能情况都列举出来,从所有的情况中找到最大值。
如下表则是应用穷举法的所有可能情况:

子集 总重量 总价值/美元
0 0
{1} 2 12
{2} 1 10
{3} 3 20
{4} 2 15
{1,2} 3 32
{1,3} 5 32
{1,4} 4 27
{2,3} 4 30
{2,4} 3 25
{3,4} 5 35
{1,2,3} 6 超重,不可行
{1,2,4} 5 37
{1,3,4} 7 超重,不可行
{2,3,4} 6 超重,不可行
{1,2,3,4} 8 超重,不可行

       通过价值列判定,发现背包中放入1,2,4三个物品时,价值最高。当物品数量为4的时候,通过穷举法,我们需要2^4次列举,无疑使用穷举查找型算法对于任何输入都是低效率的。

二、贪婪技术

       使用蛮力法自我感觉其实就是按规矩办事,我把所有的情况都列出来,你想要哪种选哪种;而贪婪技术,针对“贪婪”一词可想而知。
何为贪婪技术?贪婪技术所走的每一步必须满足如下的三个条件:

  • 可行的:即它必须满足问题的约束。
  • 局部最优:它是当前步骤中最优的选择。
  • 不可取消:即一旦做出选择,则后续步骤中无法改变。
           所以对于贪婪算法而言,我首先上来肯定选最有价值的,物品3价值最大,所以先选择物品3,价值为20,重量为3;这时候背包还剩余W-3=2,然后从剩余的里边再选择一个价值最大的,即物品4(该物品重量<=2,此实例中,物品4重量为2,价值最大,所以选择了物品4;假若有一个重量为1的物品,价值比物品4还大,那肯定就选择价值最大的了).所以此实例中,若采用贪婪算法,我们则选择3,4两个物品时,背包的价值最高。

三、动态规划

       对于动态规划这种设计技术而言,其实它是存在一段很有趣的历史的,这里不便多言。那么何为动态规划呢?
       wiki中说Dynamic programming是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。常常适用于有重叠子问题[1]和最优子结构性质的问题。
       动态规划背后的基本思想非常简单。大致上,若要解一个给定问题,我们需要解其不同部分(即子问题),再根据子问题的解以得出原问题的解。
       所以对于背包问题也是如此,我们首先得考虑如何将其分配成一个一个的小问题。
设F(i,j)为该实例的最优解的物品总价值,换句话理解,就是能够放入称重量为j(0<j<=W)的背包中的前i个物品中最有价值子集的总价值。
所以有两种情况:

  • 不包括第i个物品的子集中,最优子集的价值为F(i-1,j)
  • 在包括第i个物品的子集中(j-Wi>=0),那么最优子集则是该物品包括或者包括两者中选择最优子集。

F(i,j)={maxF(i1,j),vi+F(i1,jWi)jWi&gt;=0F(i1,j),jWi&lt;0 F(i,j) = \begin{cases} max{F(i-1,j),vi+F(i-1,j-Wi)} &amp; j-Wi&gt;=0 \\ F(i-1,j), &amp; j-Wi&lt;0 \end{cases}
注:横坐标代表承重梁j,0<=j<=5

i 0 1 2 3 4 5
0 0 0 0 0 0 0
1 0
2 0
3 0
4 0
  • 当背包中不放任何物品时,价值为0。
  • 【 j=1,i=1】j-W1=1-2<0;F(i,j)=F(0,1)=0
  • 【j=2,i=1】j-W1=2-2=0;F(i,j)= max{F(i-1,j),vi+F(i-1,j-Wi)}=max{F(0,2),F(0,0)+v1}=12
  • 【j=3,i=1】j-W1=3-2=1;F(i,j)= max{F(i-1,j),vi+F(i-1,j-Wi)}=max{F(0,3),F(0,1)+v1}=12
  • 【j=4,i=1】j-W1=4-2=2;F(i,j)=max{F(i-1,j),vi+F(i-1,j-Wi)}=max{F(0,4),F(0,2)+v1}=12
  • 【j=5,i=1】j-W1=5-2=3;F(i,j)=max{F(i-1,j),vi+F(i-1,j-Wi)}=max{F(0,5),F(0,3)+v1}=12
i 0 1 2 3 4 5
0 0 0 0 0 0 0
1 0 0 12 12 12 12
2 0
3 0
4 0
  • 【j=1,i=2】j-W2=1-1=0;F(i,j)=max{F(i-1,j),vi+F(i-1,j-Wi)}=max{F(1,1),F(1,0)+v2}=10
  • 【j=2;i=2】j-W2=2-1=1;F(i,j)=max{F(i-1,j),vi+F(i-1,j-Wi)}=max{F(1,2),F(1,1)+v2}=12
  • 【j=3,i=2】j-W2=3-1=2;F(i,j)=max{F(i-1,j),vi+F(i-1,j-Wi)}=max{F(1,3),F(1,2)+v2}=22
  • 【j=4,i=2】j-W2=4-1=3;F(i,j)=max{F(i-1,j),vi+F(i-1,j-Wi)}=max{F(1,4),F(1,3)+v2}=22
  • 【j=5,i=2】j-W2=5-1=4;F(i,j)=max{F(i-1,j),vi+F(i-1,j-Wi)}=max{F(1,5),F(1,4}+v2}=22
i 0 1 2 3 4 5
0 0 0 0 0 0 0
1 0 0 12 12 12 12
2 0 10 12 22 22 22
3 0
4 0
  • 【j=1,i=3】j-W3=1-3<0;F(i,j)=F(2,1)=10
  • 【j=2,i=3】j-W3=2-3<0;F(i,j)=F(2,2)=12
  • 【j=3,i=3】j-W3=3-3=0;F(i,j)=max{F(i-1,j),vi+F(i-1,j-Wi)}=max{F(2,3),F(2,0)+v3}=22
  • 【j=4,i=3】j-W3=4-3=1;F(i,j)=max{F(i-1,j),vi+F(i-1,j-Wi)}=max{F(2,4),F(2,1)+v3}=30
  • 【j=5,i=3】j-W4=5-3=2;F(i,j)=max{F(i-1,j),vi+F(i-1,j-Wi)}=max{F(2,5},F(2,2)+v3}=32
i 0 1 2 3 4 5
0 0 0 0 0 0 0
1 0 0 12 12 12 12
2 0 10 12 22 22 22
3 0 10 12 22 30 32
4 0
  • 【j=1,i=4】j-W3=1-2<0;F(i,j)=F(3,1)=10
  • 【j=2,i=4】j-W3=2-2=0;F(i,j)=max{F(i-1,j),vi+F(i-1,j-Wi)}=max{F(3,2},F(3,0)+v4}=15
  • 【j=3,i=4】j-W3=3-2=1;F(i,j)=max{F(i-1,j),vi+F(i-1,j-Wi)}=max{F(3,3),F(3,1)+v4}=25
  • 【j=4,i=4】j-W3=4-2=2;F(i,j)=max{F(i-1,j),vi+F(i-1,j-Wi)}=max{F(3,4),F(3,2)+v4}=30
  • 【j=5,i=4】j-W4=5-2=3;F(i,j)=max{F(i-1,j),vi+F(i-1,j-Wi)}=max{F(3,5},F(3,3)+v4}=37
i 0 1 2 3 4 5
0 0 0 0 0 0 0
1 0 0 12 12 12 12
2 0 10 12 22 22 22
3 0 10 12 22 30 32
4 0 10 15 25 30 37

       通过如上表格,我们可以很清晰的发现,最大价值为F(4,5)=37,采用回溯法的过程,我们可以自底向上判定其物品。因为F(4,5)>F(3,5),物品4为最优解物品,剩余重量为5-2=3;因为F(3,3)=F(2,2),所以物品3并非最优解;因为F(2,3)>F(1,3),所以物品2为最优解物品,剩余重量为3-1=2;因为F(1,2)>F(0,2),所以物品1为最优解物品。所以采用动态规划问题解决背包问题,物品1,2,4放入背包,价值最大。
       通过回溯法我们发现,因为F(3,3)和F(2,2)价值是一样的,所以在如上的计算过程中,有些子问题对于求解并非是必须的,所以我们完全可以利用动态规划的记忆法,再次优化背包问题。该方式则是通过自底向上的方式计算,这样可以保证需要哪个子问题求解哪个子问题,避免非必要的子问题出现。
采用的公式依旧是
F(i,j)={maxF(i1,j),vi+F(i1,jWi)jWi&gt;=0F(i1,j),jWi&lt;0 F(i,j) = \begin{cases} max{F(i-1,j),vi+F(i-1,j-Wi)} &amp; j-Wi&gt;=0 \\ F(i-1,j), &amp; j-Wi&lt;0 \end{cases}
初始状态:

i 0 1 2 3 4 5
0 0 0 0 0 0 0
1 0
2 0
3 0
4 0
求解 过程 需要的子集
F(4,5) 【j=5,i=4】j-W4=5-2=3;F(i,j)=max{F(i-1,j),vi+F(i-1,j-Wi)}=max{F(3,5},F(3,3)+v4} F(3,5) F(3,3)
F(3,5) 【j=5,i=3】j-W4=5-3=2;F(i,j)=max{F(i-1,j),vi+F(i-1,j-Wi)}=max{F(2,5},F(2,2)+v3} F(2,5) F(2,2)
F(3,3) 【j=3,i=3】j-W3=3-3=0;F(i,j)=max{F(i-1,j),vi+F(i-1,j-Wi)}=max{F(2,3),F(2,0)+v3} F(2,3) F(2,0)
F(2,5) 【j=5,i=2】j-W2=5-1=4;F(i,j)=max{F(i-1,j),vi+F(i-1,j-Wi)}=max{F(1,5),F(1,4}+v2} F(1,5) F(1,4)
F(2,3) 【j=3,i=2】j-W2=3-1=2;F(i,j)=max{F(i-1,j),vi+F(i-1,j-Wi)}=max{F(1,3),F(1,2)+v2} F(1,3) F(1,2)
F(2,2) 【j=2;i=2】j-W2=2-1=1;F(i,j)=max{F(i-1,j),vi+F(i-1,j-Wi)}=max{F(1,2),F(1,1)+v2} F(1,2) F(1,0)
F(1,5) 【j=5,i=1】j-W1=5-2=3;F(i,j)=max{F(i-1,j),vi+F(i-1,j-Wi)}=max{F(0,5),F(0,3)+v1} F(0,5) F(0,3)
F(1,4) 【j=4,i=1】j-W1=4-2=2;F(i,j)=max{F(i-1,j),vi+F(i-1,j-Wi)}=max{F(0,4),F(0,2)+v1} F(0,4) F(0,2)
F(1,3) 【j=3,i=1】j-W1=3-2=1;F(i,j)= max{F(i-1,j),vi+F(i-1,j-Wi)}=max{F(0,3),F(0,1)+v1} F(0,3) F(0,1)
F(1,2) 【j=2,i=1】j-W1=2-2=0;F(i,j)= max{F(i-1,j),vi+F(i-1,j-Wi)}=max{F(0,2),F(0,0)+v1} F(0,2) F(0,0)
F(1,1) 【 j=1,i=1】j-W1=1-2<0;F(i,j)=F(0,1)=0 F(0,1)

通过如上子集需要,所以我们最终只需要计算F(1,1),F(1,2),F(1,3),F(1,4),F(1,5),F(2,2),F(2,3),F(2,5),F(3,3),F(3,5),F(4,5)等子集即可。
最后的表格为:

i 0 1 2 3 4 5
0 0 0 0 0 0 0
1 0 0 12 12 12 12
2 0 - 12 22 - 22
3 0 - - 22 - 32
4 0 - - - - 37

       通过如上过程,我们可以很清晰的发现,其实虽然去除了无谓的子问题计算,但是这种记忆式的过程其实就是一种递归的思想。那谈到递归,效率肯定不会高到哪里去。但是相比其他思想而言,动态规划的记忆法还是效率很高的,只是提高效率的维度不大,其时间复杂度和上述讲述的自底向上算法的层级是一样的。
通过以上三个思想的背包问题总结,其实我们可以总结如下规律:

背包可扩展/非0-1问题 0-1问题 n很小
蛮力法
贪心
动态规划

       为何这么说呢,因为通过蛮力法我们发现,当n=4的时候,我们就得罗列2^4次,所以这种穷举法的方式根本不适用于背包问题;因为贪心问题是局部最优进而整体最优,所以若背包可扩展或者是非0-1问题(可切割),那么我肯定毫无疑问,选择贪心算法了,因为贪婪每次选择的都是当前里边最优的一个选择;而所以的0-1背包问题,使用动态规划的记忆方式,去除多余子问题的计算,无疑是这三个思想里边最好的一种方式。

posted on 2018-12-11 19:06  huohuoL  阅读(545)  评论(0编辑  收藏  举报

导航