模拟退火(Simulated Annealing, SA)算法简介与MATLAB实现

 

目录

模拟退火算法概述

算法步骤

算法特点

模拟退火算法MATLAB实现

【例1】一元/多元函数优化

【例2】TSP问题

模拟退火算法概述

  • 模拟退火算法(Simulated Annealing,简称SA)的思想最早是由Metropolis等提出的。其出发点是基于物理中固体物质的退火过程与一般的组合优化问题之间的相似性。模拟退火法是一种通用的优化算法,其物理退火过程由以下三部分组成:

    • 加温过程。其目的是增强粒子的热运动,使其偏离平衡位置。当温度足够高时,固体将熔为液体,从而消除系统原先存在的非均匀状态。

    • 等温过程。对于与周围环境交换热量而温度不变的封闭系统,系统状态的自发变化总是朝自由能减少的方向进行的,当自由能达到最小时,系统达到平衡状态

    • 冷却过程。使粒子热运动减弱,系统能量下降,得到晶体结构。

  • 加温过程相当于对算法设定初值,等温过程对应算法的Metropolis抽样过程,冷却过程对应控制参数的下降。这里能量的变化就是目标函数,我们要得到的最优解就是能量最低态。其中Metropolis准则是SA算法收敛于全局最优解的关键所在,Metropolis准则以一定的概率接受恶化解,这样就使算法跳离局部最优的陷阱。

  • SA算法的Metropolis准则允许接受一定的恶化解,具体来讲,是以一定概率来接受非最优解。举个例子,相当于保留一些“潜力股”,使解空间里有更多的可能性。对比轮盘赌法,从概率论来讲,它是对非最优解给予概率0,即全部抛弃。

  • 模拟退火本身是求一个最小值问题,但可以转化为求最大值问题,只需要对目标函数加个负号或者取倒数。

算法步骤

  1. 初始化:取初始温度T0足够大,令T = T0,任取初始解S1。

    对比GA、ACA、PSO之类的群优化算法,需要在解空间中产生多个个体,再继续寻优。而SA算法只需要一个点即可。

  2. 对当前温度T,重复第(3)~(6)步。

  3. 对当前解S1随机扰动产生一个新解S2。

    此处随机的扰动没有定义。结合实际例子做选择。

  4. 计算S2的增量df = f(S2) – f(S1),其中f(S1)为S1的代价函数

    代价函数相当于之前群优化算法中讲的适应度函数。

  5. 若df < 0,则接受S2作为新的当前解,即S1 = S2;否则,计算S2的接受概率exp(-df/T), T是温度。随机产生(0,1)区间上均匀分布的随机数rand,若exp(-df/T) > rand,也接受S2作为新的当前解S1 = S2,否则保留当前解S1。

    这是SA算法的核心部分,即有一定概率接受非最优解。

  6. 如果满足终止条件Stop,则输出当前解S1为最优解,结束程序,终止条件Stop通常取为在连续若干个Metropolis链中新解S2都没有被接受时终止算法或者是设定结束温度。否则按衰减函数衰减T后返回第(2)步,即被接受的新的解一直在产生,则我们要对问题进行降温,使得非最优解被接受的可能不断降低,结果愈发收敛于最优解。

算法特点

• 与遗传算法、粒子群优化算法和蚁群算法等不同,模拟退火算法不属于群优化算法,不需要初始化种群操作。

• 收敛速度较慢。因为1)它初始温度一般设定得很高,而终止温度设定得低,这样才符合物体规律,认为物质处于最低能量平衡点;2)它接受恶化解,并不是全程都在收敛的过程中。这一点可以类比GA中的变异,使得它不是持续在收敛的,所以耗时更多一些。

• 温度管理(起始、终止温度)、退火速度(衰减函数)等对寻优结果均有影响。比如T的衰减速度如果太快,就会导致可能寻找不到全局最优解。

模拟退火算法MATLAB实现

MATLAB自带模拟退火算法工具箱。在本文中,我们采用自带工具箱来解决函数优化的问题,再使用自己编写的程序来解决一个TSP问题。

要使用自带的工具箱,我们先安装:

《模拟退火(Simulated Annealing, SA)算法简介与MATLAB实现》

【例1】一元/多元函数优化

我们要找:

一元函数:x = [1,2]范围内 y = sin(10*pi*x) / x 的极值

《模拟退火(Simulated Annealing, SA)算法简介与MATLAB实现》

二元函数:在x,y都是[-5,5]范围内找z = x.^2 + y.^2 – 10*cos(2*pi*x) – 10*cos(2*pi*y) + 20 的极值

《模拟退火(Simulated Annealing, SA)算法简介与MATLAB实现》

上面是我们提前获得的ground-truth(用main.m,代码在下文)。下面我们假装不知道这个结果的,用模拟退火方法来搜索:

