球形空间产生器 模拟退火 + 爬山

球形空间产生器

luogu传送门
有一个球形空间产生器能够在n维空间中产生一个坚硬的球体。

现在,你被困在了这个n维球体中,你只知道球面上n+1个点的坐标,你需要以最快的速度确定这个n维球体的球心坐标,以便于摧毁这个球形空间产生器。

注意: 数据保证有唯一解。

输入格式

第一行是一个整数n。

接下来的n+1行,每行有n个实数,表示球面上一点的n维坐标。

每一个实数精确到小数点后6位,且其绝对值都不超过20000。

输出格式

有且只有一行,依次给出球心的n维坐标(n个实数),两个实数之间用一个空格隔开。

每个实数精确到小数点后3位。

数据范围

1≤n≤10

输入样例:

2
0.0 0.0
-1.0 1.0
1.0 0.0

输出样例:

0.500 1.500

正解应该是高斯消元

然后是在爬山法的例题里面看到的,感觉可以用模拟退火这种做法(

haha

冲了一下发现不太对头,就硬是过不去,刚开始在想是不是球心是到所有点最近的点,(马上否决

然后变成:到所有点方差最近的点

再然后debug输出方差发现就硬是到不了0,((

去观摩了dl的代码,感觉思路差不多,但是人家的起点和温度初始点选的不太一样
选点是按平均选而不是在区间内随机

改成一样的选点方式还是过不去,嘻嘻
T 0 = 1 e 2 , T E = 1 e − 6 , T_0 = 1e2, T_E = 1e-6, T0=1e2,TE=1e6,
衰减是0.9999
到这里是60分左右,acm不友好(

然后发现:
这里算方差的时候拿距离的平方算,delta会变大,如果不符合条件跳过去的概率会小一点
基本稳A

但还是很玄学((

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

const int maxn = 20;
int n;
double a[maxn][maxn];
double p[maxn], tmp[maxn], an[maxn], s[maxn];

double  ans = 1e8;

double rand(double l, double r) {
    
    
	return (double)rand() / RAND_MAX * (r - l) + l; 
}

double calc() {
    
    
	double res = 0;
	double avg = 0;
	 
	for(int i = 0; i < n + 1; ++ i) {
    
    
		double tmp = 0;
		for(int j = 0; j < n; ++ j) {
    
    
			tmp += (a[i][j] - p[j]) * (a[i][j] - p[j]);
		}
		
		s[i] = tmp;
		avg += tmp; 
	}
	
	avg /= n + 1;
	for(int i = 0; i < n + 1; ++ i) {
    
    
		res += (s[i] - avg) * (s[i] - avg);
	}
	
	res /= n + 1;
	res = sqrt(res); 
	
	if(res < ans) {
    
    
		ans = res;
		for(int i = 0; i < n; ++ i) {
    
    
			an[i] = p[i];
		}
	}
	
	return res;
}

void simulate_anneal() {
    
    
	
	for(double T = 1e2; T >= 1e-6; T = T * 0.99995) {
    
    
		double x = calc();
		
		for(int i = 0; i < n; ++ i) {
    
    
			tmp[i] = p[i];
			p[i] = rand(p[i] - T, p[i] + T);
		}	
			
		double y = calc();
		double delta = y - x;
		
		if(exp(-delta / T) < rand(0,1)) {
    
    
			for(int i = 0; i < n; ++ i) {
    
    
				p[i] = tmp[i];
			}
		}
		
	} 
	
	
}

int main() {
    
    
	cin >> n;
	for(int i = 0; i < n + 1; ++ i) {
    
    
		for(int j = 0; j < n; ++ j) {
    
    
			cin >> a[i][j];
			p[j] += a[i][j] / (n + 1);
		}
	}
	
	while((double)clock() / CLOCKS_PER_SEC < 0.7) {
    
    
		simulate_anneal();
	}
	
	//cout << ans << endl; 
	for(int i = 0; i < n; ++ i) {
    
    
		cout << fixed << setprecision(3) << an[i] << " ";
	}
	return 0;
} 

不过一直过不去样例(
可能到时候要分治一下,小数据暴力,大数据用模拟退火

晚点再补个爬山

太玄学了
爬山:调参又是很迷惑

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

const int N = 15;

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

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

int main() {
    
    
	
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	cin >> n;
	
	for(int i = 0; i < n + 1; ++i) {
    
    
		for(int j = 0; j < n; ++j) {
    
    
			cin >> a[i][j];
			ans[j] += a[i][j] / (n + 1); 
		}
	}
	
	for(double T = 1e4; T >= 1e-6; T *= 0.99995) {
    
    
		calc();
		for(int j = 0; j < n; ++j) {
    
    
			ans[j] += delta[j] * T;
		}
	}
	
	for(int i = 0; i < n; ++ i) {
    
    
		cout << fixed << setprecision(3) << ans[i] << " ";
	}
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39602052/article/details/113100856