搜索 模拟退火

本文介绍了如何使用模拟退火算法解决一个在二维平面上找到一个点,使得该点到给定点集的距离之和最小的问题。通过随机选取初始点,不断迭代并依据概率接受更优解或较差解,最终达到全局最优。文章提供了算法的伪代码,并给出了C++实现示例,强调了算法参数调整的重要性以及防止超时的策略。
摘要由CSDN通过智能技术生成

模拟退火是一种随机化算法(仙术),比赛中的骗分神器

例题
在二维平面上有 n 个点,第 i 个点的坐标为 (xi,yi)。

请你找出一个点,使得该点到这 n 个点的距离之和最小。

该点可以选择在平面中的任意位置,甚至与这 n 个点的位置重合。

输入格式
第一行包含一个整数 n。

接下来 n 行,每行包含两个整数 xi,yi,表示其中一个点的位置坐标。

输出格式
输出最小距离和,答案四舍五入取整。

数据范围
1≤n≤100,
0≤xi,yi≤10000
输入样例:
4
0 0
0 10000
10000 10000
10000 0
输出样例:
28284

对于这道题,可以用模拟退火的算法(仙术)解决
步骤:
1:在整个N * N区域内随机选取一个点X
2:计算出 X 点距离题目中各点的距离F(X)
3:设 T=0.99(衰减系数)
4:在X为中心的(N * T) * (N * T)的区域内再选择一个点Y
5:计算出Y点距离题目中各点的距离F(Y)
6:如果F(Y)的值要小于F(X)的值,那么就令X等于Y
7:如果F(Y)的值要大于F(X)的值,那么就令X 几率性 的等于Y
(一般来说,这个几率常常用e^(-(F(Y)-F(X)/T)<rand(0,1)来实现)
8:回到步骤4,直到区域的范围小于设定的精度(比如1e-4),就停止算法
9:为了增加正确几率,还可以多进行几次模拟退火
伪代码:

void simulate_anneal(){
	随机一个初始点
	for(int T=范围上界;T>精度;T=T*衰减系数){
		在当前点周围随机一个点
		E=f(新点)-f(当前点);
		if(满足更优的条件){
			跳到新点
		}else{
			几率跳到新点
		}
	}
}

注意:模拟退火仅适用于题目中这种函数关系式是连续性的问题(说人话就是可导)
衰减系数和模拟次数都要根据题目给出的范围进行调整,保证不要超时
可以用以下代码来限制模拟次数

while ((double)clock() / CLOCKS_PER_SEC < 0.8) {
        simulate_anneal();
    }

完整代码

#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;

typedef pair<double, double >PDD;
const int N = 110;
int n;
PDD q[N];//
double ans = 1e8;

double rand(double l, double r) { //随机一个浮点数
	return (double)rand() / RAND_MAX * (r - l) + l;
}

double get_dist(PDD a, PDD b) { //求两个点距离
	double dx = a.x - b.x;
	double dy = a.y - b.y;
	return sqrt(dx * dx + dy * dy);
}

double calc(PDD p) { //求全局总距离和
	double res = 0;
	for (int i = 0; i < n; i++) {
		res += get_dist(p, q[i]);
	}
	ans = min(ans, res);
	return res;

}


void simulate_anneal() {
	PDD cur(rand(0, 10000), rand(0, 10000)); //初始点
	for (double t = 1e4; t > 1e-4; t *= 0.99) {
		PDD np(rand(cur.x - t, cur.x + t), rand(cur.y - t, cur.y + t)); //随机一个新点
		auto dt = calc(np) - calc(cur); //新旧点距离差
		if (exp(-dt / t) > rand(0, 1))
			cur = np; //e^(-dt/t)  如果dt>0,exp必定大于1,如果dt<0,exp必定大于0小于1
		

	}
}

int main() {
	cin >> n;
	for (int i = 0; i < n; i++)
		cin >> q[i].x >> q[i].y;
	for (int i = 0; i < 100; i++) { //模拟一百次
		simulate_anneal();
	}
	printf("%.0lf\n",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值