如图为清风老师讲义中的背包问题 ,其给出的解法为整数规划,代码如下:
%% 背包问题(货车运送货物的问题)
c = -[540 200 180 350 60 150 280 450 320 120]; % 目标函数的系数矩阵(最大化问题记得加负号)
intcon=[1:10]; % 整数变量的位置(一共10个决策变量,均为0-1整数变量)
A = [6 3 4 5 1 2 3 5 4 2]; b = 30; % 线性不等式约束的系数矩阵和常数项向量(物品的重量不能超过30)
Aeq = []; beq =[]; % 不存在线性等式约束
lb = zeros(10,1); % 约束变量的范围下限
ub = ones(10,1); % 约束变量的范围上限
%最后调用intlinprog()函数
[x,fval]=intlinprog(c,intcon,A,b,Aeq,beq,lb,ub)
fval = -fval
我尝试了一下用模拟退火求解,也可得到相同的答案,下面为求解过程。
将各个物理参数和目标参数用类的形式整合在一起,一目了然。
%% 背包问题
clear;clc
%% 设置求解问题的参数
problem.numVar = 10; %变量个数
problem.fun = @(x)obj_fun(x); %优化目标函数名称
problem.fun_CV = @(x)obj_fun_CV(x); %约束条件
%% 模拟退火的参数
SAParameters.temperature = 100;% 初始温度 设置的足够大的话,可以在初始拥有更好的性能
SAParameters.kb = 0.3; % 温度系数
SAParameters.alpha = 0.9; % 降温系数
SAParameters.penalty = 1.5; % 惩罚系数
SAParameters.num = 100; % 马尔可夫链长度
SAParameters.Tmin = 1; % 结束温度
参考0-1规划模型。决策变量x是个长度为10的序列,只包含0或1。0代表不运送该货物;1代表运送该货物。货物价值写在矩阵c中,通过与0-1矩阵的点乘便可求出总价值。具体写法如下:
function f = obj_fun(x) % 目标函数
c = -[540 200 180 350 60 150 280 450 320 120];
f = c.*x;
f = sum(f);
end
约束条件为所装所有货物重量小于等于30。因此要对货物质量大于30的解进行惩罚。A为各货物的重量矩阵,通过与0-1矩阵的点乘便可求出货物总重量。具体写法如下:
function CV = obj_fun_CV(x) % 约束条件函数
A = [6 3 4 5 1 2 3 5 4 2];
g1 = sum(A.*x)-30;
G1 = (g1>0)*g1; % 大于30时候对其进行惩罚
CV = G1;
end
初始解必须是一个可行解,因此全部为1的序列肯定不行,需要对序列进行随机扰动,并且让该序列的解满足罚函数值为0(即满足约束条件)。
%% 解的初始化,产生一个可行解
variables = ones(1,10);
while 1
temp = ceil(rand.*problem.numVar);
variables(temp) = ~variables(temp);
CV = problem.fun_CV(variables);
if CV == 0
break
end
end
var_final = variables; % 初始化最终最优解
T = SAParameters.temperature; % 初始化温度
E0_OBJ = problem.fun(variables); % 初始化目标函数值
E0_CV = problem.fun_CV(variables); % 初始化CV值
E0 = E0_OBJ+SAParameters.penalty*E0_CV; % 最终目标值
E_OBJ_f = E0; % 初始化最佳温度
通过随机扰动,随机将序列中的1变成0或0变成1,作为新解。然后就是常规的退火筛选解的操作了。具体的筛选操作(Metropolis准则等)就不再赘述了。
%% 退火过程
while (T>=SAParameters.Tmin) % 开始降温
for i = 1:SAParameters.num % 马尔科夫链
variables_temp = variables; % 用于暂时存放原来的解
%% 新解的产生,随机扰动法
temp = ceil(rand.*problem.numVar);
variables(temp) = ~variables(temp);
%% 移动后的目标值计算
E_OBJ = problem.fun(variables); % 移动后的目标函数值
E_CV = problem.fun_CV(variables); % 移动后的CV值
E = E_OBJ+SAParameters.penalty*E_CV;
dE = E-E0;
if (E_OBJ<=E_OBJ_f && E_CV==0)
var_final = variables; % 适应度更小且满足约束条件,保留解
E_OBJ_f=E_OBJ;
end
prob=exp(-dE/SAParameters.kb/T);
if(dE>0 && rand()>prob)
variables = variables_temp; % 不满足Metropolis准则,还原解
end
E0_OBJ=problem.fun(variables); %初始目标函数值
E0_CV=problem.fun_CV(variables); %初始CV值
E0=E0_OBJ+SAParameters.penalty*E0_CV;
end
T = T*SAParameters.alpha; % 降温
end
E_OBJ_f = -E_OBJ_f;
%% 背包问题
clear;clc
%% 设置求解问题的参数
problem.numVar = 10; %变量个数
problem.fun = @(x)obj_fun(x); %优化目标函数名称
problem.fun_CV = @(x)obj_fun_CV(x); %约束条件
%% 模拟退火的参数
SAParameters.temperature = 100;% 初始温度 设置的足够大的话,可以在初始拥有更好的性能
SAParameters.kb = 0.3; % 温度系数
SAParameters.alpha = 0.9; % 降温系数
SAParameters.penalty = 1.5; % 惩罚系数
SAParameters.num = 100; % 马尔可夫链长度
SAParameters.Tmin = 1; % 结束温度
%% 解的初始化,产生一个可行解
variables = ones(1,10);
while 1
temp = ceil(rand.*problem.numVar);
variables(temp) = ~variables(temp);
CV = problem.fun_CV(variables);
if CV == 0
break
end
end
var_final = variables; % 初始化最终最优解
T = SAParameters.temperature; % 初始化温度
E0_OBJ = problem.fun(variables); % 初始化目标函数值
E0_CV = problem.fun_CV(variables); % 初始化CV值
E0 = E0_OBJ+SAParameters.penalty*E0_CV; % 最终目标值
E_OBJ_f = E0; % 初始化最佳温度
%% 退火过程
while (T>=SAParameters.Tmin) % 开始降温
for i = 1:SAParameters.num % 马尔科夫链
variables_temp = variables; % 用于暂时存放原来的解
%% 新解的产生,随机扰动法
temp = ceil(rand.*problem.numVar);
variables(temp) = ~variables(temp);
%% 移动后的目标值计算
E_OBJ = problem.fun(variables); % 移动后的目标函数值
E_CV = problem.fun_CV(variables); % 移动后的CV值
E = E_OBJ+SAParameters.penalty*E_CV;
dE = E-E0;
if (E_OBJ<=E_OBJ_f && E_CV==0)
var_final = variables; % 适应度更小且满足约束条件,保留解
E_OBJ_f=E_OBJ;
end
prob=exp(-dE/SAParameters.kb/T);
if(dE>0 && rand()>prob)
variables = variables_temp; % 不满足Metropolis准则,还原解
end
E0_OBJ=problem.fun(variables); %初始目标函数值
E0_CV=problem.fun_CV(variables); %初始CV值
E0=E0_OBJ+SAParameters.penalty*E0_CV;
end
T = T*SAParameters.alpha; % 降温
end
E_OBJ_f = -E_OBJ_f;
这是模拟退火过程求得的结果。
结果相同,以后该模拟退火法可用于同类背包问题的求解了 。