退火算法基本思想在此不再赘述,可参考本文。模板如下:
T0 = 1000; % 初始温度
T = T0; % 迭代中温度会发生改变,第一次迭代时温度就是T0
maxgen = 500; % 最大迭代次数
Lk = 500; % 每个温度下的迭代次数
alpfa = 0.95; % 温度衰减系数
%% 随机生成一个初始解(初始解的形式因问题而异)
path0 = randperm(n);
result0 = cost(path0,d); % 计算这一组解对应的耗费
%% 定义一些保存中间过程的量,方便输出结果和画图
min_result = result0;
RESULT = zeros(maxgen,1); % 记录每一次外层循环结束后找到的
for iter = 1 : maxgen % 外循环, 我这里采用的是指定最大迭代次数
for i = 1 : Lk % 内循环,在每个温度下开始迭代
path1 = gen_new_path(path0);
result1 = cost(path1,d);
if result1 < result0
path0 = path1;
result0 = result1;
else
p = exp(-(result1 - result0)/T); % 根据Metropolis准则计算一个概率
if rand(1) < p % 生成一个随机数和这个概率比较,如果该随机数小于这个概率
path0 = path1;
result0 = result1;
end
end
% 判断是否要更新找到的最佳的解
if result0 < min_result % 如果当前解更好,则对其进行更新
min_result = result0;
best_path = path0;
end
end
RESULT(iter) = min_result; % 保存本轮外循环结束后找到的最小的结果
T = alpfa*T; % 温度下降
end
PSO(粒子群算法),GA(遗传算法),SA(退火算法)等优化算法,个人认为虽然优化思路不同,但在某些地方是相通的。
而这几个地方则是将优化算法应用到实际问题的关键所在(问题转化能力,编程能力)。
模板中的randperm函数生成的是一个1*n的排列,可用于tsp这种路线安排(个人理解为离散?)。
再如y = 2a+3b 自变量a范围为(-1,1),自变量6范围为(0,1)初始解得构造形式需改变,可使用rand函数来构造。
当然对其他问题还会有其他的初始解形式。
模板中对应的cost函数;
例如 max = 2a+3b,2a+3b可作为cost函数
但实际应用场景中要求并非如此单调
如tsp中要求路线最短。就需要构造cost函数来计算每次路线的耗费。
再比如寝室分配问题,点击看视频要求不同寝室间同寝室同学的共同爱好数量分布的尽可能均匀,就需要我们将不同寝室间共同爱好数量的标准差降到最小(即目标函数为标准差)
产生新解过程差异也是各种优化算法差异的一个重要表现。
根据不同优化算法的特性以及解形式能正确编程得到新解是很重要的,以tsp使用退火算法求解为例,randperm函数就很好适配了这个解得形式并能很容易的产生随机新解。
但以遗传算法为例,种群中染色体会经历交叉变异等操作会导致产生的新解会不符合要求;
1 2 3 4 5 6 7
4 6 5 3 1 2 7
以第四个位置进行交叉结果会变为
1 2 3 3 1 2 7
4 6 5 4 5 6 7
因此需要进行处理,在此不进行赘述,此文提供了一种思路,因此根据不同的问题选择不同的算法来解决也很重要。
例如pso中引入罚函数的概念来对不满足规范的粒子进行惩罚,在此不多赘述,本文稍有提及。
main.m
n = 50;%城市的数量
city = 10*rand(n,2);%城市的位置坐标
d = zeros(n); % 初始化两个城市的距离矩阵全为0
for i = 2:n
for j = 1:i
city_i = city(i,:); x_i = city_i(1); y_i = city_i(2); % 城市i的横坐标为x_i,纵坐标为y_i
city_j = city(j,:); x_j = city_j(1); y_j = city_j(2); % 城市j的横坐标为x_j,纵坐标为y_j
d(i,j) = sqrt((x_i-x_j)^2 + (y_i-y_j)^2); % 计算城市i和j的距离
end
end
d = d+d';
%% 参数初始化
T0 = 1000; % 初始温度
T = T0; % 迭代中温度会发生改变,第一次迭代时温度就是T0
maxgen = 500; % 最大迭代次数
Lk = 500; % 每个温度下的迭代次数
alpfa = 0.95; % 温度衰减系数
%% 随机生成一个初始解
path0 = randperm(n);
result0 = calculate_ans(path0,d); % 计算这一组解对应的耗费
min_result = result0;
%% 模拟退火过程
for iter = 1 : maxgen % 外循环, 我这里采用的是指定最大迭代次数
for i = 1 : Lk % 内循环,在每个温度下开始迭代
path1 = gen_new_path(path0);
result1 = calculate_ans(path1,d);
% disp('result1:');
% disp(result1);
if result1 < result0
path0 = path1;
result0 = result1;
else
p = exp(-(result1 - result0)/T); % 根据Metropolis准则计算一个概率
if rand(1) < p % 生成一个随机数和这个概率比较,如果该随机数小于这个概率
path0 = path1;
result0 = result1;
end
end
if result0 < min_result % 如果当前解更好,则对其进行更新
min_result = result0;
best_path = path0;
end
end
T = alpfa*T; % 温度下降
end
disp('最佳的方案是:'); disp(mat2str(best_path))
disp('此时最优值是:'); disp(min_result)
gen_new_path.m
function path1 = gen_new_path(path0)
% path0: 原来的路径
n = length(path0);
% 随机选择两种产生新路径的方法
p1 = 0.33; % 使用交换法产生新路径的概率
p2 = 0.33; % 使用移位法产生新路径的概率
r = rand(1); % 随机生成一个[0 1]间均匀分布的随机数
if r< p1 % 使用交换法产生新路径
c1 = randi(n); % 生成1-n中的一个随机整数
c2 = randi(n); % 生成1-n中的一个随机整数
path1 = path0; % 将path0的值赋给path1
path1(c1) = path0(c2); %改变path1第c1个位置的元素为path0第c2个位置的元素
path1(c2) = path0(c1); %改变path1第c2个位置的元素为path0第c1个位置的元素
elseif r < p1+p2 % 使用移位法产生新路径
c1 = randi(n); % 生成1-n中的一个随机整数
c2 = randi(n); % 生成1-n中的一个随机整数
c3 = randi(n); % 生成1-n中的一个随机整数
sort_c = sort([c1 c2 c3]); % 对c1 c2 c3从小到大排序
c1 = sort_c(1); c2 = sort_c(2); c3 = sort_c(3); % c1 < = c2 <= c3
tem1 = path0(1:c1-1);
tem2 = path0(c1:c2);
tem3 = path0(c2+1:c3);
tem4 = path0(c3+1:end);
path1 = [tem1 tem3 tem2 tem4];
else % 使用倒置法产生新路径
c1 = randi(n); % 生成1-n中的一个随机整数
c2 = randi(n); % 生成1-n中的一个随机整数
if c1>c2 % 如果c1比c2大,就交换c1和c2的值
tem = c2;
c2 = c1;
c1 = tem;
end
tem1 = path0(1:c1-1);
tem2 = path0(c1:c2);
tem3 = path0(c2+1:end);
path1 = [tem1 fliplr(tem2) tem3]; %矩阵的左右对称翻转 fliplr,上下对称翻转 flipud
end
end
calculate_ans.m
function [tsp_ans] = calculate_ans(path,d)
tsp_ans = 0;
n = size(path,2);
for i = 2:n
tsp_ans = tsp_ans+ d(path(i-1),path(i));
end
end
饿了,先吃点东西