仅作自己学习使用
一、问题
需求: 计算函数
的极小值,其中个体x的维数n=10,即x=(x1,x2,…,x10),其中每一个分量xi均需在[-20,20]内。因此可以知道,这个函数只有一个极小值点x = (0,0,…,0),且其极小值是0,那么我们用模拟退火来实现一下:
二、matlab代码实现
clear
clc
T1 = cputime;
D = 10; % 变量维数
Xs = 20; % 上限
Xx = -20; % 下限
L = 20; % 马尔可夫链长度
K = 0.98; % 降温系数
S = 0.01; % 步长因子
T = 100; % 初始温度
YZ = 1e-8; % 容差
P = 0; % MetroPolis过程中总接受点(MetroPolis是接受准则,小于接受,大于是概率接受)
PreX = rand(D,1)*(Xs-Xx) + Xx; % 设置初值位置(这个随机数可以产生一个-20到20的随机数)
PreBestX = PreX; % 上一个最优解
PreX = rand(D,1)*(Xs-Xx) + Xx; % 虽然代码相同,但是因为随机数种子,产生的值会不同
BestX = PreX; % 最优解
trace = eval(BestX); % 记录初始值
deta = abs(eval(BestX) - eval(PreBestX));
while (deta>YZ) && (T>0.0001)
%% 在当前温度T下的迭代次数为L
for i = 1 : L
% 在此点附近随机选择下一个点
NextX = PreX + S*(rand(D,1)*(Xs-Xx) + Xx);
% 如果这个点其中有个分量超出了定义域,则重新分配一个值
for j = 1:D
if(NextX(j) > Xs || NextX(j)<Xx)
NextX(j) = PreX(j) + S*(rand*(Xs-Xx) + Xx);
j = j-1; % 因为重新分配的值任然可能超出边界,所以退回到当前的那个j,再次检查是否超出边界
end
end
%% 判断是否是全局最优解
if( eval(NextX) < eval(BestX) )
PreBestX = BestX; % 保留上一个最优解
BestX = NextX; % 更新上一个最优解
end
%% MetroPolis接受准则
if( eval(NextX) < eval(PreX) )
% 当前解更优秀,接受新解
PreX = NextX;
P = P + 1;
else
% 当前解更差,概率接受
P1 = exp((eval(PreX)-eval(NextX))/T); % 注意指数部分是个复数,所以要自己调整减的顺序
if (P1 > rand)
PreX = NextX;
P = P + 1;
end
end
trace = [trace eval(BestX)];
end
deta = abs(eval(BestX)-eval(PreBestX));
%% 本次退火结束,降温
T = K * T;
end
T2 = cputime;
% 运行代码所需要的CPU时间
timeConsume = T2 - T1;
disp('最小值点在:');
BestX
disp('最小值为');
eval(BestX)
figure(color=[1 1 1])
plot(trace(2:end),Color=[0.502, 0.000, 0.502],LineWidth=2);
xlabel("迭代次数")
ylabel("目标函数值")
title("适应度曲线","CPU时间消耗: "+timeConsume + 's');
function result = eval(x)
%% 评估函数
result = sum(x.^2);
end
三、效果图
可以看到,最后是达到0.0269403,还不是真正意义上的极小值,因为模拟退火算法是模拟固体退火的原理,其结果的好坏与退温系数非常相关,温度下降得越慢,结果越精确。但是代码中有一个问题,也就是NextX = PreX + S*(rand(D,1)*(Xs-Xx) + Xx);
这行代码,非常清楚的看到这下一个X的跳动范围是与温度无关的,但是根据物理退火的过程,温度越高,分子越活跃,对应退火模型中的x应该跳动范围更大,所以这行代码应该是值得改进的,比如改成:NextX = PreX + T*(rand(D,1)*(Xs-Xx) + Xx);
但是这样又会极大的增加搜索时间。
如果有朋友有其他的方案,欢迎大家在评论区留言。