题意:
- 求平面内到3个点最短距离的点(费马点问题)
- 这个问题引申的有任意边费马点问题(多元韦伯问题)
- 详情参见这个网址https://www.cnblogs.com/cinemaparadiso/p/9008184.html
解法:随机梯度下降+疯狂调参
- 首先要求出我们要进行随机梯度下降的表达式 f ( x ) f(x) f(x) f ( x ) = ∑ i = 1 3 ( x − x i ) 2 + ( y − y i ) 2 f(x)=\sum_{i=1}^3 \sqrt{(x-x_i)^2+(y-y_i)^2} f(x)=i=1∑3(x−xi)2+(y−yi)2
- 其次我们要找到找到每次迭代的x梯度和y梯度
g r a d X = ∑ i = 1 3 x − x i ( x − x i ) 2 + ( y − y i ) 2 gradX=\sum_{i=1}^3\frac{x-x_i}{\sqrt{(x-x_i)^2+(y-y_i)^2}} gradX=i=1∑3(x−xi)2+(y−yi)2x−xi
g r a d Y = ∑ i = 1 3 y − y i ( x − x i ) 2 + ( y − y i ) 2 gradY=\sum_{i=1}^3\frac{y-y_i}{\sqrt{(x-x_i)^2+(y-y_i)^2}} gradY=i=1∑3(x−xi)2+(y−yi)2y−yi - 如何求偏导参见链式法则
f ( g ( x ) ) ′ = f ′ ( g ( x ) ) g ( x ) ′ f(g(x))'=f'(g(x))g(x)' f(g(x))′=f′(g(x))g(x)′ - 于是得到迭代方程,减号的原因是要沿负梯度下降, α \alpha α为学习率 x = x − α ∗ g r a d X x=x-\alpha*gradX x=x−α∗gradX
- y同理
- 但随机梯度下降需要从样本集中选择一个样本进行学习,我们直接选择上一个样本和当前求得梯度相乘,来学习样本集中的数据(其实这里不知道为什么)
x i = x i − 1 − g r a d X i − 1 ∗ d i s t i − 1 ∗ α x_i = x_{i-1}-gradX_{i-1}*dist_{i-1}*\alpha xi=xi−1−gradXi−1∗disti−1∗α - d i s t i − 1 dist_{i-1} disti−1代表上次的迭代点与三个顶点的距离之和
- 此时剩下的问题就是学习率 α \alpha α和终止条件了,这个就全靠对数据的感觉
- 我的思路大概是这样的:
猜想1:学习率需要逐渐下降,这样才能够保证一开始迭代的快,可以脱出局部最优,之后迭代的慢,可以找到结果(正确)
猜想2:学习率会有一个最小值,不能无限小(错误)
不限制最小学习率居然算的更快
猜想3:用前一次迭代和后一次迭代的差来代表收敛情况,合适的前后差值应该在 1 0 − 6 − 1 0 − 8 10^{-6}-10^{-8} 10−6−10−8(错误)
合适的范围是 1 0 − 9 − 1 0 − 10 10^{-9}-10^{-10} 10−9−10−10
学习率更新
double learning_rate(double lr)
{
return 0.99*lr;
}
终止条件
if (abs(pre-GetSum(p,n,cur))<1e-10)//--9也行
break;
AC代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iomanip>
#include<cmath>
#define N 3
using namespace std;
struct Point
{
double x, y;
} p[N];
double dist(Point a, Point b)//求两个顶点的距离
{
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
double GetSum(Point p[], int n, Point t)//求当前迭代点到三角形三个顶点的和
{
double ans = 0;
while (n--)
{
ans += dist(p[n], t);
}
return ans;
}
Point gradient(Point p[], int n, Point t)//求梯度
{
Point grad;
grad.x = 0;
grad.y = 0;
while (n--)
{
grad.x += (t.x - p[n].x) / dist(p[n], t);
grad.y += (t.y - p[n].y) / dist(p[n], t);
}
return grad;
}
double learning_rate(double lr)
{
return 0.99*lr;
}
void SGD(Point p[], int n)
{
Point cur;
//cur.x = p[0].x,cur.y = p[0].y;
cur.x = (p[0].x + p[1].x + p[2].x) / 3;
cur.y = (p[0].y + p[1].y + p[2].y) / 3;
int cnt = 0;double lr = 0.1;
while (true)
{
++cnt;
double pre = GetSum(p, n, cur);
Point grad = gradient(p, n, cur);
cur.x =cur.x - grad.x * lr * pre;
cur.y =cur.y - grad.y * lr * pre;
lr = learning_rate(lr);
double now = GetSum(p, n, cur);
//cout.setf(ios::fixed);
//cout << setprecision(9);
//cout << cur.x << " " << cur.y << endl;
if (abs(pre-GetSum(p,n,cur))<1e-10)
break;
}
//cout<<cnt<<endl;
cout.setf(ios::fixed);
cout << setprecision(9);
cout << cur.x << " " << cur.y << endl;
}
int main()
{
ios::sync_with_stdio(false);
//freopen("E.txt","r",stdin);
int n = 3;
for (int i = 0; i < n; i++)
cin >> p[i].x >> p[i].y;
SGD(p, n);
return 0;
}