爬山算法

爬山算法

一种启发式算法。可以求得局部最优值(极大值)。非常适用于单峰函数。(虽然说单峰函数可以直接三分,但也说不准,爬山算法也可以用于多元函数)

基本原理

对于某一n元函数f,我们先取一点X,对这个点进行偏移D。(X、D均为n维点)

如果f(X+D)比f(X)更优(大或者小,随目标定),那么将X转移到X+D。

往复很多次转移,就可以得到一个局部最优值。

直观理解就是你看到高处就向上爬,那你肯定是一直向上爬的,最终到达一个峰值。

在很多情况下我们会设置一个递降温度函数,让偏移D不断趋于稳定。

例1 球形空间产生器

n+1个点, 求圆心。

此题可以采用爬山算法。

取圆心为O,对当前圆心与所有点的距离求平均值,对于每个点,如果距离圆心小于平均值,则圆心相对要远离这个点一些,大于平均值反之。

从物理上理解来说就是相互排斥,距离太近了排斥一下,距离太远了拉近一点。

代码如下

#include<bits/stdc++.h>
using namespace std;
int n;
double a[20][20];
double ans[20], dis[20], cans[20];
void check()
{
    double sum = 0;
    for (int i = 1; i <= n + 1; ++i)
    {
        dis[i] = 0;
        cans[i] = 0;
        for (int j = 1; j <= n; ++j)
            dis[i] += (a[i][j] - ans[j]) * (a[i][j] - ans[j]);
        dis[i] = sqrt(dis[i]);
        sum += dis[i];
    }
    sum /= (n + 1);
    for (int i = 1; i <= n + 1; ++i)
        for (int j = 1; j <= n; ++j)
            cans[j] += (dis[i] - sum) * (a[i][j] - ans[j]) / sum;
}
int main()
{
    cin >> n;
    for (int i = 1; i <= n + 1; ++i)
        for (int j = 1; j <= n; ++j)
        {
            cin >> a[i][j];
            ans[j] += a[i][j];
        }
    for (int i = 1; i <= n; ++i)
        ans[i] = ans[i] / (n + 1);
    for (double T = 3; T >= 1e-6; T *= 0.99995)
    {
        check();
        for (int i = 1; i <= n; ++i)
            ans[i] += cans[i] * T;
    }
    for (int i = 1; i <= n; ++i)
        printf("%.3f ", ans[i]);
    return 0;
}

此题实际上是经典的高斯消元。

例2 平衡点

n个物体在桌面上通过小洞悬挂,并且绳子连接在一个结点上,问自然下垂时结点所在位置。

该题实际上是求带权费马点。

设置温度,每次模拟合力,将点不断朝合力方向移动。

代码如下。

#include <bits/stdc++.h>
using namespace std;
int n;
int x[1010], y[1010], w[1010];
double ansx, ansy;
int main()
{
    cin >> n;
    for (int i = 1; i <= n; ++i)
        cin >> x[i] >> y[i] >> w[i];
    ansx = 0, ansy = 0;
    for (double T = 10; T >= 1e-6; T *= 0.9995)
    {
        double fx = 0, fy = 0;
        for (int i = 1; i <= n; ++i)
        {
            double dis = sqrt((x[i] - ansx) * (x[i] - ansx) + (y[i] - ansy) * (y[i] - ansy));
            if (dis < 1e-8)
                continue;
            fx += (x[i] - ansx) * w[i] / dis;
            fy += (y[i] - ansy) * w[i] / dis;
        }
        ansx += fx * T, ansy += fy * T;
    }
    printf("%.3f %.3f", ansx, ansy);
    return 0;
}

费马点

费马点就是一个平面上n个点,求平面上一个点O,使得O与n个点距离之和最小,\(min\sum{OI}\)

带权费马点就是加权了,\(min\sum k_iOI\)

正常数学角度来说,是通过几何关系得到费马点。

但对于计算机算法来说,结合物理原理与爬山算法,可以得到。

爬山缺点

多峰函数求最值的时候会陷入局部最优。

因此用随机化的模拟退火,可以有很大概率跳出局部解。。。

下篇再做吧。

posted @ 2022-01-26 13:35  cacu  阅读(530)  评论(0编辑  收藏  举报