求极值算法整理

三分求极值

一元函数求单峰极值

用l,r来求到三等分点lsec,rsec,从而通过比较f(lsec),f(rsec)的大小,来确定极值的范围,进一步缩小区间

具体来说,如果f(lsec)<f(rsec),那么极值点必然是出现在[lsec,r]这一段区间内,同理可知大于的情况

double th_division(double left, double right, double eps)
{ //求解极小值点
    double midl, midr;
    while (right - left > eps)
    {
        midl = (2 * left + right) / 3;
        midr = (left + 2 * right) / 3;
        if (f(midl) <f(midr)) // f是需要求的函数
            left = midl;
        else
            right = midr;
    }
    return midl; //返回近似极值点
}

这个可以优化到每次减少约二分之一的长度,就是将三等分点,改为中间附近某处取点

while (r - l > eps)
{
    mid = (l + r) / 2;
    double fl = f(mid - eps), fr = f(mid + eps);
    if (fl < fr)
        l = mid; // 这里不写成mid - eps,防止死循环;可能会错过极值,但在误差范围以内所以没关系
    else
        r = mid;
}

二元函数求极值

可以用三分套三分,也就是固定一个参数来改变另一个求到极值这样的思想,函数也得满足单峰的条件

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>

using namespace std;
const double eps=1e-8;
double ans;
double f(double x,double y)
{
	return (x-1)*(x-1)+(x+y)*(x+y)+sin(y);
}
double ff(double x)//固定x,三分y 
{
	double l=-4,r=4,mid;
	while(r-l>eps)
	{
		mid=(l+r)/2;
		if(f(x,mid-eps)>f(x,mid+eps))
		l=mid;
		else r=mid;
	 } 
	 return f(x,mid);	
}

int main()
{
	double l=-4,r=4,mid;
	while(r-l>eps)
	{
	     mid=(l+r)/2;//改变x 
		if(ff(mid-eps)>ff(mid+eps))
		{
			l=mid;
		}
		else
		{
			r=mid;
		}
	}
	cout<<ff(mid)<<endl; 
 } 

参考:算法学习笔记(62): 三分法 - 知乎 (zhihu.com)

退火求极值

模拟退火有一定概率能够得到到最优解

算法模板,在主函数内迭代退火,具体退火算法是,用循环来模拟温度的下降,每一次生成一个随机数(可能与res相关),计算生成的和我们要保留迭代的所对应的函数值的大小,求出差值,如果要求最大值,如果生成的大于当前的,必然要迭代,但如果小于,为了避免局部最优解,也有一定几率会迭代

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <ctime>

#define x first
#define y second

using namespace std;

typedef pair<double, double> PDD;
const int N = 110;

int n;
PDD q[N];
double ans = 1e8;

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

double get_dist(PDD a, PDD b)
{
    double dx = a.x - b.x;
    double dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
}

double calc(PDD p)
{
    double res = 0;
    for (int i = 0; i < n; i ++ )
        res += get_dist(p, q[i]);
    ans = min(ans, res);
    return res;
}

void simulate_anneal()
{
    PDD cur(rand(0, 10000), rand(0, 10000));
    for (double t = 1e4; t > 1e-4; t *= 0.9)
    {
        PDD np(rand(cur.x - t, cur.x + t), rand(cur.y - t, cur.y + t));
        double dt = calc(np) - calc(cur);
        if (exp(-dt / t) > rand(0, 1)) cur = np;
    }
}

int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ ) scanf("%lf%lf", &q[i].x, &q[i].y);

    for (int i = 0; i < 100; i ++ ) simulate_anneal();
    printf("%.0lf\n", ans);

    return 0;
}

tip:dt=随机的-当前的

     求最小值:if (exp(-dt / t) > rand(0, 1)) cur = np;

     求最大值:if(exp(delta/t)>(double)rand()/RAND_MAX)

上面这道题目也可以用三分

#include <iostream>
#include <cmath>

using namespace std;

const int N = 110;

#define eps 1e-4

int n;
int x[N],y[N];

double dis(double x1,double y1)
{
    double sum = 0.0;
    for(int i =0;i < n;i ++ )
        sum += sqrt((x[i] - x1)*(x[i] - x1) + (y[i] - y1)*(y[i] - y1));
    return sum;
}

double check(double x1) // 三分y,x和y都是凹函数,结构一样
{
    double l = 0.0,r = 10000.0;
    while(r - l > eps)
    {
        double mid = (l + r) / 2;
        double mmid = (mid + r) / 2;
        if(dis(x1, mid) > dis(x1, mmid)) l = mid;
        else r = mmid; // 这里是r = mmid;
    }
    return dis(x1,l);
}

int main()
{
    cin >> n;
    for(int i = 0;i < n;i ++ ) cin >> x[i] >> y[i];

    double l = 0.0,r = 10000.0;
    while(r - l > eps)  // 三分x
    {
        double mid = (l + r) / 2;
        double mmid = (mid + r) / 2;
        if(check(mid) > check(mmid)) l = mid;
        else r = mmid; // 这里是r = mmid;
    }

    printf("%d",(int)(check(l) + 0.5)); // 浮点数四舍五入

    return 0;
}



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值