非线性优化--NLopt算法使用及C++实例

简介: 非线性优化--NLopt算法使用及C++实例

看这篇之前建议先看这篇,里面讲了非线性优化的原理即相关名词的概念,然后介绍了NLopt的使用方法,这个方法是基于C语言的,本片介绍一个NLopt的实例,用的C++语言。

在实例之前,先介绍下NLopt支持的算法,以及算法使用的注意事项

NLopt 支持的算法

NLopt 包含很多种不同的优化算法。

在头文件里面算法名称的枚举类型为

  enum algorithm {
     GN_DIRECT = 0,
     GN_DIRECT_L,
     GN_DIRECT_L_RAND,
     GN_DIRECT_NOSCAL,
     GN_DIRECT_L_NOSCAL,
     GN_DIRECT_L_RAND_NOSCAL,
     GN_ORIG_DIRECT,
     GN_ORIG_DIRECT_L,
     GD_STOGO,
     GD_STOGO_RAND,
     LD_LBFGS_NOCEDAL,
     LD_LBFGS,
     LN_PRAXIS,
     LD_VAR1,
     LD_VAR2,
     LD_TNEWTON,
     LD_TNEWTON_RESTART,
     LD_TNEWTON_PRECOND,
     LD_TNEWTON_PRECOND_RESTART,
     GN_CRS2_LM,
     GN_MLSL,
     GD_MLSL,
     GN_MLSL_LDS,
     GD_MLSL_LDS,
     LD_MMA,
     LN_COBYLA,
     LN_NEWUOA,
     LN_NEWUOA_BOUND,
     LN_NELDERMEAD,
     LN_SBPLX,
     LN_AUGLAG,
     LD_AUGLAG,
     LN_AUGLAG_EQ,
     LD_AUGLAG_EQ,
     LN_BOBYQA,
     GN_ISRES,
     AUGLAG,
     AUGLAG_EQ,
     G_MLSL,
     G_MLSL_LDS,
     LD_SLSQP,
     LD_CCSAQ,
     GN_ESCH,
     NUM_ALGORITHMS /*不是一种算法 只是算法的数量*/
  };

命名规律:

G/L代表的就是 全局(global)或者局部(local)优化
N/D代表的就是 不需导数 或者 需要导数 的优化

例如 LN_COBYLA 就是用的 COBYLA 算法 ,然后该算法用于局部(L)无需导数(N)的优化

算法选择

那么我们在使用的时候用哪种算法呢?随意选择吗?NO
对于一个确定的数学模型的优化问题,比较好的方式是 通过比较几种可用的算法,再确定选哪种,因为没有最佳算法,不同优化问题对于最佳的解决方式不同。

在比较算法的时候也要注意,不同算法适配的 函数值容差和参数容差 也应该是不同的。

有一种比较好的比较两种算对于一个数学模型的优劣的方式:
先运行一个算法得到最小值,然后运行第二个算法的时候设定就收敛到那个值,然后比较运行的时间

选择全局优化要注意的问题

当前,所有全局优化算法都要求对所有优化参数指定边界约束。
并且一定要注意的是
在这些算法中 只有 ISRES, AGS,ORIG_DIRECT 支持非线性不等式约束
只有 ISRES 支持非线性等式约束

有一种很好的做法就是通过全局算法找到一个最优点,然后以这个最优点作为局部优化的起点,使结果更准确

全局优化算法在找到最优点时 比局部优化算法要 花的精力多很多

Code

数学模型:

求 :max(lnx1+lnx2)    目标函数
约束: p1x1+p2x2=5        等式约束
            x1<=x2                  不等式约束                
            x1>=0;x2>=0         边界约束

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#include <nlopt.hpp> //nlopt的头文件

引用头文件
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//声明一个 是否使用梯度的 全局变量
bool grad_bool=1;

声明一个 是否使用梯度的 全局变量
这样方便在切换 基于梯度的算法和不需要导数的算法的时候可以 改下这个变量就可以
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

double myfunc(const std::vector<double>& x,std::vector<double>&grad,void* f_data )
{
    double result;//声明结果
    if (grad_bool)
    {
        grad[0] = 1 / x[0];
        grad[1] = 1 / x[1];
    }
    result = log(x[0])+log(x[1]);//计算结果
    return result;//返回结果
}

目标函数是 max(lnx1+x2)
定义目标函数 注意函数的参数的形式 是不可以变化的
参数x 是 要优化的参数向量
grad返回为此时最优化参数的梯度 就是数学模型里里 对x求偏导的结果 如果要求偏导那么 lnx1+lnx2 对x1求偏导,及1/x1 也就是 代码的 grad[0] = 1 / x[0]; 这个地方
f_data 是要传入的参数
返回值 为在一个x向量下的目标函数的值
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

