【备战美赛】模拟退火

算法介绍

模拟退火算法本质上是一种贪心算法。在引出模拟退火算法之前,先来看一种比较好理解的算法——爬山算法。简而言之,爬山算法就算爬到一个相对来说的最高峰(继续爬就下坡了)就停止的算法,这种算法虽然快,但是很容易限于局部最优解而非全局最优解。所以我们需要对爬山算法进行改进,便有了模拟退火算法。

模拟退火算法在爬山算法的基础上增添了一个随机性。如果下一个解优于当前解,那么肯定要更新当前解;如果下一个解不优于当前解,那么有一定的概率接受下一个解,并且这个概率会随着时间推移降低。而这里选取的概率正是运用了物理学中的热力学原理:在温度为T时,出现能量差为dE的概率为P(dE)=exp(dE/kT),在算法中,dE为当前解与新的解的差。因为dE小于0,并且随着T增加exp(dE/kT)在(0,1)区间上单调递减,所以随着时间的推移,接受这个新解的概率会变小。在实际操作中可以用生成随机数的方式来模拟是否会选取这个解。在这里显然要设置三个参数:起始温度、终止温度、温度变化速度。要根据不同的题目进行不同的设置。模拟退火算法在计算机竞赛中也经常会用到。

例题:Haywire

题目链接

分析:通过枚举牛的排序状态来获取最优解,这里用到模拟退火来判断是否进行下一步。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<stdlib.h>
using namespace std;

int n;
int a[15][5],p[15];
int ans;

int cal(){
	int sum=0;
	for(int i=1;i<=n;i++){
		for(int j=0;j<3;j++){
			int x=a[i][j];
			int c=p[x]-p[i];
			sum+=abs(c);
		}
	}
	return sum/2;//每头牛被算了两次 
}

void SA(){
	double T=100000,endT=1,cT=0.99;
	int x,y;//任意交换两头牛的顺序 
	while(T>endT){
		do{
			x=rand()%n+1;
		    y=rand()%n+1;
		}while(x==y);
		int tp=p[x];p[x]=p[y];p[y]=tp;
		int temp=cal();
		if(temp<ans){
			ans=temp;
		} 
		else{
			double px=(ans-temp)/T;
			double py=(double)rand()/RAND_MAX;
			if(px>py){
				tp=p[x];p[x]=p[y];p[y]=tp;
			}
		}
		T*=cT;
	}
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		for(int j=0;j<3;j++)
		    scanf("%d",&a[i][j]);
		p[i]=i;//初始状态 
	}
	ans=cal();
	for(int k=0;k<=1000;k++)
	    SA();
	printf("%d",ans);
	return 0;
}

代码的思路是不难理解的,但是这里存在一个和爬山算法一样的问题,就是我们还是可能陷入局部最优解当中,而找不到全局最优解。并且这里是用随机数去模拟的,所以这个算法应当属于非完美算法。为了弥补非完美算法带来的不稳定性,只能多做几次模拟退火的过程,增大搜索的覆盖面,从而获得最优解。 但是次数增大难免会引起时间复杂度的增加,所以实际操作时需要注意平衡。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fellyhosn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值