模拟退火消化(MATLAB

(大概

总括一下,模拟退火算法,仍然是算力有限情况下的近似推断,只能得到模糊的近似正确的结果。
毕竟如本题的情况,如果穷举,情况还是太多了,A_100的话,近似指数阶了。按照一个朴素的想法的话,就算你再怎么优化,一个算法总得把所有情况大概走一遍,那么最坏情况下的复杂度仍然是O(n)的,而对于这样大的计算量,反正我家电脑是一定要爆栈的。
(神威太湖之光不妨一试。

题目部分

一个TSP问题(旅行商问题,一架飞机,从某一经纬度出发,前往100个目标经纬度进行巡视一周,最终回到出发点,假设飞机速度为1000km/h,则最少需要的时间是多少?

代码部分

d(i,j)的部分是求解距离,此时用到经度纬度及地球半径,求解距离,这里用到的是弧长近似距离。使用了相应的三角变换。

%  MATLAB代码
clc,clear;
sj0=load('sj.txt');
x=sj0(:,[1:2:8]);x=x(:);%将x变成列向量
y=sj0(:,[2:2:8]);y=y(:);%将y变成列向量
sj=[x y];%
d1=[70 40];%出发地点
sj=[d1;sj;d1];%起点,中间过程,终点
sj=sj*pi/180;%角度化成弧度
d=zeros(102);%距离矩阵初始化
for i=1:101
   for j=i+1:102
       d(i,j)=6370*acos(cos(sj(i,1)-sj(j,1))*cos(sj(i,2))*cos(sj(j,2))+sin(sj(i,2))*sin(sj(j,2)));%计算任意两点之间的距离
   end
end
d=d+d';%矩阵等于矩阵加上转置,构造一个对称矩阵
path=[];
long=inf;%巡航路线及长度优化
rand('state',sum(clock));%初始化随机数发生器
for j=1:1000 %求较好的初始解
   path0=[1 1+randperm(100),102];temp=0;
   for i=1:101
       temp=temp+d(path0(i),path0(i+1));
   end
   if temp<long
       path=path0;
       long=temp;
   end
end
e=0.1^50;L=2000000;at=0.999;T=1;
for k=1:L %退火过程
   c=2+floor(100*rand(1,2));%产生新的解
   c=sort(c);c1=c(1);c2=c(2);
   %计算代价函数值的增量
   df=d(path(c1-1),path(c2))+d(path(c1),path(c2+1))-d(path(c1-1),path(c1))-d(path(c2),path(c2+1));
   if df<0 %接受准则
       path=[path(1:c1-1),path(c2:-1:c1),path(c2+1:102)];long=long+df;
   elseif exp(-df/T)>=rand       %%%%%手动加一个飞雷神标记%%%%%一会说这个接受准则
       path=[path(1:c1-1),path(c2:-1:c1),path(c2+1:102)];long=long+df;
   end
   T=T*at;
   if T<e
       break;
   end
end
path,long %输出巡航路线及其长度
xx=sj(path,1);yy=sj(path,2);
plot(xx,yy,'-*') %画出巡航路径
//C++给了点作用,用于图像识别之后,格式化写到对应的文本文件里面
#include<iostream>
using namespace std;
int main(){
    
    
    FILE * f=NULL;
    FILE * p=NULL;
    f=fopen("temp.txt","r");
    p=fopen("sj.txt","w+");
    for(int i=0;i<25;i++){
    
    
        float a,b,c,d;
        fscanf(f,"%f%f%f%f",&a,&b,&c,&d);
        fprintf(p,"%.4f    %.4f    %.4f    %.4f    ",a,b,c,d);
        printf("%.4f    %.4f    %.4f    %.4f    ",a,b,c,d);
        fscanf(f,"%f%f%f%f",&a,&b,&c,&d);
        fprintf(p,"%.4f    %.4f    %.4f    %.4f\n",a,b,c,d);
        printf("%.4f    %.4f    %.4f    %.4f\n",a,b,c,d);
    }
    fclose(f);
    fclose(p);
    return 0;
}

正文

模拟退火是模拟物质的降温过程,无论如何他是认为吸热放热也是一个概率问题,即高温的时候,放热的概率要远大于吸热,低温同理,最后达到一个吸热放热概率相等的情况,则近似平衡了。即这里的路线中各个点的排列变化,其实就是一种物质的高能量状态到低能量状态的转变,其能量体现为消耗的时间。时间越长,则认为能量越高。

那么依据何种规则进行状态的转化呢,如下两种:
1、二变换法,即交换排列中两个点的位置,增加或者减少一个逆序对的方式进行转化。
2、三变换法,任选三个点,将前两个点插入到最后一个点的后面来转化为新的路径。

%%%%%飞雷神标记%%%%%%

接受准则:
1、若能量减少,则必然接受(看似合理其实,可能造成无法求得最优解,因为贪心的局部最优,在此处未必是全局最优。
2、所以第二条其实是用来补充第一条,计算其能量差,若能量增加,则以概率exp(-delta f/T)的概率判定是否接受,这个概率,是根据物理热力学算出来的。
【而T即为温度,每次操作,假设温度以一定的比例进行减少,即物质的总趋势是趋向于降温的。】

具体流程如下,
对于此问题,首先生成一个解空间,表示为{1 2 3 ……101 102},对于所有固定起点和终点的循环排列集合,每一个代表一个回路。
首先用蒙特卡洛方法大概求一个较好的初始解,为了首先排除明显不合理解,减少后面的模拟退火的代偿计算量,保证模拟退火能够发挥宰牛刀的作用,而不必在有明显单调性的区间上浪费算力。

改进方法:
通过实际演算可以得到,模拟退火在此问题中得到的最终解,实际上是十分不稳定的。实际求解,能够得到44h到41h不等的时间,而标准差与不确定度是较大的。
但是,实际上应当是不存在精确解的吧,因为本身用弧长来代替距离首先是一种精确,同时也是一种精度缺失。不过如果使用动态规划的话,我觉得倒是可以,yysy这道题还是能求精确解的吧,因为动态规划复杂度我没算错的话是O(n^2)。

所以,算力不足,就先算,存起来较好结果,然后接着算,可以把上一次的结果当做较好的初始解。动态规划有时间试试。

结束条件

结束条件有两个,一个是保证降温过程充分碰撞的L参数,表示经过L次操作之后,必然退出,另一个是表示温度参数的T,当T小于某一值(本题中表示为e,设定为了0.1^30)。
所以,当温度达到一定的稳定态,即此时的温度已经满足我们的需要的时候可以退出,或者当次数满足一定阈值时,也可以退出。当然,单一的限制明显也是说的过去的,所以当两者都作为约束条件的时候,可以发现,无论是极大扩大L的倍数,比如将其扩大50倍,或者100倍,最终结果仍然与先前没有明显差异,这与常识中的模拟次数越多,越趋近于稳定是不可相联想的。而对于T也一样,改变alpha(降温常数,即本题中的at值同理。

来看折纸

以后每天折纸来打卡,这个假期希望成为一个折纸带师。
今天就来看点MATLAB无内鬼图吧,呐呐呐呐,呐呐呐!
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_47741017/article/details/112913412
今日推荐