double myconstraint(const std::vector<double>& x,std::vector<double>&grad,void* f_data)
{
    double *p = (double *) f_data;
    if (grad_bool)
    {
        grad[0] = p[0];
        grad[1] = p[1];
    }
    double result;//声明结果    
    result = x[0] * p[0] + x[1] * p[1] - 5;//计算结果
    return result;//返回结果
}

定义等式约束函数
数学模型的等式是:p1x1+p2x2=5
p1和p2以外部参数的方式传进来

最好的方式换种写法,这样什么样的参数格式都可以套用

typedef struct{
    double p1,p2;
}my_constraint_data;

声明要传入参数的结构体

double myconstraint(const std::vector<double>& x,std::vector<double>&grad,void* f_data)
{
    my_constraint_data *d = (my_constraint_data*)f_data;
    //double *p = (double *) f_data;
    double p1 = d->p1;
    double p2 = d->p2;
    if (grad_bool)
    {
        grad[0] = p1;
        grad[1] = p2;
    }
    double result;//声明结果    
    result = x[0] *  p1  + x[1] * p2 - 5;//计算结果
    return result;//返回结果
}

然后取参数的时候这样就可以

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

double myinconstraint(const  std::vector<double>& x,std::vector<double>&grad,void* f_data)
{
    if (grad_bool)
    {
        grad[0] = 1;
        grad[1] = -1;
    }

    double result;//声明结果    
    result = x[0]  - x[1] ;//计算结果
    return result;//返回结果
}

不等式的约束函数
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

