2017 越南国家赛 第E题 European Trip 费马点问题(随机梯度下降+调参)

4 篇文章 0 订阅

题意:

  • 求平面内到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=13(xxi)2+(yyi)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=13(xxi)2+(yyi)2 xxi
    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=13(xxi)2+(yyi)2 yyi
  • 如何求偏导参见链式法则
    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=xi1gradXi1disti1α
  • d i s t i − 1 dist_{i-1} disti1代表上次的迭代点与三个顶点的距离之和
  • 此时剩下的问题就是学习率 α \alpha α和终止条件了,这个就全靠对数据的感觉
  • 我的思路大概是这样的:
猜想1:学习率需要逐渐下降,这样才能够保证一开始迭代的快,可以脱出局部最优,之后迭代的慢,可以找到结果(正确)
猜想2:学习率会有一个最小值,不能无限小(错误)

不限制最小学习率居然算的更快

猜想3:用前一次迭代和后一次迭代的差来代表收敛情况,合适的前后差值应该在 1 0 − 6 − 1 0 − 8 10^{-6}-10^{-8} 106108(错误)

合适的范围是 1 0 − 9 − 1 0 − 10 10^{-9}-10^{-10} 1091010

学习率更新

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值