数值优化学习笔记(二)C++实现牛顿法

本篇里面是结合有Armijio condition的实现,所以对步长的选取可能更好一些。
关于最速下降法的实现,在本人的另一篇博客里面也有实现,同时里面也简单介绍了本篇文章里面要实现的函数RosenBrock。
牛顿法是对函数进行二阶泰勒展开,会使用到函数的二阶高维信息,所以更精确一些,但随之而来的就是计算二维Hessian时带来的计算量,求逆矩阵时甚至达到了 O ( n 3 ) O(n_3) O(n3)
以下是函数的梯度计算结果和Hessian计算结果:
在这里插入图片描述
具体的牛顿法实现步骤在之前的博客里面也有介绍,这里我把流程截图了过来(请原谅没有太多时间把这些敲成latex公式)
在这里插入图片描述
下图是输入为(1.5,3)时的输出:
在这里插入图片描述
具体代码实现:

#include<iostream>
#include<algorithm>
#include<fstream>
#include<iomanip>
#include<chrono>
#include<ctime>
#include<sstream>
#include<vector>
#include<cmath>
#include<iterator>
#include<map>
#include<string.h>

#include <complex>
#include <set>
#include <Eigen/StdVector>
#include <Eigen/Dense>
#include <Eigen/Core>
#include <unsupported/Eigen/MatrixFunctions>

using namespace std;
using namespace Eigen;

// 函数值
double RosenBrock(const Eigen::VectorXd x)
{
    double res = 0.0;
    for(int i = 0; i <= int(x.size()/2 - 1); ++i)
    {
        res += 100 * pow(pow(x(2*i),2) - x(2*i+1), 2) + pow(x(2*i) - 1, 2);
    }
    return res;
} 
// 算函数梯度
Eigen::VectorXd gradient(const Eigen::VectorXd x)
{
    Eigen::VectorXd res(x.size());
    for(int i = 0; i <= int(x.size()/2 - 1); ++i)
    {
        res(2*i) = 400 * (pow(x(2*i), 3) - x(2*i)*x(2*i+1)) + 2 * (x(2*i) - 1);
        res(2*i+1) = -200 * (pow(x(2*i), 2) - x(2*i+1));
    }
    return res;
}
// Hessian
Eigen::MatrixXd Hessian(const Eigen::VectorXd x)
{
    Eigen::MatrixXd H(x.size(), x.size());
    for(int i = 0; i < x.size()/2; ++i){
        H(2*i,2*i) = 400 * (3 * pow(x(2*i), 2) - x(2*i+1)) + 2;
        H(2*i,2*i+1) = -400 * x(2*i);
        H(2*i+1,2*i) = -400 * x(2*i);
        H(2*i+1,2*i+1) = 200;
    }
    return H;
}

int main() {
    Eigen::VectorXd x(2);
    x << 1.5,3;
    cout <<"初始函数值:" << RosenBrock(x) <<endl;
    
    // iteration initialization
    double t = 1.0;
    double c = 0.5;
    double sigma = 1e-5;
    Eigen::VectorXd gra = gradient(x);
    Eigen::VectorXd d = gra * (-1);
    cout <<"初始梯度值:";
    for(int i = 0; i < x.size(); ++i) cout << gra(i) << " ";
    cout << endl;

    cout << RosenBrock(x + d) <<endl;
    int cnt = 0;

    while(sqrt(gra.adjoint()*gra) > sigma)
    {
        Eigen::VectorXd tt = gra * c *t;
        double tmp = d.adjoint() * tt;
        while(RosenBrock(x + d*t) > RosenBrock(x) + tmp)
        {
            // update
            t /= 2;
            tt = gra * c *t;
            tmp = d.adjoint() * tt;
        }
        x += d*t;
        Eigen::MatrixXd M = Hessian(x);
        gra = gradient(x);
        d = M.inverse() * gra * (-1);
        ++ cnt;
    }
    cout <<"运行迭代次数: "<<  cnt <<endl;
    cout << "最终函数值:" << RosenBrock(x) <<endl;
    cout << "最终步长 " << t <<endl;
    cout << "最终收敛时对应的x:";
    for(int i = 0; i < x.size(); ++i) cout << x(i) << " ";
    cout << endl;
    return 1;
}



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
牛顿法是求解无约束优化问题中的一种方法,它通过构建和更新目标函数的Hessian矩阵(或其逆)的近似来逼近牛顿法。C语言中有很多可以用来实现牛顿法的库,比如L-BFGS,DFP,BFGS等。 其中,L-BFGS(Limited-memory Broyden-Fletcher-Goldfarb-Shanno)是使用最广泛的拟牛顿法之一。它可以根据目标函数的梯度和Hessian矩阵的逆,利用有限的内存空间进行高效的迭代优化。 在C语言中,可以使用一些开源库来实现L-BFGS算法,如LIBLBFGS。这个库提供了一系列的函数用于设置优化问题的参数、计算梯度和目标函数的值,并进行优化迭代的操作。 具体使用LIBLBFGS库实现牛顿法的步骤如下: 1. 引入LIBLBFGS库头文件,并声明相关的数据结构和函数。 2. 定义目标函数的计算方法,包括函数值和梯度的计算。 3. 初始化优化参数和设置迭代停止的准则。 4. 调用LIBLBFGS库中的函数进行优化迭代操作,直到满足停止准则。 5. 获取优化结果,包括最优的函数值和参数值。 通过以上步骤,我们可以在C语言环境中实现牛顿法,并得到优化问题的解。实现过程中需要注意合理设置参数和停止准则,以及对目标函数的适当处理。LIBLBFGS库提供了丰富的函数和选项,可以根据具体的问题进行适配和调试。 总之,使用C语言实现牛顿法需要引入相应的库,并根据库的接口和函数实现所需的计算过程。可以根据具体的问题选择合适的库,并进行相应的参数设置和优化过程控制,从而得到所需的优化结果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值