遗传算法求解0/1揹包问题

遗传算法(genetic algorithm,GA)是计算数学中用于解决最优化问题的搜索算法,是进化算法的一种。进化算法最初是借鉴了达尔文进化生物学中的一些现象而发展起来的,这些现象包括遗传、突变、自然选择以及杂交等。

遗传算法通常实现方式为一种计算机模拟。对于一个最优化问题,一定数量的候选解(称为个体)可抽象表示为染色体,使种羣向更好的解进化。传统上,解用二进制表示(即0和1的串),但也可以用其他表示方法。进化从完全随机个体的种羣开始,之后一代一代发生。在每一代中评价整个种羣的适应度,从当前种羣中随机地选择多个个体(基于它们的适应度),通过自然选择突变产生新的生命种羣,该种羣在算法的下一次迭代中成为当前种羣。

基本的遗传算法以初始种羣为起点,经过自然选择交叉突变操作生成新的种羣,经过反复更新种羣直到寻找到最优解。其计算步骤如下:

  1. 编码:将问题空间转换为遗传空间;
  2. 生成初始种羣:随机生成P个染色体;
  3. 种羣适应度计算:按照确定的适应度函数,计算各个染色体的适应度;
  4. 选择:根据染色体适应度,按照选择算子进行染色体的选择;
  5. 交叉:按照交叉概率对被选择的染色体进行交叉操作,形成下一代种羣;
  6. 突变:按照突变概率对下一代种羣中的个体进行突变操作;
  7. 返回第3步继续迭代,直到满足终止条件。

揹包问题(Knapsack Problem)是一个典型的组合优化问题,在计算理论中属于NP-完全问题, 其计算复杂度为 O(2n) ,传统上采用动态规划来求解。设 s[i] 是经营活动 i 所需要的资源消耗, C 是所能提供的资源总量, v[i] 是人们经营活动i得到的利润或收益,则揹包问题就是在资源有限的条件下, 追求总的最大收益的资源有效分配问题。

0/1揹包问题的一般提法是:已知n个物品的体积为

size={s1,s2,...,sn},si>0

其价值(或收益)是

value={v1,v2,...,vn},vi>0

揹包的容量(contain)假设设为

C ,如何选择哪些物品装入揹包可以使得在揹包的容量约束限制之内所装物品的价值最大?

更形式地,求解一个有

n 个元素的集合

{x1,x2,...,xn},xi0,1 ,使得目标函数

f(x1,x2,...,xn)=Σvixi 在条件

ΣsixiC 下最大。

例题1:揹包容量为9,物品体积 size={2,3,4,5} ,其价值是 value={3,4,5,7}

方法一:使用动态规划法求解0/1揹包问题:
V[i,j] 是前 i 项中取出的装入体积为 j 的揹包中物品的最大价值,这里 0in,0jC 。我们要求的值就是 V[n,C] 。其递推公式为:

V[i,j]=0V[i1,j]max{V[i1,j],V[i1,jsi]}i=0j=0j<sii>0jsi

求解过程:

i0123456789
00000000000
10033333333
20034477777
300344789912
40034578101112

时间复杂度: O(nC) ,空间复杂度 O(C) 。注意这里是伪多项式时间,算法对于输入是指数的。

方法二:使用贪心算法求解0/1揹包问题:计算的复杂度降低了很多,但是往往难以得到最优解,有时所得解与最优解相差甚远。
在本例中,物品性价比依次为 {1.5,1.33,1.25,1.4} ,若采用贪心算法,则依次选择物品1和4,总价值只能达到10。
时间复杂度为 O(nlogn)

