这些天疫情在家里自己看高级人工智能,写了一个模拟退火算法做函数优化的例子,防止以后忘了…
优化函数:
f
(
x
)
=
x
4
−
2
x
3
+
32
x
2
+
6
x
f(x)=x^{4}-2x^{3}+32x^{2}+6x
f(x)=x4−2x3+32x2+6x
函数图像:
c++源码:
#include<iostream>
#include<vector>
#include<cstdlib>
#include<cmath>
#include<ctime>
using namespace std;
double E(const vector<double> &X)
{
const double X2=X[0]*X[0];
return (X2*X2-2*X2*X[0]-32*X2+6*X[0]);
}
double random(double start=0,double end=1)
{
return (rand()/(double)RAND_MAX)*(end-start)+start;
}
void drop(double &t0)
{
static double tc=10.0;
tc-=0.05;
t0-=(tc>0.05?tc:0.05);
if(t0<=1) //常温
t0=1;
}
double P(double Ei,double Ej,double t)
{
return exp(-(Ej-Ei)/t);
}
int main()
{
srand(time(NULL));
vector<double> v0={-10.0}; //粒子初始位置(随意设置,不要过大过小即可)
double t0=1200; //初始温度
const double alpha=0.1; //活跃度系数(连续邻域->离散邻域)
int cnt=0;
while(cnt<=20000)
{
vector<double> vn;
vn.push_back(rand()&1?v0[0]-alpha*t0:v0[0]+alpha*t0); //alhpa*t0为t0温度下粒子活跃度
double Ei=E(v0),Ej=E(vn);
if(Ei>Ej||P(Ei,Ej,t0)>random())
{
v0[0]=vn[0];
}
drop(t0);
cnt++;
if(cnt%100==0)
{
vector<double>::iterator it=v0.begin();
for(;it!=v0.end();it++)
{
cout<<cnt<<"th result and tempreture:[ "<<*it<<" , "<<t0<<" ]"<<endl;
}
}
}
return 0;
}
这几天,深入研究了一下,发现了上次写的模型有一些小问题,例如降温函数,以及邻域函数的随机性问题,以及缺少内循环,导致了需要大量的迭代才能得到一个精确度不是很高的解,于是我更改了源代码,去优化难度更大的二维函数,发现效果特别好。
被优化的二维函数:
f
(
x
)
=
x
4
+
y
4
−
8
x
3
+
5
y
3
−
32
x
2
−
74
y
2
f(x)=x^{4}+y^{4}-8x^{3}+5y^{3}-32x^{2}-74y^{2}
f(x)=x4+y4−8x3+5y3−32x2−74y2
等高线图:
c++源码:
主要将原离散二次降温函数更改为了
1
l
o
g
(
1
+
t
)
\frac{1}{log(1+t)}
log(1+t)1,
t
t
t为时间从1开始增长,不设置末温,但是根据函数理论上讲是0,不过永远不可能达到(有点绝对零度的意思?)。
在外循环中加入100次内循环,并且在邻域选择加入了随机性。效果很好,而且鲁棒性和精确度都很高。
#include<iostream>
#include<vector>
#include<cstdlib>
#include<cmath>
#include<ctime>
using namespace std;
double E(const vector<double> &X)
{
double x2=X[0]*X[0],y2=X[1]*X[1];
return x2*x2+y2*y2-8*x2*X[0]+5*y2*X[1]-32*x2-74*y2;
}
double random(double start=0,double end=1)
{
return (rand()/(double)RAND_MAX)*(end-start)+start;
}
void drop(double &t0)
{
static double tot=1;
t0=t0/log(tot+1);
tot++;
}
double P(double Ei,double Ej,double t)
{
return exp(-(Ej-Ei)/t);
}
int main()
{
srand(time(NULL));
vector<double> v0={random(-100000,100000),random(-100000,100000)}; //粒子初始位置(随意设置,不要过大过小即可)
double t0=120000; //初始温度
const double alpha=10; //活跃度系数(连续邻域->离散邻域)
int cnt=0;
while(cnt<=30)
{
const int k=100;
for(int i=0;i<=k-1;i++)
{
vector<double> vn={v0[0]-alpha*t0*random(-1,1),v0[1]-alpha*t0*random(-1,1)}; //alhpa*t0为t0温度下粒子活跃度
double Ei=E(v0),Ej=E(vn);
if(Ei>Ej||P(Ei,Ej,t0)>random())
{
v0[0]=vn[0];
v0[1]=vn[1];
}
}
drop(t0);
cnt++;
if(cnt%1==0)
{
cout<<cnt<<"th result and tempreture:[ ";
vector<double>::iterator it=v0.begin();
for(;it!=v0.end();it++)
{
cout<<*it<<" ";
}
cout<<"] , "<<t0<<endl;
}
}
return 0;
}