模拟退火算法(SA)

算法背景

模拟退火算法(Simulated Annealing,SA)最早的思想是由N. Metropolis [1] 等人于1953年提出。1983 年,S. Kirkpatrick 等成功地将退火思想引入到组合优化领域。它是基于Monte-Carlo迭代求解策略的一种随机寻优算法,其出发点是基于物理中固体物质的退火过程与一般组合优化问题之间的相似性。 从某一较高初温出发,伴随温度参数的不断下降,结合概率突跳特性在求解空间中随机寻找目标函数的全局最优解,即在局部最优解能概率性地跳出并最终趋于全局最优。

优点:

  • 模拟退火算法是一种通用的优化算法,理论上算法具有概率的全局优化性能。
  • 赋予搜索过程一种时变且最终趋于零的概率突跳性,从而可有效避免陷入局部极小并最终趋于全局最优的串行结构的优化算法。

应用:VLSI(超大规模集成电路)最优设计、图像处理、组合优化问题、生产调度、控制工程、机器学习、神经网络、信号处理等领域。

算法思想

模拟退火算法的思想借鉴于固体的退火过程,当固体的温度很高时,内能比较大,固体内的粒子处于快速无序运动状态,当温度慢慢降低,固体的内能减小,粒子逐渐趋于有序,最终固体处于常温状态,内能达到最小,此时粒子最为稳定。

模拟退火算法包含两个部分即Metropolis算法和退火过程。Metropolis算法就是如何在局部最优解的情况下让其跳出来,是退火的基础。

Metropolis准则

1953年Metropolis提出重要性采样方法,即以概率来接受新状态,而不是使用完全确定的规则,称为Metropolis准则,可以显著减小计算量。

假设前一状态为 x(n),系统受到一定扰动,状态变为 x(n+1),相应地,系统能量由 E(n) 变为 E(n+1)。 定义系统由 x(n)) 变为 x(n+1) 的接收概率为 p(probability of acceptance):

模拟退火算法(SA)_第1张图片

公式解释:当状态转移之后,如果能量减小了,那么这种转移就被接受了(以概率 1 发生)。如果能量增大了,就说明系统偏离全局最优位置(能量最低点,模拟退火算法所要寻找的就是密度最高能量最低的位置)更远了,此时算法不会立即将其抛弃,而是进行概率判断:首先在区间 [0,1]产生一个均匀分布的随机数 ε ,如果 ε

其核心思想是当能量增加时以一定概率接收,而不是一味的拒绝,即陷入局部最优时有一定概率能跳出局部最优,其中以能量的变化量和 T 进行决定概率 p 的大小,所以这个 p 值是动态的。

退火算法参数控制

Metropolis算法可以调整 T 的大小,控制算法收敛速度。T 如果过大,就会导致退火太快,达到局部最优值就会结束迭代,如果取值较小,则计算时间会增加。实际应用中采用退火温度表,在退火初期采用较大的T值,随着退火的进行,逐步降低,具体如下:

  • 初始的温度T(0)应选的足够高,使的所有转移状态都被接受。初始温度越高,获得高质量的解的概率越大,耗费的时间越长。
  • 退火速率 :最简单的下降方式是指数式下降。T=T , 其中是小于1的正数,一般取值为0.8到0.99之间,使得对每一温度,有足够的转移尝试,指数式下降的收敛速度比较慢。
  • 终止温度 :如果在若干次迭代的情况下没有可以更新的新状态或者达到用户设定的阈值,则退火完成。

定义对照表

固体退火 模拟退火算法
粒子状态 可行解
粒子能量最低状态 最优解
设置初始温度 初始解
能量 目标函数
冷却 控制参数的下降
等温过程 Metropolis抽样过程

算法流程

算法实质分两层循环,在任一温度水平下,随机扰动产生新解,并计算目标函数值的变化,决定是否被接受。由于算法初始温度比较高,这样,使 E 增大的新解在初始时也可能被接受,因而能跳出局部极小值,然后通过缓慢地降低温度,算法就最终可能收敛到全局最优解,具体流程为:

模拟退火算法(SA)_第2张图片

算法流程图:

模拟退火算法(SA)_第3张图片

算法测试

  • 我们用熟悉的TSP问题对算法进行测试
% 模拟退火算法解决SA解决TSP问题
clear all; close all;  clc;                         
%% 初始化
load('city.mat');       % 导入城市数据
C=city;
n=size(city,1);         % 得到TSP问题的规模,即城市数目
T=100*n;                % 设置初始温度
L=100;                  % 马可夫链长度
K=0.99;                 % 衰减参数(退火速率)
l=1;                                % 初始化统计迭代次数为1
len(l)=distance(city,n);            % 每次迭代后的路线长度(目标函数值)
 
