模拟退火

如图所示。

退火算法,爬山算法都是一种复杂度低,但是能得到近似解的算法。

其中爬山算法其实就是梯度下降,可能会陷入局部最优。

  1. 爬山算法:兔子朝着比现在高的地方跳去。它找到了不远处的最高山峰。但是这座山不一定是珠穆朗玛峰。这就是爬山算法,它不能保证局部最优值就是全局最优值。
  2. 模拟退火:兔子喝醉了。它随机地跳了很长时间。这期间,它可能走向高处,也可能踏入平地。但是,它渐渐清醒了并朝最高方向跳去。这就是模拟退火。

根据热力学规律并结合计算机对离散数据的处理, 我们定义: 如果当前温度为 (T), 当前状态与新状态之间的能量差为 (ΔE), 则发生状态转移的概率为:

[P(Delta E) = e^{dfrac {Delta E} {T}} ]

(Delta E) 为正的时候一定是转移成功的,对于 $Delta E < 0 $ , 则通过计算的概率接受这个新的解

可以写出如下一段伪代码

void mnth(){ //模拟退火...
	for(double T = 初始温度;T > 终止温度;T *= 系数){
		rand_operate();//做一个随机操作
		now = cal(); //计算目前的答案
		if(now < ans) ans = now;//若更优,直接转移
		else if(exp((ans - now) / T) * RAND_MAX < rand()){
            //这里写的是小于号,是不转移
			//不转移,撤销刚刚的随机操作
		}
	}
}

看两道题

P1337 [JSOI2004]平衡点 / 吊打XXX

其实就是选一个点,使得这个点到所有点的距离乘上质量之和要最小

相当于在二维的平面找一个最优解

#include<bits/stdc++.h>
#define N 2000
using namespace std;

struct node
{
	double x, y, w;
}e[N];
int n;
double ansx, ansy;
const double eps = 1e-15;
double f(double x, double y)
{
	double tot = 0;
	for (int i = 1; i <= n; i++){
		double delx = x - e[i].x;
		double dely = y - e[i].y;
		tot += sqrt(delx * delx + dely * dely) * e[i].w;
	}
	return tot;
}
void mnth()
{
	for(double T = 5000;T > 1e-15;T *= 0.995){
		double nowx = ansx + (rand() * 2 - RAND_MAX) * T;
		double nowy = ansy + (rand() * 2 - RAND_MAX) * T;
		double delta = f(nowx, nowy) - f(ansx, ansy);
		if (delta < 0)ansx = nowx, ansy = nowy;
		else if (exp(-delta / T)> rand()*1.0/RAND_MAX)ansx = nowx, ansy = nowy;
	}
}
int main(){
	scanf("%d", &n);
	for (int i = 1; i <= n; i++){
		scanf("%lf%lf%lf", &e[i].x, &e[i].y, &e[i].w);
		ansx += e[i].x; ansy += e[i].y;
	}
	ansx /= (double)n; ansy /= (double)n;
	mnth();
	printf("%.3lf %.3lf
", ansx, ansy);
}

P3878 [TJOI2010]分金币

现在有 (n) 枚金币,它们可能会有不同的价值,第 (i) 枚金币的价值为 (v_i)

现在要把它们分成两部分,要求这两部分金币数目之差不超过 1 ,问这样分成的两部分金币的价值之差最小是多少?

每次随机交换两个元素

当时这里的小于号和大于号弄混了,调了好久

#include<bits/stdc++.h>
using namespace std;

int ans, T, n, A[50];

int get() {
	int sum = 0;
	for (int i = 0; i < n; i++) {
		if (i < n / 2)sum += A[i];
		else sum -= A[i];
	}
	return abs(sum);
}

void mnth() {
	for (double T = 5000; T > 1e-15; T *= 0.945) {
		int x = rand() % n, y = rand() % n;
		swap(A[x], A[y]); int now = get();
		if (now < ans) ans = now;
		else if (exp((ans - now) / T) * RAND_MAX < rand()) swap(A[x], A[y]); // > 是转移, < 是撤销
	}
}

int main() {
	scanf("%d", &T);
	while (T--) {
		scanf("%d", &n);
		for (int i = 0; i < n; i++)scanf("%d", A + i);
		ans = 0x7fffffff; int x = 1000; while (x--)mnth();
		printf("%d
", ans);
	}
}
原文地址:https://www.cnblogs.com/sduwh/p/13900669.html