您的位置:首页 > 其它

2014 ACM/ICPC Asia Regional Xi'an Online Ellipsoid 学习模拟退火随机算法

2014-09-17 21:48 441 查看
这是一个并行算法,很有效。

先贴一个伪代码描述:

/*
引:http://www.cnblogs.com/heaad
* J(y):在状态y时的评价函数值
* Y(i):表示当前状态
* Y(i+1):表示新的状态
* r: 用于控制降温的快慢
* T: 系统的温度,系统初始应该要处于一个高温的状态
* T_min :温度的下限,若温度T达到T_min,则停止搜索
*/
while( T > T_min )
{
  dE = J( Y(i+1) ) - J( Y(i) ) ;

  if ( dE >=0 ) //表达移动后得到更优解,则总是接受移动
Y(i+1) = Y(i) ; //接受从Y(i)到Y(i+1)的移动
  else
  {
// 函数exp( dE/T )的取值范围是(0,1) ,dE/T越大,则exp( dE/T )也
if ( exp( dE/T ) > random( 0 , 1 ) )
Y(i+1) = Y(i) ; //接受从Y(i)到Y(i+1)的移动
  }
  T = r * T ; //降温退火 ,0<r<1 。r越大,降温越慢;r越小,降温越快
  /*
  * 若r过大,则搜索到全局最优解的可能会较高,但搜索的过程也就较长。若r过小,则搜索的过程会很快,但最终可能会达到一个局部最优值
  */
  i ++ ;
}


poj 1379

先做个题熟悉一下

#include<math.h>
#include<time.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define max(a,b)  (a)>(b)?(a):(b)
const int maxn=10+1000;
struct P
{
double x,y,dis;
}a[maxn],b[maxn],ans;
int X,Y,N,M,T;
double dis(P c)         //计算最短距离
{
double ans=1e8,t;
for(int i=0;i<M;i++)
{
t=sqrt((a[i].x-c.x)*(a[i].x-c.x)+(a[i].y-c.y)*(a[i].y-c.y));
if(ans>t)
ans=t;
}
return ans;
}
int main()
{
int i,j,k;
scanf("%d",&T);
srand(time(NULL));
while(T--)
{
scanf("%d%d%d",&X,&Y,&M);
for(i=0;i<M;i++)
scanf("%lf%lf",&a[i].x,&a[i].y);

//得到随机点集
double ran;
for(i=0;i<500;i++)
{
ran=rand();
b[i].x=cos(ran)*X+1;
b[i].y=sin(ran)*Y+1;
b[i].dis=dis(b[i]);
}
P tmp,ans;
ans.dis=-1;
for(double t=max(X,Y);t>1e-2;t*=0.9)
for(i=0;i<500;i++)
for(int L=1;L<=50;L++)
{
ran=rand();
tmp.x=b[i].x+cos(ran)*t;//取点,用弧度不影响
tmp.y=b[i].y+sin(ran)*t;
if(tmp.x<0 || tmp.y<0 || tmp.x>X || tmp.y>Y) continue;//符合题意
tmp.dis=dis(tmp);
if(tmp.dis>b[i].dis) b[i]=tmp;
if(tmp.dis>ans.dis)  ans=tmp;
else if(exp((tmp.dis-b[i].dis)/t)>(rand()/RAND_MAX) )b[i]=tmp;//这个加了效果反而不好
}

printf("The safest point is (%.1f, %.1f).\n",ans.x,ans.y);
}
return 0;
}


Ellipsoid

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>

using namespace std;
const double eps = 1e-8;
const double r = 0.99;  //降温速度
const int dx[] = { 0, 0, 1, -1, 1, -1, 1, -1 };
const int dy[] = { 1, -1, 0, 0, -1, 1, 1, -1 };
double a, b, c, d, e, f;

double dis(double x, double y, double z) {
return sqrt(x * x + y * y + z * z);
}

//已知x,y,求z
double getz(double x, double y) {
double A = c, B = e * x + d * y,
C = a * x * x + b * y * y + f * x * y - 1;
double delta = B * B - 4 * A * C;
if (delta < 0) return 1e60;
double z1 = (-B + sqrt(delta)) / 2 / A,
z2 = (-B - sqrt(delta)) / 2 / A;
if (z1 * z1 < z2 * z2) return z1;
else return z2;
}//二次函数写的很好

double solve() {
//模拟退火
double step = 1;    //步长
double x = 0, y = 0, z;
while (step > eps) {
z = getz(x, y);
for (int i = 0; i < 8; i++) {
double nx = x + dx[i] * step,
ny = y + dy[i] * step,
nz = getz(nx, ny);
if (nz > 1e30) continue;
if (dis(nx, ny, nz) < dis(x, y, z)) {
x = nx; y = ny; z = nz;
}
}
step *= r;
}
return dis(x, y, z);
}

int main() {
while (scanf("%lf%lf%lf%lf%lf%lf", &a, &b, &c, &d, &e, &f) != EOF) {
printf("%.8f\n", solve());
}
return 0;
}


模拟退火步长选择

1.以一个点为中心四周发散八个方向

const double eps = 1e-8;
const double r = 0.99;  //降温速度
const int dx[] = { 0, 0, 1, -1, 1, -1, 1, -1 };
const int dy[] = { 1, -1, 0, 0, -1, 1, 1, -1 };
2.

使用弧度制,随机角度
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