随机算法-模拟退火
来源:互联网 发布:知乎精彩回答 编辑:程序博客网 时间:2024/06/05 05:41
爬山算法
一种搜索算法,思想类似走山路,从某点开始不断往四周相邻更高的点走去,当走到某点已经是山峰顶,四周没有比当前点更高的点就停下,认为当前点是目标。
其中“高”是指点的估值,类似A*的那个意思。
但爬山法得出的一定是可行解,但不一定是最优解。
如图,如果从A点出发,则到达C点后因为四周没有比它更低的点就停止了,导致到不了真正的最高点D,而从B点出发却能到达最优解
模拟退火
爬山算法的改进,但仍然是随机算法,不一定求出最优解。跟爬山不同的是:即使四周没有更高的点,我们也以一定概率接受比当前点更低的点,这样就有机会摆脱局部最优而到达最优解,而这个概率会随时间不断减小。一定的概率是多大呢?参考热力学公式:在温度为T时,出现能量差为dE的降温的概率为P(dE)=
代码
ans = 初始解E = val(ans); //解的估值 while(T > T_min){ nE = val(next); //新点的估值 dE = nE - E; if(dE > 0 || eps(dE/T) > random(0,1) ){ //高度更高或者高度更低但符合概率去接受这个更低点 ans = next; e = nE; //接受新点的话更新对应值 } T = T * r; //0<r<1,r越大找到最优解的几率更大,r过小可能陷入局部解}
其中eps(dE/T) > random(0,1)
是因为dE < 0则 eps(dE/T)
< 1。随着程序进行,T越来越小,而接受更低点的概率就越来越小。
在具体题目的代码中,T相当于步长,初始的ans和T应能覆盖所有的解,对于每个当前点仅选一个点作为新点是不够的,所以可以选当前点的附近若干个点,再从里面选最大。同时,初始点也可以是个点集,并行扫描效果更佳。且eps(dE/T) > random(0,1)
可以改成其他,只要符合T越小接受的几率越小就行。
还没看懂的可以看下链接:
http://wenku.baidu.com/view/5e0cdfd376eeaeaad1f330f4.html
题目
POJ 1379 Run Away
题意:T组数据 ,在给出长宽的矩形里有n个点,求某点坐标,该点 跟距离跟它最近的点 最远。
#include <cstdio>#include <cmath>#include <cstring>#include <string>#include <map>#include <queue>#include <iostream>#include <algorithm>#include <time.h>#include <iomanip>using namespace std;#define RE freopen("1.in","r",stdin);#define SpeedUp std::cout.sync_with_stdio(false);const int maxn=1e5+50;const int inf = 0x7FFFFFFF; //可改勿删,不知名bug,以前遇到过是越界导致,但我并没有找到越界?const double PI = acos(-1.0);int n;double X,Y;struct Point{ double x,y; Point(){} Point(double _,double __):x(_),y(__){} void read(){ cin>>x>>y; }}p[maxn];double dis(Point a,Point b){ return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}double minDis(Point cur){ double ans = 1e80; for (int i = 0; i < n; ++i){ ans = min(ans,dis(cur,p[i])); } return ans;}double randNum(){ //rand()生成[0,32767),包装下生成[0,1) return rand()%10000/10000.0;}void solve(Point &ans,double T){ double T_min = 1e-8; double E = minDis(ans);//当前高度,即估值 int count = 30;//对当前点取周围count个点,再从里面挑最高的当下一个点 while(T > T_min){ //温度,也即本题步长 Point next; double nE = 0.0; for (int i = 0; i < count; ++i){ Point tmp; double angle = randNum()*2*PI; //[0,2π) tmp.x = ans.x + cos(angle)*T; //ans.x + [-1,1)*T tmp.y = ans.y + sin(angle)*T; tmp.x = min(X,max(0.0,tmp.x)),tmp.y=min(Y,max(0.0,tmp.y)); double tE = minDis(tmp); if(tE > nE){ //取周围所有点最高 nE = tE; next = tmp; } } double dE = nE - E; //新点和原点的高度差 if(dE >= 0.0 || exp(dE/T) > randNum()){ //更高或者随机得符合降温条件 // cout<<"E = "<<E<<"\t("<<next.x<<","<<next.y<<")\t"<<" nE = "<<nE<<endl; E = nE; ans = next; } T = T * 0.8; //0.8改大则更可能得到最优解,时间更长,改小则可能局部解 }}int main(){ // RE SpeedUp int t; srand(time(NULL)); //c++提交,g++会因为这个RE cin>>t; while(t--){ cin>>X>>Y>>n; for (int i = 0; i < n; ++i){ p[i].read(); } Point ans = Point(X/2,Y/2); //起始为中心点 double T = sqrt(X*X+Y*Y)/2.0; //步长为对角线长度,包含了整个域 solve(ans,T); cout <<setiosflags(ios::fixed); cout<<"The safest point is ("<<setprecision(1)<<ans.x<<", "<<setprecision(1)<<ans.y<<")."<<endl; } return 0;}
POJ 2420 A Star not a Tree?
题意:给n个点,求到这些点距离和最近的那个点对应的距离和。
这题还是比较中规中举,虽然输入的是int,但处理方便还是用了double存,不过因为这题求的是最近(小),而爬山本身应该是往最高走,所以我们取个符号就行了(也可以把大于小于号反过来)。
#include <cstdio>#include <cmath>#include <cstring>#include <time.h>#include <iostream>#include <algorithm>using namespace std;#define RE freopen("1.in","r",stdin);#define SpeedUp std::cout.sync_with_stdio(false);const int maxn=1e4+5;const int inf = 0x3f3f3f3f;const double PI = acos(-1.0);struct Point{ double x, y, z; void read() { cin >> x >> y; }} p[maxn];int n;double dis(Point a, Point b) { return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) );}double getVal(Point cur){ //距离和的负数 double ans = 0.0; for (int i = 0; i < n; ++i){ ans += dis(p[i],cur); } return -ans;}double rand1(){ //[0,1) return rand()%10000/10000.0;}double solve(){ Point ans; ans.x = 5000,ans.y = 5000; double T = sqrt(5000.0*5000+5000*5000)/2; //半径 double eps = 1e-6; double E = getVal(ans); while(T > eps){ Point next; double nE = - 1e10; //-inf for (int i = 0; i < 20; ++i){ //附近取20个点 Point tmp; double angle = rand1()*2*PI; tmp.x = ans.x + cos(angle)*T; tmp.y = ans.y + sin(angle)*T; //ans为中心的T半价圆内新点 double tE = getVal(tmp); if(tE > nE){ nE = tE; next = tmp; } } double dE = nE - E; if(dE > 0 || exp(-1.0/T) > rand1()){ //公式本身是可以改变的,只要符合T越小接受的几率越小就行 // if(dE > 0 || exp(dE/T) > rand1()){ E = nE; ans = next; } T *= 0.8; } return -E;}int main(){ // RE srand(time(NULL)); while(cin>>n){ for (int i = 0; i < n; ++i){ p[i].read(); } printf("%.0f\n", solve()); } return 0;}
HDU 3932 Groundhog Build Home
题意:若干个点,求覆盖全部点的圆心和半径,要求半径最小(即原题说:到最远点的距离最小)
这里给两个做法,一个是一个初始点扩散周围若干点的,再挑里面最大(小)当下一个点,另一种是初始一个点集,直接扩散求最大。
法一
/** 一个中心点搜四周40个点(需要足够多),里面挑最大(本题对应是最小)后再按模拟退火来 265 MS**/#include <cstdio>#include <cmath>#include <cstring>#include <time.h>#include <iostream>#include <algorithm>using namespace std;#define SpeedUp std::cout.sync_with_stdio(false);const int maxn=1e4+50;const int inf = 0x3f3f3f3f;const double PI = acos(-1.0);struct Point{ double x, y, z; void read() { cin >> x >> y; }} p[maxn];int n;double dis(Point a, Point b) { return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) );}double getVal(Point cur){ double ans = 0.0; for (int i = 0; i < n; ++i){ ans = max(ans, dis(p[i],cur) ); } return ans;}double rand1(){ //[0,1] return rand()%10001/10000.0;}double X,Y;Point ans;double E;void solve(){ ans.x = X/2,ans.y = Y/2; double T = sqrt(X*X+Y*Y)/2; //半径 double eps = 1e-6; E = getVal(ans); while(T > eps){ Point next; double nE = 1e10; for (int i = 0; i < 40; ++i){ Point tmp; double angle = rand1()*2*PI; tmp.x = ans.x + cos(angle)*T; tmp.y = ans.y + sin(angle)*T; double tE = getVal(tmp); if(tE < nE){ nE = tE; next = tmp; } } double dE = nE - E; if(dE < 0 || exp(dE/T) < rand1()){ E = nE; ans = next; } T *= 0.9; }}int main(){ // RE srand(time(NULL)); while(cin>>X>>Y>>n){ for (int i = 0; i < n; ++i){ p[i].read(); } solve(); printf("(%.1f,%.1f).\n%.1f\n", ans.x,ans.y,E); } return 0;}
法二
/** 随机多个初始点搜四周20个点,直接往最大的爬去,不给接受更低的机会 733MS**/#include <cstdio>#include <cmath>#include <cstring>#include <time.h>#include <iostream>#include <algorithm>using namespace std;#define SpeedUp std::cout.sync_with_stdio(false);const int maxn=1e5+50;const double PI = acos(-1.0);struct Point{ double x, y, z; Point(){} Point(double tx,double ty){x=tx;y=ty;} void read() { cin >> x >> y; }} p[maxn];int n;double dis(Point a, Point b) { return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) );}double getVal(Point cur){ double ans = 0.0; for (int i = 0; i < n; ++i){ ans = max(ans, dis(p[i],cur) ); } return ans;}double rand1(){ //[0,1] return rand()%10001/10000.0;}double X,Y;Point randPoint(double X,double Y){ return Point(rand1()*X,rand1()*Y);}Point minAns;double minDis;void solve(){ int initNum = 15; double T = sqrt(X*X+Y*Y)/2; double eps = 1e-4; double dis[30]; Point ans[30]; for (int i = 0; i < initNum; ++i){ ans[i] = randPoint(X,Y); dis[i] = getVal(ans[i]); } while(T > eps){ for (int i = 0; i < initNum; ++i){ for (int j = 0; j < 20; ++j){ //足够多... Point tmp; double angle = rand1()*2*PI; tmp.x = ans[i].x + cos(angle)*T; tmp.y = ans[i].y + sin(angle)*T; if(tmp.x < 0 || tmp.x > X || tmp.y < 0 || tmp.y > Y) continue; double tDis = getVal(tmp); if(tDis < dis[i] ){ // || exp((dis[i]-tDis)/T) > rand1() 写了等WA dis[i] = tDis; ans[i] = tmp; } } } T *= 0.8; } minDis = 1e30; for (int i = 0; i < initNum; ++i){ if(dis[i] < minDis){ minDis = dis[i]; minAns = ans[i]; } }}int main(){ SpeedUp srand(time(NULL)); while(cin>>X>>Y>>n){ for (int i = 0; i < n; ++i){ p[i].read(); } solve(); printf("(%.1f,%.1f).\n%.1f\n", minAns.x,minAns.y,minDis); } return 0;}
POJ 2069 Super Star
题意:若干个三维的点,有个球,求足够覆盖所有点的最小的半径。
球心和半径自己定,看大家说法是:求球心到最远点的距离,然后球心往那边移动,初始球心自定。所以谁跟我说这是退火算法?跟确定算法没啥区别了。
#include <cstdio>#include <cmath>#include <cstring>#include <string>#include <iostream>#include <algorithm>#include <time.h>#include <iomanip>using namespace std;#define MOD 10009#define SpeedUp std::cout.sync_with_stdio(false);const int maxn = 50;const int inf = 0x7FFFFFFF;const double PI = acos(-1.0);int n;double X, Y;struct Point{ double x, y, z; Point() {} Point(double _, double __, double ___): x(_), y(__), z(___) {} void read() { cin >> x >> y >> z; }} p[maxn];double dis(Point a, Point b) { return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) + (a.z - b.z) * (a.z - b.z));}int maxDisIndex(Point cur) { //当前点距离所有点中最远点的下标 int ans = 0; for (int i = 0; i < n; ++i) { if ( dis(cur, p[ans]) < dis(cur, p[i]) ) { ans = i; } } return ans;}double solve(Point cur) { double eps = 1e-10; double T = 100.0; double ans = 1e20; while (T > eps) { int index = maxDisIndex(cur); double r = dis(cur, p[index]); ans = min(r, ans); cur.x += (p[index].x - cur.x ) / r * T; cur.y += (p[index].y - cur.y ) / r * T; cur.z += (p[index].z - cur.z ) / r * T; T = T * 0.98; } return ans;}int main() { // RE SpeedUp while (cin >> n, n) { for (int i = 0; i < n; ++i) { p[i].read(); } Point ans = Point(0, 0, 0); cout << setiosflags(ios::fixed); cout << setprecision(5) << solve(ans) << endl; } return 0;}
- 随机算法-模拟退火
- 随机算法 —— 模拟退火
- 随机神经网络之模拟退火
- 模拟退火算法
- 模拟退火算法
- 模拟退火算法
- 模拟退火,遗传算法
- 模拟退火算法概述
- 模拟退火算法
- 遗传模拟退火算法
- 模拟退火遗传算法
- 模拟退火算法
- 模拟退火算法
- 模拟退火算法
- 解析模拟退火算法
- 模拟退火算法
- 模拟退火算法
- 模拟退火算法
- HTTP
- 专题二 · 1011
- (总结)密码破解之王:Ophcrack彩虹表(Rainbow Tables)原理详解(附:120G彩虹表下载)
- Spring报错
- 搜索—Problem_1020-Prime Ring Problem
- 随机算法-模拟退火
- python之logging模块
- 对随机数“去重”和“排序”(使用set实现)
- dict和set
- PTA 5-1 最大子列和问题 (20分)
- android在本应用中d长截图,截长图
- 图形学博客
- C#连接数据库的四种方法
- makefile定义作用及其执行过程