最优化理论与自动驾驶(二-补充):求解算法(梯度下降法、牛顿法、高斯牛顿法以及LM法,C++代码)

在之前的章节里面(最优化理论与自动驾驶(二):求解算法)我们展示了最优化理论的基础求解算法,包括高斯-牛顿法(Gauss-Newton Method)、梯度下降法(Gradient Descent Method)、牛顿法(Newton's Method)和勒文贝格-马夸尔特法(Levenberg-Marquardt Method, LM方法)法。在实际工程应用中,我们一般采用C++进行开发,所以本文补充了上述求解方法的C++代码。在实际应用中,我们既可以自己进行简单的求解,也可以采用第三方库进行求解。我们列举了三种方式:1)直接使用c++ vector容器 2)采用eigen库进行迭代计算 3)采用eigen库封装好的函数求解,工程应用中建议使用eigen库进行矩阵操作,因为底层进行了大量的优化,包括SIMD指令集优化、懒惰求值策略等。

C++示例代码如下:

以指数衰减模型y=a\cdot e^{bx} 为例,通过不同方法获得最小二乘拟合参数,其中参数为a和b。

1. 梯度下降法

1.1 使用C++ vector容器

#include <iostream>
#include <vector>
#include <cmath>
#include <limits>// 定义指数衰减模型函数 y = a * exp(b * x)
double model(const std::vector<double>& params, double x) {double a = params[0];double b = params[1];return a * std::exp(b * x);
}// 定义残差函数
std::vector<double> residuals(const std::vector<double>& params, const std::vector<double>& x, const std::vector<double>& y) {std::vector<double> res(x.size());for (size_t i = 0; i < x.size(); ++i) {res[i] = model(params, x[i]) - y[i];}return res;
}// 计算目标函数(即平方和)
double objective_function(const std::vector<double>& params, const std::vector<double>& x, const std::vector<double>& y) {std::vector<double> res = residuals(params, x, y);double sum = 0.0;for (double r : res) {sum += r * r;}return 0.5 * sum;
}// 计算梯度
std::vector<double> compute_gradient(const std::vector<double>& params, const std::vector<double>& x, const std::vector<double>& y) {double a = params[0];double b = params[1];std::vector<double> res = residuals(params, x, y);// 梯度计算double grad_a = 0.0;double grad_b = 0.0;for (size_t i = 0; i < x.size(); ++i) {grad_a += res[i] * std::exp(b * x[i]);             // 对 a 的偏导数grad_b += res[i] * a * x[i] * std::exp(b * x[i]);   // 对 b 的偏导数}return {grad_a, grad_b};
}// 梯度下降法
std::vector<double> gradient_descent(const std::vector<double>& x, const std::vector<double>& y, const std::vector<double>& initial_params, double learning_rate = 0.01, int max_iter = 10000, double tol = 1e-6) {std::vector<double> params = initial_params;for (int i = 0; i < max_iter; ++i) {// 计算梯度std::vector<double> gradient = compute_gradient(params, x, y);// 更新参数std::vector<double> params_new = {params[0] - learning_rate * gradient[0], params[1] - learning_rate * gradient[1]};// 检查收敛条件double diff = std::sqrt(std::pow(params_new[0] - params[0], 2) + std::pow(params_new[1] - params[1], 2));if (diff < tol) {std::cout << "Converged after " << i + 1 << " iterations." << std::endl;return params_new;}params = params_new;}std::cout << "Max iterations exceeded. No solution found." << std::endl;return params;
}int main() {// 示例数据std::vector<double> x_data = {0, 1, 2, 3, 4, 5};std::vector<double> y_data;for (double x : x_data) {y_data.push_back(2 * std::exp(-1 * x));}// 初始参数猜测 (a, b)std::vector<double> initial_guess = {1.0, -1.0};// 执行梯度下降法std::vector<double> solution = gradient_descent(x_data, y_data, initial_guess);// 输出结果std::cout << "Fitted parameters: a = " << solution[0] << ", b = " << solution[1] << std::endl;return 0;
}

1.2 使用eigen库

#include <iostream>
#include <Eigen/Dense>
#include <cmath>using Eigen::VectorXd;// 定义指数衰减模型函数 y = a * exp(b * x)
VectorXd model(const VectorXd& params, const VectorXd& x) {double a = params(0);double b = params(1);return a * (x.array() * b).exp().matrix(); // 使用 Eigen 的数组和矩阵运算
}// 定义残差函数
VectorXd residuals(const VectorXd& params, const VectorXd& x, const VectorXd& y) {return model(params, x) - y;
}// 计算目标函数(即平方和)
double objective_function(const VectorXd& params, const VectorXd& x, const VectorXd& y) {VectorXd res = residuals(params, x, y);return 0.5 * res.squaredNorm(); // 使用 Eigen 的 squaredNorm() 计算平方和
}// 计算梯度
VectorXd compute_gradient(const VectorXd& params, const VectorXd& x, const VectorXd& y) {double a = params(0);double b = params(1);VectorXd res = residuals(params, x, y);// 梯度计算double grad_a = (res.array() * (b * x.array()).exp()).sum();                  // 对 a 的偏导数double grad_b = (res.array() * a * x.array() * (b * x.array()).exp()).sum();   // 对 b 的偏导数VectorXd gradient(2);gradient << grad_a, grad_b;return gradient;
}// 梯度下降法
VectorXd gradient_descent(const VectorXd& x, const VectorXd& y, const VectorXd& initial_params, double learning_rate = 0.01, int max_iter = 10000, double tol = 1e-6) {VectorXd params = initial_params;for (int i = 0; i < max_iter; ++i) {// 计算梯度VectorXd gradient = compute_gradient(params, x, y);// 更新参数VectorXd params_new = params - learning_rate * gradient;// 检查收敛条件if ((params_new - params).norm() < tol) {std::cout << "Converged after " << i + 1 << " iterations." << std::endl;return params_new;}params = params_new;}std::cout << "Max iterations exceeded. No solution found." << std::endl;return params;
}int main() {// 示例数据VectorXd x_data(6);x_data << 0, 1, 2, 3, 4, 5;VectorXd y_data = 2 * (-1 * x_data.array()).exp();// 初始参数猜测 (a, b)VectorXd initial_guess(2);initial_guess << 1.0, -1.0;// 执行梯度下降法VectorXd solution = gradient_descent(x_data, y_data, initial_guess);// 输出结果std::cout << "Fitted parameters: a = " << solution(0) << ", b = " << solution(1) << std::endl;return 0;
}

1.3 使用eigen库QR分解

#include <iostream>
#include <Eigen/Dense>
#include <cmath>using Eigen::VectorXd;
using Eigen::MatrixXd;int main() {// 示例数据VectorXd x_data(6);x_data << 0, 1, 2, 3, 4, 5;VectorXd y_data(6);for (int i = 0; i < 6; ++i) {y_data(i) = 2 * std::exp(-1 * x_data(i));}// 对 y_data 取对数以转换为线性方程 ln(y) = ln(a) + b * xVectorXd log_y_data = y_data.array().log();// 构造线性方程的矩阵形式 A * params = log(y)// A 是 x_data 的增广矩阵 [1, x_data]MatrixXd A(x_data.size(), 2);A.col(0) = VectorXd::Ones(x_data.size()); // 第一列为 1A.col(1) = x_data;                        // 第二列为 x_data// 使用最小二乘法求解参数 [ln(a), b]VectorXd params = A.colPivHouseholderQr().solve(log_y_data);// 提取参数 ln(a) 和 bdouble log_a = params(0);double b = params(1);double a = std::exp(log_a);  // 还原 a// 输出拟合结果std::cout << "Fitted parameters: a = " << a << ", b = " << b << std::endl;return 0;
}

2. 牛顿法

#include <iostream>
#include <Eigen/Dense>
#include <cmath>using Eigen::VectorXd;
using Eigen::MatrixXd;// 定义模型函数 y = a * exp(b * x)
VectorXd model(const VectorXd& params, const VectorXd& x) {double a = params(0);double b = params(1);return a * (x.array() * b).exp().matrix(); // 使用 Eigen 的数组和矩阵运算
}// 定义残差函数
VectorXd residuals(const VectorXd& params, const VectorXd& x, const VectorXd& y) {return model(params, x) - y;
}// 计算梯度和 Hessian 矩阵
std::pair<VectorXd, MatrixXd> gradient_and_hessian(const VectorXd& params, const VectorXd& x, const VectorXd& y) {double a = params(0);double b = params(1);VectorXd res = residuals(params, x, y);// 计算雅可比矩阵 JMatrixXd J(x.size(), 2);J.col(0) = (b * x.array()).exp();            // 对 a 的偏导数J.col(1) = a * x.array() * (b * x.array()).exp();  // 对 b 的偏导数// 计算梯度:g = J.transpose() * resVectorXd gradient = J.transpose() * res;// 计算 Hessian:H = J.transpose() * J + 二阶项(这里省略二阶项,只保留 J.T * J)MatrixXd Hessian = J.transpose() * J;return {gradient, Hessian};
}// 牛顿法求解
VectorXd newton_method(const VectorXd& x, const VectorXd& y, const VectorXd& initial_params, int max_iter = 100, double tol = 1e-6) {VectorXd params = initial_params;for (int i = 0; i < max_iter; ++i) {auto [gradient, Hessian] = gradient_and_hessian(params, x, y);// 检查 Hessian 是否可逆if (Hessian.determinant() == 0) {std::cerr << "Hessian matrix is singular." << std::endl;return VectorXd();}// 更新参数:params_new = params - H.inverse() * gradientVectorXd delta_params = Hessian.inverse() * gradient;VectorXd params_new = params - delta_params;// 检查收敛条件if (delta_params.norm() < tol) {std::cout << "Converged after " << i + 1 << " iterations." << std::endl;return params_new;}params = params_new;}std::cerr << "Max iterations exceeded. No solution found." << std::endl;return params;
}int main() {// 示例数据VectorXd x_data(6);x_data << 0, 1, 2, 3, 4, 5;VectorXd y_data(6);y_data = 2 * (-1 * x_data.array()).exp();// 初始参数猜测 (a, b)VectorXd initial_guess(2);initial_guess << 1.0, -1.0;// 执行牛顿法VectorXd solution = newton_method(x_data, y_data, initial_guess);// 输出结果if (solution.size() > 0) {std::cout << "Fitted parameters: a = " << solution(0) << ", b = " << solution(1) << std::endl;} else {std::cout << "No solution found." << std::endl;}return 0;
}

3. 高斯牛顿法

#include <iostream>
#include <Eigen/Dense>
#include <cmath>using Eigen::VectorXd;
using Eigen::MatrixXd;// 定义模型函数 y = a * exp(b * x)
VectorXd model(const VectorXd& params, const VectorXd& x) {double a = params(0);double b = params(1);return a * (b * x.array()).exp();
}// 定义残差函数
VectorXd residuals(const VectorXd& params, const VectorXd& x, const VectorXd& y) {return model(params, x) - y;
}// 定义雅可比矩阵
MatrixXd jacobian(const VectorXd& params, const VectorXd& x) {double a = params(0);double b = params(1);MatrixXd J(x.size(), 2);J.col(0) = (b * x.array()).exp();                 // 对 a 的偏导数J.col(1) = a * x.array() * (b * x.array()).exp();  // 对 b 的偏导数return J;
}// 高斯牛顿法函数
VectorXd gauss_newton(const VectorXd& x, const VectorXd& y, const VectorXd& initial_params, int max_iter = 1000, double tol = 1e-6) {VectorXd params = initial_params;for (int i = 0; i < max_iter; ++i) {VectorXd res = residuals(params, x, y);MatrixXd J = jacobian(params, x);// 计算更新步长:delta_params = (J^T * J)^(-1) * J^T * resVectorXd delta_params = (J.transpose() * J).ldlt().solve(J.transpose() * res);// 更新参数VectorXd params_new = params - delta_params;// 检查收敛条件if (delta_params.norm() < tol) {std::cout << "Converged after " << i + 1 << " iterations." << std::endl;return params_new;}params = params_new;}std::cout << "Max iterations exceeded. No solution found." << std::endl;return params;
}int main() {// 示例数据VectorXd x_data(6);x_data << 0, 1, 2, 3, 4, 5;VectorXd y_data(6);y_data = 2 * (-1 * x_data.array()).exp();// 初始参数猜测 (a, b)VectorXd initial_guess(2);initial_guess << 1.0, -1.0;// 执行高斯牛顿法VectorXd solution = gauss_newton(x_data, y_data, initial_guess);// 输出结果std::cout << "Fitted parameters: a = " << solution(0) << ", b = " << solution(1) << std::endl;return 0;
}

4. LM法

#include <iostream>
#include <Eigen/Dense>
#include <cmath>using Eigen::VectorXd;
using Eigen::MatrixXd;// 定义模型函数 y = a * exp(b * x)
VectorXd model(const VectorXd& params, const VectorXd& x) {double a = params(0);double b = params(1);return a * (b * x.array()).exp();
}// 定义残差函数
VectorXd residuals(const VectorXd& params, const VectorXd& x, const VectorXd& y) {return model(params, x) - y;
}// 定义雅可比矩阵
MatrixXd jacobian(const VectorXd& params, const VectorXd& x) {double a = params(0);double b = params(1);MatrixXd J(x.size(), 2);J.col(0) = (b * x.array()).exp();                 // 对 a 的偏导数J.col(1) = a * x.array() * (b * x.array()).exp();  // 对 b 的偏导数return J;
}// Levenberg-Marquardt算法
VectorXd levenberg_marquardt(const VectorXd& x, const VectorXd& y, const VectorXd& initial_params, int max_iter = 1000, double tol = 1e-6, double lambda_init = 0.01) {VectorXd params = initial_params;double lamb = lambda_init;for (int i = 0; i < max_iter; ++i) {VectorXd res = residuals(params, x, y);MatrixXd J = jacobian(params, x);// 计算 Hessian 矩阵近似 H = J.T * JMatrixXd H = J.transpose() * J;// 添加 lambda 倍的单位矩阵,以调整步长方向MatrixXd H_lm = H + lamb * MatrixXd::Identity(params.size(), params.size());// 计算梯度VectorXd gradient = J.transpose() * res;// 计算参数更新值VectorXd delta_params = H_lm.ldlt().solve(gradient);// 更新参数VectorXd params_new = params - delta_params;// 计算新的残差VectorXd res_new = residuals(params_new, x, y);// 如果新的残差平方和小于当前残差平方和,则接受新的参数,并减小 lambdaif (res_new.squaredNorm() < res.squaredNorm()) {params = params_new;lamb /= 10;} else {// 否则增加 lambdalamb *= 10;}// 检查收敛条件if (delta_params.norm() < tol) {std::cout << "Converged after " << i + 1 << " iterations." << std::endl;return params;}}std::cout << "Max iterations exceeded. No solution found." << std::endl;return params;
}int main() {// 示例数据VectorXd x_data(6);x_data << 0, 1, 2, 3, 4, 5;VectorXd y_data(6);y_data = 2 * (-1 * x_data.array()).exp();// 初始参数猜测 (a, b)VectorXd initial_guess(2);initial_guess << 1.0, -1.0;// 执行 Levenberg-Marquardt 法VectorXd solution = levenberg_marquardt(x_data, y_data, initial_guess);// 输出结果std::cout << "Fitted parameters: a = " << solution(0) << ", b = " << solution(1) << std::endl;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/149849.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

FileLink跨网文件传输 | 跨越网络边界的利器,文件传输不再受限

在当今数字化时代&#xff0c;企业与个人对文件传输的需求不断增长&#xff0c;尤其是在跨网环境中。传统的文件传输方式常常受到网络带宽、传输速度和安全性的限制&#xff0c;给用户带来了诸多不便。FileLink 的出现&#xff0c;为这一难题提供了完美解决方案&#xff0c;让文…

Python 聊聊有内置函数,又该怎么学习内置函数

前言 python有内置函数的概念&#xff0c;从Python3.x开始&#xff0c;内置函数位于builtins模块&#xff0c;比如我们常用的内置函数len()&#xff0c;其实它是builtins模块下的属性&#xff0c;我们也可以builtins.len&#xff08;&#xff09;去访问&#xff0c;当然因为每个…

鼎曼白茶贡眉:贮留芳香记忆,书写老茶传奇

在茶的世界 每一叶都承载着岁月的印记 每一香都凝聚着时光的韵味 其中 有一种温润如玉、恬淡从容的存在 它便是2017年贡眉 这款经过七年时光沉淀与陈化的白茶 以其独特的韵味与品质 吸引了无数茶客的青睐 今日 让我们一同领略2017年贡眉的魅力 PART 01 FIRST OF ALL …

力扣【118-杨辉三角】【数组-C语言】

题目&#xff1a;力扣-118 杨辉三角&#xff1a;&#xff08;算法思路&#xff09; 1. 每行第一个数和最后一个数都是1 2. 把杨辉三角左端对齐&#xff0c;从第三行开始&#xff0c;非首尾的元素值等于上一行同列的元素与该元素之前的元素之和&#xff0c;即 t [ j ] r e t …

【自动化测试】Appium 生态工具以及Appium Desktop如何安装和使用

引言 Appium 是一个开源的自动化测试框架&#xff0c;用于测试原生、移动 Web 和混合应用程序。它支持 iOS、Android 和 Windows 平台。Appium 生态系统包含多个工具和库&#xff0c;这些工具和库可以与 Appium 一起使用&#xff0c;以提高移动应用的自动化测试效率 文章目录 引…

[翟旭发射器]python-推导式-列表list表达式练习

# 简单的列表生成 numbers00[x for x in range(1,11)] print(numbers00) # 带条件的列表生成 numbers01[x for x in range(1,11) if x%20] print(numbers01) # 带表达式的列表生成 numbers10[x**2 for x in range(1,11)] print(numbers10) # 嵌套循环的列表生成 coordinates[(x…

船舶检测系统源码分享

船舶检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vision …

【Linux基础IO】深入解析Linux基础IO缓冲区机制:提升文件操作效率的关键

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;Linux “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;暂无 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀Linux基础IO &#x1f4d2;1. 什么是缓…

Golang plugin包教程:创建与管理插

Golang plugin包教程&#xff1a;创建与管理插 介绍plugin包什么是plugin包使用场景和优势使用场景优势 plugin包的基本用法如何创建插件编写插件代码编译插件 加载插件使用plugin.Open获取符号&#xff1a;plugin.Lookup 插件实例讲解实例一&#xff1a;简单的Hello插件编写He…

Java语言程序设计基础篇_编程练习题**18.39(拖动树)

目录 题目&#xff1a;**18.39(拖动树) 代码示例 代码逻辑解析 类定义和变量初始化 main 方法 start 方法 drawRecursiveTree 方法 动画演示 题目&#xff1a;**18.39(拖动树) 修改编程练习题18.38, 将树移动到鼠标所拖动到的位置 Java语言程序设计基础篇_编程练习题…

DevOps学习路线图

DevOps 是软件工程领域中的一种文化和实践方法&#xff0c;它将开发 (Dev) 和运维 (Ops) 相结合&#xff0c;从而在应用程序规划、开发、交付和运营中统一人员、流程和技术。 DevOps 支持以前孤立角色&#xff08;如开发、IT 运营、质量工程和安全&#xff09;之间的协调和协作…

静态路由和默认路由(实验)

目录 一、实验设备和环境 1、实验设备 2、实验环境 &#xff08;1&#xff09;实验拓扑图 &#xff08;2&#xff09;实验命令列表 二、实验记录 1、直连路由与路由表查看 步骤1:建立物理连接并运行超级终端。 步骤2:在路由器上查看路由表。 2、静态路由配置 步骤1:配…

花半小时用豆包Marscode 和 Supabase免费部署了一个远程工作的导航站

以下是「 豆包MarsCode 体验官」优秀文章&#xff0c;作者谦哥。 &#x1f680; 项目地址&#xff1a;remotejobs.justidea.cn/ &#x1f680; 项目截图&#xff1a; 数据处理 感谢开源项目&#xff1a;https://github.com/remoteintech/remote-jobs 网站信息获取&#xff1…

数据库学习2

&#x1f31f;欢迎来到 我的博客 —— 探索技术的无限可能&#xff01; &#x1f31f;博客的简介&#xff08;文章目录&#xff09; 目录 查询 1 查询所有列 2 查询指定列 3 模糊查询 4 如何备份sql语句文件 5 如果列中有重复的则可以“去重” 6 将数值的列进行相加后生成…

SLMi350DB-DG—— 实现兼容光耦的单通道隔离驱动卓越之选

SLMi350DB-DG是一款兼容光耦的单通道隔离驱动器&#xff0c;具有4A/7A源电流/灌电流以及3.75kVRMS隔离耐压值&#xff0c;适用于驱动低边侧和高边侧的MOSFET和IGBT。与光耦栅极驱动器相比&#xff0c;SLMi350DB-DG具有高共模瞬态抗扰度(CMTI)、低传播延迟和较小的脉宽失真等关键…

基于Java,SpringBoot和Vue的仓库管理商品管理电商后台管理系统

摘要 基于Java、Spring Boot和Vue的仓库管理系统是一个现代化的库存管理解决方案&#xff0c;旨在提高仓库运营效率和准确性。系统采用Java作为后端开发语言&#xff0c;结合Spring Boot框架简化配置和部署过程&#xff0c;实现业务逻辑和数据处理。前端使用Vue.js构建用户界面…

79、Python之鸭子类型:没有听过鸭子类型?关键在于认知的转变

引言 不同于Java等静态类型的语言&#xff0c;Python基于动态类型系统的设计理念&#xff0c;使得Python在很多应用场景中&#xff0c;显得更急灵活、高效。而在动态类型系统中&#xff0c;有一个很重要的概念&#xff0c;就是“鸭子类型”。鸭子类型的背后&#xff0c;代表的…

软考高级:需求工程- 需求获取方式 AI解读

需求获取是项目管理和产品开发中的关键步骤&#xff0c;关系到项目的成功与否。你提到的几种需求获取方式涵盖了多个维度&#xff0c;以下我将逐一解析它们的用途与优势。 生活化例子 需求获取就像你要准备一场家庭聚会&#xff0c;需要先了解每个家庭成员的喜好。你可以通过…

Nexpose 6.6.270 发布下载,新增功能概览

Nexpose 6.6.270 for Linux & Windows - 漏洞扫描 Rapid7 Vulnerability Management, release Sep 18, 2024 请访问原文链接&#xff1a;https://sysin.org/blog/nexpose-6/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.or…

TDengine 在业务落地与架构改造中的应用实践!

前言 在物联网和大数据时代&#xff0c;时序数据的管理和分析变得至关重要。TDengine&#xff0c;作为一款专为时序数据设计的开源数据库&#xff0c;以其卓越的存储和查询效率&#xff0c;成为众多企业优化数据架构的优选。本文将分享我将TDengine成功应用于实际业务的经验&am…