figure(1); 
% 多次迭代扰动,温度降低之前多次实验
while T>0.01  % 停止迭代温度
    % 内层循环得到新的解
    for i=1:L            
        len1=distance(city,n);         
        p1=floor(1+n*rand());       % 随机选择两个不同的城市的坐标
        p2=floor(1+n*rand());
        while p1==p2                % 确保选择的两个城市不一样
            p1=floor(1+n*rand());
            p2=floor(1+n*rand());
        end
        tmp_city=city;              % 置换两个不同的城市的坐标
        tmp=tmp_city(p1,:);
        tmp_city(p1,:)=tmp_city(p2,:);
        tmp_city(p2,:)=tmp;
  
        len2=distance(tmp_city,n);    % 计算新路线的距离 
        delta_e=len2-len1;            % 新老距离的差值,相当于能量的差值
        
        % 新路线好于旧路线,用新路线代替旧路线
        if delta_e<0        
            city=tmp_city;
        else
        % 以概率选择是否接受新解
            if exp(-delta_e/T)>rand()
                city=tmp_city;      
            end
        end
    end
    l=l+1;                       % 更新迭代次数
    len(l)=distance(city,n);     % 计算新路线距离 
    T=T*K;                       % 更新温度
 
    for i=1:n-1
        plot([city(i,1),city(i+1,1)],[city(i,2),city(i+1,2)],'bo-');
        hold on;
    end
    plot([city(n,1),city(1,1)],[city(n,2),city(1,2)],'ro-');
    title(['最短距离:',num2str(len(l))]);
    hold off;
    pause(0.002);
end
 
figure(2);
for i=1:n-1
    plot([city(i,1),city(i+1,1)],[city(i,2),city(i+1,2)],'bo-');
    hold on;
end
plot([city(n,1),city(1,1)],[city(n,2),city(1,2)],'ro-');
for i=1:n
    text(C(i,1)+0.5,C(i,2),num2str(i));       %标号  
end
text(city(1,1),city(1,2),'    起点');
text(city(n,1),city(n,2),'    终点');
title('最终路线图');
 
figure(3);
plot(len)
xlabel('迭代次数')
ylabel('目标函数值')
title('适应度进化曲线')
 
 
function len=distance(City,n)
len=0;
for i=1:n-1
    len=len+sqrt(sum((City(i,:)-City(i+1,:)).^2));
end
len=len+sqrt(sum((City(n,:)-City(1,:)).^2));
end 

代码中的city.mat文件,和脚本放同一个文件夹即可,懒得下文件的可以自己定义一个30x2的矩阵

链接:https://pan.baidu.com/s/1j7omLCzx1uIJS0VdBbR-eA
提取码:1234

  • 再测试一下)0-1 背包问题
% 模拟退火算法SA 解01背包问题
clc;clear;close all
weight = [2,5,18,3,2,5,10,4,11,7,14,6];    % 每个货物重量
price = [5,10,13,4,3,11,13,10,8,16,7,4];   % 每个货物单价
weight_cons = 46;   % 背包重量约束
%初始化
alpha = 0.95;       % 退火系数
t_begin = 200;      % 最高温度
t_end = 0.1;        % 结束温度
t = t_begin;        % 当前退火温度
solution_new = ones(1,length(weight));    % 新解
solution_current = zeros(1,length(weight));  % 当前解
value_current = 0;          % 当前解的价值
value_best =0;              % 最大的价值
solution_best = solution_new;
counter = 0;        % 记录迭代次数
while t>t_end
    counter = counter+1;
    for i = 1:100    % 马尔科夫长度
        %生成新的解
        index = randi([1,length(weight)],1,1);  %随机生成1~10的一个整数
        solution_new(1,index) =~solution_new(1,index);
        % 判断是否满足约束
        while sum(solution_new.*weight) > weight_cons
            index = randi([1,length(weight)],1,1);
            solution_new(1,index) =~solution_new(1,index);
        end
        % 判断是否接受当前解
        value_new = sum(solution_new.*price);            % 计算最新价值
        probability = exp((value_new-value_current)/t);  % 当前概率
        if (value_new>value_current)||probability>rand   % 新解更好或者劣解满足概率
            value_current = value_new;
            solution_current = solution_new;
        else
            solution_new = solution_current;
        end
        % 与最优解对比
        if value_current>value_best
            value_best = value_current;
            solution_best = solution_current;
        end
    end
    %保存每个过程的最优解
    value_list(counter,:)= value_best;
    solution_list(counter,:) = solution_best;
    t = t*alpha;
end
%显示当前最优解
figure(3);
plot(value_list)
xlabel('迭代次数')
ylabel('目标函数值')
title('适应度进化曲线')
fprintf('最大价值:%f, 货物重量:%d\n',value_best,sum(solution_best.*weight));
disp(['解:',num2str(solution_best)]);

原文作者:Handsome_Zpp

原文地址:模拟退火算法(SA)_Handsome_Zpp的博客-CSDN博客(版权归原文作者所有,侵权留言联系删除)

你可能感兴趣的:(模拟退火算法,机器学习,算法)