爬山法

什么是爬山法?

爬山法是向值增加的方向持续移动到简单循环过程,算法在到达一个“峰顶”时终止,此时相邻状态中没有比该“峰顶”更高的值。爬山法不维护搜索树,当前节点只需要记录当前状态及其目标函数值;爬山法不会前瞻与当前状态不直接相邻的状态的值——“就像健忘的人在大雾中试图登顶珠峰一样”。爬山法从来不会“下山”,只会向值比当前节点好的方向搜索,因而肯定不完备,很容易停留在局部极值上。

怎么实现?

爬山法又称贪婪局部搜索,只是选择相邻状态中最好的一个。尽管贪婪是七宗罪之一,但是贪婪算法往往能够获得很好的效果。当然,爬山法会遇到以下问题:

(1)局部极值

(2)山脊:造成一系列的局部极值

(3)高原:平坦的局部极值区域——解决办法:继续侧向移动
随机爬山法:在上山移动中,随机选择下一步,选择的概率随着上山移动到陡峭程度而变化。

首选爬山法:随机地生成后继节点直到生成一个优于当前节点的后继。

随机重新开始的爬山法:“如果一开始没有成功,那么尝试,继续尝试”算法通过随机生成的初始状态来进行一系列的爬山法搜索,找到目标时停止搜索。该算法以概率1接近于完备:因为算法最终会生成一个目标状态作为初始状态。如果每次爬山搜索成功的概率为p,则需要重新开始搜索的期望次数为 \(1/p\)

例题P4035 [JSOI2008]球形空间产生器

#include<bits/stdc++.h>

#define rint register int
#define int long long
#define endl '\n' 

using namespace std;

const int N = 2e1 + 5;

int n;
double d[N][N],ans[N],dist[N],delta[N];

void calc(){
    double avg=0;
    
    for(rint i=0;i<n+1;i++){
        dist[i]=delta[i]=0;
        for(rint j=0;j<n;j++){
        	dist[i]+=(d[i][j]-ans[j])*(d[i][j]-ans[j]);
		}  
        dist[i]=sqrt(dist[i]);
        avg+=dist[i]/(n+1);
    }
    
    for(rint i=0;i<n+1;i++){
        for(rint j=0;j<n;j++){
        	delta[j]+=(dist[i]-avg)*(d[i][j]-ans[j])/avg;
		}   	
	}
    return ;  
}

signed main(){
    cin>>n;
    for(rint i=0;i<n+1;i++)
        for(rint j=0;j<n;j++){
            cin>>d[i][j];
            ans[j]+=d[i][j]/(n+1);
        }
    for(double T=1e4;T>1e-6;T*=0.99995){
        calc();
        for(rint i=0;i<n;i++){
        	ans[i]+=delta[i]*T;
		}    
    }
    for(rint i=0;i<n;i++){
    	printf("%.3lf ",ans[i]);
	} 
    return 0;
}

posted @ 2022-07-22 17:32  PassName  阅读(545)  评论(0编辑  收藏  举报