方法三:使用遗传算法求解0/1揹包问题:

  1. 编码:基于揹包问题的模型,我们设计了针对于揹包问题的染色体编码方法:将待求解的各量 {x1,x2,...,xn} 表示成长为 n 的二进制字符串x[j],j=1,2, …,n。x[j]=0表示物体j不放入揹包内,x[j]=1表示物体j放入揹包内。例如:1010代表一个解,它表示将第1、3号物体放入揹包中,其它的物体则不放入。

  2. 生成初始种羣:本例中,羣体规模的大小取为4,即羣体由4个个体组成,每个个体可通过随机的方法产生。例如:
    a1 = [1, 1, 1, 1]
    a2 = [1, 0, 1, 0]
    a3 = [1, 0, 0, 1]
    a4 = [1, 0, 0, 0]

  3. 种羣适应度计算:按照下列公式计算种羣中个体适应度:

    TotalSize=Σsixi
    Fitness={ΣvixiΣvixialpha(TotalSizeC)TotalSizeCTotalSize>C
    公式的下半部分即为适应度的惩罚函数,其中参数 alpha>1.0 。在本例中我们取 alpha=2
    a1 = [1, 1, 1, 1], totalSize = 14, totalValue = 19, fit = 9
    a2 = [1, 0, 1, 0], totalSize = 6, totalValue = 8, fit = 8
    a3 = [1, 0, 0, 1], totalSize = 7, totalValue = 10, fit = 10
    a4 = [1, 0, 0, 0], totalSize = 2, totalValue = 3, fit = 3

  4. 选择:我们采用与适应度成正比的概率来确定各个个体复制到下一代羣体中的数量。其具体操作过程如下:

    • 先计算出羣体中所有个体的适应度的总和 Σf(ai) i=1,2,3,4 ;
    • 其次计算出每个个体的相对适应度的大小 p(ai)=f(ai)/Σf(ai) i=1,2,3,4 。它即为每个个体被遗传到下一代羣体中的概率;
    • 每个概率值组成一个区间,全部概率值之和为1;
    • 最后再产生一个0到1之间的随机数,依据该随机数出现在上述哪一个概率区域内来确定各个个体被选中的次数。
      在本例中,
      Σf(ai)=30
      p(a1)=30.00%p(a2)=26.66%p(a3)=33.33%p(a4)=10.00%
      因此 a1=[0,0.3] a2=[0.3,0.5666] a3=[0.5666,0.9] a4=[0.9,1]
      经过4次选择, a3 被选1次, a2 被选2次, a4 落选:
      b1 = [1, 0, 1, 0], totalSize = 6, totalValue = 8, fitValue = 8
      b2 = [1, 0, 0, 1], totalSize = 7, totalValue = 10, fitValue = 10
      b3 = [1, 0, 1, 0], totalSize = 6, totalValue = 8, fitValue = 8
      b4 = [1, 1, 1, 1], totalSize = 14, totalValue = 19, fitValue = 9
  5. 交叉:本例采用单点交叉的方法,其具体操作过程是:

    • 先对羣体进行随机配对;
    • 其次随机设置交叉点位置;
    • 最后再相互交换配对染色体之间的部分基因。
      在本例中,
      b1 b4 在第3位后交叉,生成:
      c1 = [1, 0, 1, 1], totalSize = 11, totalValue = 15, fitValue = 11
      c2 = [1, 1, 1, 0], totalSize = 9, totalValue = 12, fitValue = 12
      b2 b3 在第2位后交叉,生成:
      c3 = [1, 0, 1, 0], totalSize = 6, totalValue = 8, fitValue = 8
      c4 = [1, 0, 0, 1], totalSize = 7, totalValue = 10, fitValue = 10
  6. 突变:我们采用基本位变异的方法来进行变异运算,其具体操作过程是:
    • 首先确定出各个个体的基因变异位置;
    • 然后依照某一概率将变异点的原有基因值取反。
      本例中, c1 的第1位发生突变:
      c1 = [0, 0, 1, 1], totalSize = 9, totalValue = 12, fitValue = 12
      c2 = [1, 1, 1, 0], totalSize = 9, totalValue = 12, fitValue = 12
      c3 = [1, 0, 1, 0], totalSize = 6, totalValue = 8, fitValue = 8
      c4 = [1, 0, 0, 1], totalSize = 7, totalValue = 10, fitValue = 10
      至此,我们已经找到了2个最优解 c1 c2

例题2:假设50个物品的体积Size、物体的价值Value和揹包的容量C分别为:
Size={ 80,82,85,70,72, 70,66,50,55,25 ,
50,55,40,48,50, 32,22,60,30,32 ,
40,38,35,32,25, 28,30,22,50,30 ,
45,30,60,50,20 , 65,20,25,30,10 ,
20,25,15,10,10 , 10,4, 4, 2, 1 }

Value={ 220,208,198,192,180, 180,165,162,160,158,
155,130,125,122,120 , 118,115,110,105,101,
100,100,98, 96, 95, 90, 88, 82, 80, 77 ,
75, 73, 72, 70, 69, 66, 65, 63, 60, 58,
56, 50, 30, 20, 15, 10, 8, 5, 3, 1}

C=1000,
如何选择哪些物品装入该揹包可使得在揹包的容量约束限制之内所装物品的总价值最大?
传统算法(回溯法或者动态规划法)所得结果:总价值为3103, 总重量为1000。最大迭代次数为400次。
贪心算法所得结果:总价值为3077 , 总重量为999。
遗传算法所得结果:总价值为3103, 总重量为1000。最大迭代次数为75次。
我们所求得最优解的个体分配情况为:
11010 10111 10110 11011 01111 11101 00001 01001 10000 01000

例题3:用遗传算法求解TSP问题。
旅行商问题(Travelling Salesman Problem,又称为货郎担问题、TSP问题)是一个多局部最优的最优化问题:有n个城市,一个推销员要从其中某一个城市出发,唯一走遍所有的城市,再回到他出发的城市,求最短的路线。也即求一个最短的哈密顿回路。

问题回顾:用遗传算法求解八皇后问题的编码和交叉方式:
编码:染色体的长度取决于皇后的个数,本例中染色体长度即为8。染色体中每个基因所处的位置表示其在棋盘上的行号,基因值表示其所在的列号。例如,染色体4075623,表示第0行皇后放在第4列,第1行皇后放在第0列,第2行皇后放在第7列,等等。八皇后问题中的任意两个皇后不能出现在同列,因此染色体中的基因取值(0~7)不可重复。
交叉:采用单点交叉的方法,先对羣体进行随机配对,对于每一对染色体,首先随机选取两点截取一段基因,例如:
01∥24∥3675
12∥30∥4576
然后将这段基因进行交换:
01∥30∥3675
12∥24∥4576
这样生成的两条染色体并不合法,因此要对中间段外的基因进行交换,从而生成合法的染色体:
41∥30∥2675
13∥24∥0576
交叉完成。

在TSP问题中,因为每个城市只能经过一次,所以可采用类似的方式求解。
具体求解方法请参考:
http://blog.5long.me/2015/genetic-algorithm-on-tsp/

点赞