个人感觉模拟退火就是通过随机法逐步趋近于最优值。
结合例题进行说明:
一、求三角形的费马点(平面内到三个顶点距离和最小的点)。
题目链接:
https://open.kattis.com/problems/europeantrip
#include<bits/stdc++.h>
using namespace std ;
struct point
{
double x , y ;
} p[5] ;
int row[4] = {-1 , 1 , 0 , 0} ; //向四个方向移动点寻找最优解
int col[4] = {0 , 0 , -1 , 1} ;
double len(point a , point b) //两个点的距离
{
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)) ;
}
double cal(point a) //计算点到三个顶点的距离和
{
return len(a , p[1]) + len(a , p[2]) + len(a , p[3]) ;
}
double random1() //生成[0,1)区间的随机浮点数
{
return double(rand() % 10000) / 10000.0 ;
}
void solve()
{
int i ;
point ans , next ;
point now ;
ans.x = (p[1].x + p[2].x + p[3].x) / 3 ;
ans.y = (p[1].y + p[2].y + p[3].y) / 3 ;
double t = 100 ; //温度
double delta = 0.99 ; //降温的速度
double dE ;
double ans1 ;
double inf = 1000000000 ;
while(t > 1e-8)
{
ans1 = inf ;
for(i = 0 ; i < 4 ; i ++) //向四个方向移动
{
next.x = ans.x + row[i] * t ;
next.y = ans.y + col[i] * t ;
if(cal(next) < ans1)
{
ans1 = cal(next) ;
now = next ;
}
}
dE = cal(now) - cal(ans) ;
if(dE < 0.0 || exp(dE / t) > random1()) //距离和减小 或 以较小的概率向距离和增大的方向移动
{
ans = now ;
}
t *= delta ;
}
printf("%.12f %.12f" , ans.x , ans.y) ;
}
int main()
{
int i , j ;
for(i = 1 ; i <= 3 ; i ++)
scanf("%lf%lf" , &p[i].x , &p[i].y) ;
solve() ;
}
二、求解方程的根,然后代入弧长积分。求解根时可以转化为求绝对值的最小值用模拟退火。
题目链接:
#include<bits/stdc++.h>
using namespace std ;
double p , d , a ;
double delta = 0.999 ; //降温速度
double t ;
double tempa , tempans ;
double ans ;
double dE , maxd ;
int row[2] = {-1 , 1} ;
double cal(double h) //求根
{
return fabs(h / 2 * (exp(d / (2 * h)) + exp(-d / (2 * h))) - h - maxd) ;
}
double random1() //生成[0,1)区间的随机浮点数
{
return double((rand() % 10000) / 10000.0) ;
}
double f(double h) //弧长积分
{
return h * (exp(d / (2 * h)) - exp(-d / (2 * h))) ;
}
int main()
{
int i ;
double num , tempnum ;
double ans ;
while(scanf("%lf" , &p) && p != -1)
{
scanf("%lf" , &d) ;
t = 100 ; //温度
a = 7 ; //初始值
maxd = p - 4.2 ;
ans = 3000 ;
while(t > 1e-9)
{
tempnum = 1000000000 ;
for(i = 0 ; i < 2 ; i ++)
{
num = cal(a + row[i] * t) ;
if(num < tempnum)
{
tempa = a + row[i] * t ;
tempnum = num ;
}
}
dE = ans - tempnum ;
if(dE > 0.0 || exp(dE / t) > random1())
{
a = tempa ;
ans = min(ans , tempnum) ;
}
t *= delta ;
}
ans = f(a) ;
ans=(int)(ans*1000); //截断后面的小数而不是四舍五入
ans=(double)(ans*1.0/1000);
printf("%.3f\n" , ans) ;
}
}