01背包问题概述
问题:有一个箱子的最大载重量为cap(正整数),同时有n个物品(正整数),每个物品有一个重量(正整数)以及对应的价值。要求n个物品中,任取若干个装入箱内,要求:在保证箱子最大载重量不被超过的情况下,箱子内的物品价值最高。物品i的重量为weighti(i=1,2,...,N),物品i的价值为valuei(i=1,2,...,N),物品i被选择时xi=1,否则xi=0。背包内物品的总重量为
,物品的价值总量为
,如何决定变量的值使背包内物品价值总量为最大。这个问题的数学模型表示如下:
遗传算法解法
遗传算法是一种常见的解决方案。
基本原理(这里借鉴了其他博主的博客版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。原文链接:https://blog.csdn.net/m0_70379869/article/details/126455436)是:
模拟生物基因遗传的做法,通过编码组成初始群体后,遗传操作的任务就是对群体的个体按照它们对环境适应度(适应度评估)施加一定的操作,从而实现优胜劣汰的过程。通过一代一代的优化,逼近最优化的解(局部最优化解)。
具体算法若干概念如下:
遗传操作包含四个算子(基本的四个):种群初始化(Population initialization)、选择(select)、交叉(crossover)、变异(mutation)
其他概念包含:编码(Coding)、解码(Decoding)、个体(Individual)等。
基本设置与种群初始化
根据书本:Matlab 智能优化算法:从写代码到算法思想,中给了以下算例,假设箱子最大载重量为1000
首先进行基本设置
close;% 关闭绘图窗口
clear all;% 清除工作区所有变量
clc;%清除command line window 命令行窗口
背包问题初始设置
weight = [80,82,85,70,72,70,82,75,78,45,49,76,45,35,94,49,76,79,84,74,76,63,...
35,26,52,12,56,78,16,52, 16,42,18,46,39,80,41,41,16,35,70,72,70,66,50,55,25, 50,55,40];%5个物品各自的重量
value = [200,208,198,192,180,180,168,176,182,168,187,138,184,154,168,175,198,...
184,158,148,174,135, 126,156,123,145,164,145,134,164,134,174,102,149,134,...
156,172,164,101,154,192,180,180,165,162,160,158,155, 130,125]; %50个物品对应的价值
max_bagweight = 1000;%背包最大承受重量
决策变量设置
具体理论可以看这一篇博客,以免大家对以下代码中特定名词如染色体,基因看不懂的可以看上文链接
chromlength = length(weight);%使用length长度获取物品的数量,即个体基因个数(染色体长度)
p_crossover = .95; % probability of crossover 交叉概率
p_mutation = .15; %probability of crossover 变异概率
max_iterations = 500; %最大迭代次数,单词iteration意思为迭代
population_sizes = 200;%初始种群规模
构建初始种群
%load population.mat
pop =[];%创建初始种群的空矩阵
for i = 1:population_sizes;
%pop(i,:) = truerand(1,chromlength,0,1);%随机获得初始种群的个体,即染色体
pop=round(rand(population_sizes,chromlength));%破解版matlab需要自行安装truerand组件,不行可以使用这行代码进行种群初始化
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%前面的基本参数已经输入完毕,接下来就是遗传代码的循环迭代来产生新的种群
迭代开始
%遗传算法循环
for k = 1:max_iterations;
个体的淘汰以及适应度计算
在这里我使用精英保留策略,因为我们在种群初始化时会遇到不符合约束的染色体(解),这里我采用了精英保留策略,即不符合约束的解换成当下适应度(目标函数)最高的值,还有另外一种解决不符合约束解的方法:遗传算法求解0-1背包问题(附matlab源代码) (qq.com),微信gzh: 优化算法交流地,文章:遗传算法求解0-1背包问题(附matlab源代码)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%第一步,选择符合约束的个体(染色体)
for i = 1:population_sizes;
while pop(i,:) * weight' > max_bagweight;%当初始解不符合约束时,染色体上的基因为0
pop(i,:)=0;
end
end
%适应度计算
for i = 1:population_sizes; %对14个种群计算适应度
Fit(i) = pop(i,:) * value';
end
Fit=Fit';
maxFit = max(Fit); %寻找适应度最大值
minFit = min(Fit); %寻找适应度最小值,即不符合约束的染色体
Index_of_chromBest = find(Fit==maxFit);%%找路境中与maxFit相等元素的位置
Index_of_chromworse = find(Fit==minFit);%%找路境中与minFit相等元素的位置
chromBest = pop(find(Fit==maxFit),:); %find(Fit==maxFit),寻找fit中最大值索引,对应pop中的行数,然后找到历代最优个体
%用最优个体替代不符合约束条件的个体
for i=1:population_sizes;
if sum(abs(pop(i,:)))==0; %重量超过约束
pop(i,:)=chromBest(1,:);
Fit(i)=maxFit;
end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
选择:基于轮盘赌的操作
这部分理论知识可以参考这篇文章:轮盘赌选择解析 (附MATLAB代码) (qq.com),微信gzh: MATLAB智能优化算法,文章:轮盘赌选择解析 (附MATLAB代码) 之前我没搞懂为啥要使用累加cumsum函数,看了这部分就了解了什么是轮盘赌以及实现方法。
fitvalue = Fit/sum(Fit); %个体/染色体被选中的概率为书中的Pi
Pcum = cumsum(fitvalue); %个体适应度的累加
roulette_hands = sort(rand(population_sizes,1));%rand随机产生0到1之间的数;sort对数组元素按升序排列
fiti = 1;
newi = 1;
while newi <= population_sizes;
if (roulette_hands(newi)) < Pcum(fiti);
new_population(newi,:) = pop(fiti,:);
newi = newi + 1;
else
fiti = fiti + 1;
end
end
交叉
newpop=ones(size(pop));%产生下一代的种群矩阵的初始规模
for i=1:2:(population_sizes-1);%步长为2,是将相邻的两个个体进行交叉
if(rand<p_crossover);
cpoint=round(rand*chromlength);%四舍五入到最接近的整数,确定染色体上哪个基因之后的染色体片段需要互换(一维向量的索引值)
newpop(i,:)=[pop(i,1:cpoint),pop(i+1,cpoint+1:chromlength)];%相邻两个个体的染色体片段开始交叉
newpop(i+1,:)=[pop(i+1,1:cpoint),pop(i,cpoint+1:chromlength)];
else
newpop(i,:)=pop(i,:);
newpop(i+1,:)=pop(i+1,:);
end
end
变异
for i=1:population_sizes;
if(rand<p_mutation);
mpoint=round(rand*chromlength); %确定变异点在染色体基因之间
if mpoint<=0;
mpoint=1;
%变异位置
end
newpop(i,:)=pop(i,:);
if any(newpop(i,mpoint))==0;
newpop(i,mpoint)=1;
else
newpop(i,mpoint)=0;
end
else
newpop(i,:)=pop(i,:);
end
end
将新的New population 赋值给initial population
pop=newpop;%新的New population 赋值给initial population
%pop(1,:)=chromBest(1,1);%保留上一代最优个体
trace(k) = maxFit;
end
画图
disp(["该装进背包的物品编号为",[find(chromBest(1,:)==1)]]); %最优个体
disp(["总价值为",num2str(maxFit)]); %最优目标值
figure
plot(trace)
xlabel('迭代次数')
ylabel('目标函数值')
title('适应度进化曲线')
由于遗传算法只能近似最优解,所以多运行几次会出现最优解:
最优目标值为:3966.0 最优分配为:[0 0 0 0 0 0 0 0 0 1 1 0 1 1 0 1 0 0 0 0 0 0 1 1 0 1 1 0 1 1 1 1 1 1 1 0 1 1 1 1 0 0 0 1 1 1 1 1 0 1] 背包标签为:[10 11 13 14 16 23 24 26 27 29 30 31 32 33 34 35 37 38 39 40 44 45 46 47 48 50]