首先定义我们在SA算法中需要用到的代价函数fitness.m:

function fitnessVal = fitness( x )

%一元函数优化:

fitnessVal = sin(10*pi*x) / x;  %求最小值

% fitnessVal = -1 * sin(10*pi*x) / x; 用模拟退火求最大值,可以加个负号或者弄个倒数!

%二元函数优化:

% fitnessVal = -1 * (x(1)^2 + x(2).^2 - 10*cos(2*pi*x(1)) - 10*cos(2*pi*x(2)) + 20);

end

主程序main.m,用于我们直观看图先获得ground-truth:

%% I. 清空环境变量
clear all
clc

%% II. 一元函数优化
x = 1:0.01:2;
y = sin(10*pi*x) ./ x;
figure
plot(x,y,'linewidth',1.5)
ylim([-1.5, 1.5])
xlabel('x')
ylabel('y')
title('y = sin(10*pi*x) / x')
hold on

%%
% 1. 标记出最大值点
[maxVal,maxIndex] = max(y);
plot(x(maxIndex), maxVal, 'r*','linewidth',2)
text(x(maxIndex), maxVal, {['    X: ' num2str(x(maxIndex))];['    Y: ' num2str(maxVal)]})
hold on

%%
% 2. 标记出最小值点
[minVal,minIndex] = min(y);
plot(x(minIndex), minVal, 'ks','linewidth',2)
text(x(minIndex), minVal, {['    X: ' num2str(x(minIndex))];['    Y: ' num2str(minVal)]})

%% III. 二元函数优化
[x,y] = meshgrid(-5:0.1:5,-5:0.1:5);
z = x.^2 + y.^2 - 10*cos(2*pi*x) - 10*cos(2*pi*y) + 20;
figure
mesh(x,y,z)
hold on
xlabel('x')
ylabel('y')
zlabel('z')
title('z =  x^2 + y^2 - 10*cos(2*pi*x) - 10*cos(2*pi*y) + 20')

%%
% 1. 标记出最大值点
maxVal = max(z(:));
[maxIndexX,maxIndexY] = find(z == maxVal);
for i = 1:length(maxIndexX)
    plot3(x(maxIndexX(i),maxIndexY(i)),y(maxIndexX(i),maxIndexY(i)), maxVal, 'r*','linewidth',2)
     text(x(maxIndexX(i),maxIndexY(i)),y(maxIndexX(i),maxIndexY(i)), maxVal, {['    X: ' num2str(x(maxIndexX(i),maxIndexY(i)))];['    Y: ' num2str(y(maxIndexX(i),maxIndexY(i)))];['    Z: ' num2str(maxVal)]})
    hold on
end

现在正式开始模拟退火算法环节。在MATLAB命令中输入:optimtool,打开工具箱。

《模拟退火(Simulated Annealing, SA)算法简介与MATLAB实现》

《模拟退火(Simulated Annealing, SA)算法简介与MATLAB实现》

boltzmann就是对应Metropolis准则的退火方式。

选择可视化的输出的项目:

《模拟退火(Simulated Annealing, SA)算法简介与MATLAB实现》

《模拟退火(Simulated Annealing, SA)算法简介与MATLAB实现》

对于二元函数,在工具箱中的设置方法大同小异:记得先在fitness.m中修改我们的目标函数!

《模拟退火(Simulated Annealing, SA)算法简介与MATLAB实现》

运行后即可得到结果。

【例2】TSP问题

这里和上一篇讲蚁群算法的博文不同,我们的TSP中城市是自己虚拟的14座城市。

main.m:

 %% I. 清空环境变量
clear all
clc

%% II. 导入城市位置数据
X = [16.4700   96.1000
     16.4700   94.4400
     20.0900   92.5400
     22.3900   93.3700
     25.2300   97.2400
     22.0000   96.0500
     20.4700   97.0200
     17.2000   96.2900
     16.3000   97.3800
     14.0500   98.1200
     16.5300   97.3800
     21.5200   95.5900
     19.4100   97.1300
     20.0900   92.5500];

%% III. 计算距离矩阵
D = Distance(X);  %计算距离矩阵
N = size(D,1);    %城市的个数

%% IV. 初始化参数
T0 = 1e10;   % 初始温度,10的10次方!需要设定一个很大的温度。
Tend = 1e-30;  % 终止温度
L = 2;    % 各温度下的迭代次数
q = 0.9;    %降温速率
Time = ceil(double(solve([num2str(T0) '*(0.9)^x = ',num2str(Tend)])));  % 计算迭代的次数
% Time = 132;
count = 0;        %迭代计数
Obj = zeros(Time,1);         %目标值矩阵初始化
track = zeros(Time,N);       %每代的最优路线矩阵初始化