int main(int argc, char** argv) 
{
    //初始化节点
    ros::init(argc, argv, "lidar_align");

    //声明两个句柄   
    ros::NodeHandle nh;

然后就是main函数的,我是在ros下做的,所以加个节点的初始化
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    //数学模型里的参数
    //double p[2]={1,2};
    my_constraint_data data = {1,2};

    //待优化 求取的参数
    std::vector<double> x{1,1};

声明数学模型里面 等式约束的 参数 my_constraint_data这个结构体在上面定义了
声明 待优化 求取的参数 1,1相当于初始化了
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    //声明一个优化器
    nlopt::opt opter;

声明一个NLopt的优化器

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    /*局部优化算法*/
    //opter = nlopt::opt(nlopt::LD_SLSQP,2);  //有导数
    //opter = nlopt::opt(nlopt::LN_COBYLA,2); //无导数
    /*全局优化算法*/
    opter = nlopt::opt(nlopt::GN_ISRES,2);    //无导数  是可以的   x1 = 1.66667    x2= 1.66667  fmax = 1.02165

给优化器设置使用什么优化算法,并设置因子个数,就是x的个数
优化算法在nlopt.hpp里面有, 可以从里面选择想用的

但是注意并不是所有算法都可以计算出来,例如这个数学模型有 不等式约束和等式约束的 全局优化算法里面只有ISRES可以使用

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    /*优化参数的边界约束*/
    std::vector<double> lb {1,1};//注意参数的个数要对应上
    std::vector<double> ub {10000,10000};//注意参数的个数要对应上
        //设置 参数 边界
    opter.set_lower_bounds(lb);//设置参数下限
    opter.set_upper_bounds(ub);//设置参数上限

优化参数的边界约束

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    /*设置优化精度*/
    double tol = 1e-8;//容差
    opter.set_xtol_rel(tol);
    opter.set_force_stop(tol);

设置优化精度
控制优化什么时候停止
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    /*设置目标函数  其中目标函数是自己定义的 */
    // myfunc 是自己定义的函数名字 ,第二个参数为 可传入数据 没有数据则为NULL
    opter.set_max_objective(myfunc,NULL);

设置目标函数

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    /*设置数学模型的 等式 约束 和 不等式 约束   */
    //myconstraint 是自己定义的函数名字  ,第二个参数为 可传入数据,
    opter.add_equality_constraint(myconstraint,&data,tol);
    opter.add_inequality_constraint(myinconstraint, NULL,tol);

设置数学模型的 等式 约束 和 不等式 约束
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    /* 获取结果 最大值储存在f_max内 对应的向量储存在x内*/
    double f_max = -10000;
    nlopt::result res = opter.optimize(x,f_max);

执行优化计算
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 std::cout<<"x1 = " <<x[0]<<"    x2= "<<x[1]<<"  fmax = "<<f_max<<std::endl;

打印计算结果

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Result

在这里插入图片描述

相关文章
|
存储 监控 算法
基于 C++ 哈希表算法实现局域网监控电脑屏幕的数据加速机制研究
企业网络安全与办公管理需求日益复杂的学术语境下,局域网监控电脑屏幕作为保障信息安全、规范员工操作的重要手段,已然成为网络安全领域的关键研究对象。其作用类似网络空间中的 “电子眼”,实时捕获每台电脑屏幕上的操作动态。然而,面对海量监控数据,实现高效数据存储与快速检索,已成为提升监控系统性能的核心挑战。本文聚焦于 C++ 语言中的哈希表算法,深入探究其如何成为局域网监控电脑屏幕数据处理的 “加速引擎”,并通过详尽的代码示例,展现其强大功能与应用价值。
271 2
|
存储 算法 C++
Windows共享文件:探秘C++实现的B树索引算法奇境
在数字化时代,Windows共享文件的高效管理至关重要。B树算法以其自平衡多路搜索特性,在文件索引与存储优化中表现出色。本文探讨B树在Windows共享文件中的应用,通过C++实现具体代码,展示其构建文件索引、优化数据存储的能力,提升文件检索效率。B树通过减少磁盘I/O操作,确保查询高效,为企业和个人提供流畅的文件共享体验。
|
11月前
|
机器学习/深度学习 传感器 算法
【无人车路径跟踪】基于神经网络的数据驱动迭代学习控制(ILC)算法,用于具有未知模型和重复任务的非线性单输入单输出(SISO)离散时间系统的无人车的路径跟踪(Matlab代码实现)
【无人车路径跟踪】基于神经网络的数据驱动迭代学习控制(ILC)算法,用于具有未知模型和重复任务的非线性单输入单输出(SISO)离散时间系统的无人车的路径跟踪(Matlab代码实现)
644 2
|
12月前
|
传感器 算法 Python
【电机矢量控制算法】基于线性死区补偿的永磁同步电机矢量控制算法仿真
【电机矢量控制算法】基于线性死区补偿的永磁同步电机矢量控制算法仿真
310 3
|
12月前
|
机器学习/深度学习 人工智能 算法
【多智能体编队】基于自适应控制算法非线性输入的多智能体系统编队控制研究(Matlab代码复现)
【多智能体编队】基于自适应控制算法非线性输入的多智能体系统编队控制研究(Matlab代码复现)
281 0
|
12月前
|
机器学习/深度学习 算法 数据格式
MARS算法理论和Python代码实现:用分段回归解决非线性时间序列预测问题
本文将深入探讨MARS算法的核心原理,并详细阐述其在时间序列预测任务中的应用策略与技术实现。
557 0
|
监控 算法 数据处理
基于 C++ 的 KD 树算法在监控局域网屏幕中的理论剖析与工程实践研究
本文探讨了KD树在局域网屏幕监控中的应用,通过C++实现其构建与查询功能,显著提升多维数据处理效率。KD树作为一种二叉空间划分结构,适用于屏幕图像特征匹配、异常画面检测及数据压缩传输优化等场景。相比传统方法,基于KD树的方案检索效率提升2-3个数量级,但高维数据退化和动态更新等问题仍需进一步研究。未来可通过融合其他数据结构、引入深度学习及开发增量式更新算法等方式优化性能。
333 17
|
存储 机器学习/深度学习 算法
基于 C++ 的局域网访问控制列表(ACL)实现及局域网限制上网软件算法研究
本文探讨局域网限制上网软件中访问控制列表(ACL)的应用,分析其通过规则匹配管理网络资源访问的核心机制。基于C++实现ACL算法原型,展示其灵活性与安全性。文中强调ACL在企业与教育场景下的重要作用,并提出性能优化及结合机器学习等未来研究方向。
345 4
|
存储 监控 算法
基于跳表数据结构的企业局域网监控异常连接实时检测 C++ 算法研究
跳表(Skip List)是一种基于概率的数据结构,适用于企业局域网监控中海量连接记录的高效处理。其通过多层索引机制实现快速查找、插入和删除操作,时间复杂度为 $O(\log n)$,优于链表和平衡树。跳表在异常连接识别、黑名单管理和历史记录溯源等场景中表现出色,具备实现简单、支持范围查询等优势,是企业网络监控中动态数据管理的理想选择。
306 0
|
存储 监控 算法
基于 C++ 哈希表算法的局域网如何监控电脑技术解析
当代数字化办公与生活环境中,局域网的广泛应用极大地提升了信息交互的效率与便捷性。然而,出于网络安全管理、资源合理分配以及合规性要求等多方面的考量,对局域网内计算机进行有效监控成为一项至关重要的任务。实现局域网内计算机监控,涉及多种数据结构与算法的运用。本文聚焦于 C++ 编程语言中的哈希表算法,深入探讨其在局域网计算机监控场景中的应用,并通过详尽的代码示例进行阐释。
322 4