%% V. 随机产生一个初始路线
S1 = randperm(N);
DrawPath(S1,X)
disp('初始种群中的一个随机值:')
OutputPath(S1);
Rlength = PathLength(D,S1);
disp(['总距离:',num2str(Rlength)]);

%% VI. 迭代优化
while T0 > Tend
    count = count + 1;     %更新迭代次数
    temp = zeros(L,N+1);
    %%
    % 1. 产生新解
    S2 = NewAnswer(S1);
    %%
    % 2. Metropolis法则判断是否接受新解
    [S1,R] = Metropolis(S1,S2,D,T0);  %Metropolis 抽样算法
    %%
    % 3. 记录每次迭代过程的最优路线
    if count == 1 || R < Obj(count-1)
        Obj(count) = R;           %如果当前温度下最优路程小于上一路程则记录当前路程
    else
        Obj(count) = Obj(count-1);%如果当前温度下最优路程大于上一路程则记录上一路程
    end
    track(count,:) = S1;
    T0 = q * T0;     %降温
end

%% VII. 优化过程迭代图
figure
plot(1:count,Obj)
xlabel('迭代次数')
ylabel('距离')
title('优化过程')

%% VIII. 绘制最优路径图
DrawPath(track(end,:),X)

%% IX. 输出最优解的路线和总距离
disp('最优解:')
S = track(end,:);
p = OutputPath(S);
disp(['总距离:',num2str(PathLength(D,S))]);

计算距离的函数Distance.m:

function D = Distance(citys)
%% 计算两两城市之间的距离
% 输入 citys  各城市的位置坐标
% 输出 D  两两城市之间的距离

n = size(citys,1);
D = zeros(n,n);
for i = 1:n
    for j = i+1:n
        D(i,j) = sqrt(sum((citys(i,:) - citys(j,:)).^2));
        D(j,i) = D(i,j);
    end
end

画出路径的函数DrawPath.m:

function DrawPath(Route,citys)
%% 画路径函数
%输入
% Route  待画路径   
% citys  各城市坐标位置

figure
plot([citys(Route,1);citys(Route(1),1)],...
     [citys(Route,2);citys(Route(1),2)],'o-');
grid on

for i = 1:size(citys,1)
    text(citys(i,1),citys(i,2),['   ' num2str(i)]);
end

text(citys(Route(1),1),citys(Route(1),2),'       起点');
text(citys(Route(end),1),citys(Route(end),2),'       终点');

输出路径函数OutputPath.m:

function p = OutputPath(R)
%% 输出路径函数
% 输入:R 路径
R = [R,R(1)];
N = length(R);
p = num2str(R(1));
for i = 2:N
    p = [p,'―>',num2str(R(i))];
end
disp(p)

PathLength.m:

function Length = PathLength(D,Route)
%% 计算各个体的路径长度
% 输入:
% D     两两城市之间的距离
% Route 个体的轨迹

Length = 0;
n = size(Route,2);
for i = 1:(n - 1)
    Length = Length + D(Route(i),Route(i + 1));
end
Length = Length + D(Route(n),Route(1));

增加随机扰动产生新解NewAnswer.m:

function Length = PathLength(D,Route)
%% 计算各个体的路径长度
% 输入:
% D     两两城市之间的距离
% Route 个体的轨迹

Length = 0;
n = size(Route,2);
for i = 1:(n - 1)
    Length = Length + D(Route(i),Route(i + 1));
end
Length = Length + D(Route(n),Route(1));

我们的做法是随机产生两个城市让他们交换位置,从而得到一个新的路径。当然,这只是这个问题的一个做法,也有其他“增加随机扰动”的做法,而且对于多元函数问题更加简单,只要在当前解的附近增加一些小的值即可。

Metropolis准则的实现:

function [S,R] = Metropolis(S1,S2,D,T)
%% 输入
% S1:  当前解
% S2:   新解
% D:    距离矩阵(两两城市的之间的距离)
% T:    当前温度
%% 输出
% S:   下一个当前解
% R:   下一个当前解的路线距离

R1 = PathLength(D,S1);  %计算路线长度
N = length(S1);         %得到城市的个数

R2 = PathLength(D,S2);  %计算路线长度
dC = R2 - R1;   %计算能力之差
if dC < 0       %如果能力降低 接受新路线
    S = S2;
    R = R2;
elseif exp(-dC/T) >= rand   %以exp(-dC/T)概率接受新路线
    S = S2;
    R = R2;
else        %不接受新路线
    S = S1;
    R = R1;
end

程序结果:

《模拟退火(Simulated Annealing, SA)算法简介与MATLAB实现》

 

参考一个写得通俗易懂的: https://www.cnblogs.com/flashhu/p/8884132.html

    原文作者:蚁群算法
    原文地址: https://blog.csdn.net/lyxleft/article/details/82982567
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