【数学和算法】最小二乘法理论(附c++代码)
插值
:是给出一些点,求出这些点的表达式,表达式一定经过这些点,然后利用表达式就可以得到某点的值。
插值应用场景
:在数据点稀少的时候,也可以用来插值,使得数据点密集。
拟合
:是给出一些点,求出最接近这些点的曲线表达式,表达式不要求穿过这些点,只要求最贴合这些点,这就是拟合。
拟合应用场景
:用来使得这些点的曲线更平滑。
注意多项式插值
与多项式拟合
的区别:
多项式插值
的话不需要用到最小二乘法
,比如三次多项式插值
,你只需要四个点
,四个方程,组成由4个多项式系数作为未知数的四元一次方程组
,就可以求出三次多项式
的四个系数;多项式拟合
,例如三次多项式拟合
,可能会给你几十个几百个上千个点,然后使用最小二乘法
,求出最贴合这些点的曲线来拟合这些点。
看了上面的区别,应该很明了了,如果使用多项式插值
,每增加一个点,多项式次数就会增加,就需要重新计算,而且次数也并非越大越好,会有龙格现象
,在实际工程中,这显然不合理,所以多项式插值一般只是理论中一带而过。实际工程中我们通常使用多项式拟合
。当数据点本身很多的话,多项式插值则次数很高,龙格现象会造成不准确插值。当然这一点可以通过分段低次插值
克服,而不会直接用到高次数的多项式插值。
下面只介绍拟合
中最常用的最小二乘法
,理论部分直接参考知乎大佬的博客: 最小二乘法
1.最小二乘法 理论部分
知乎大佬的博客中讲到了最小二乘法推导,以及选择算术平均数
、一次多项式曲线
、二次多项式曲线
等进行拟合的效果。
- 如果使用
算术平均数
来拟合,得到的拟合曲线的所有点的y
值都相等,即平行于x轴的直线(斜率为0); - 如果使用
一次多项式
来拟合,得到的拟合曲线的所有点的y
值不相等,即一条斜率不为0的直线; - 如果使用
二次多项式
来拟合,得到的拟合曲线是一个二次函数
的曲线; - 如果使用
n次多项式
来拟合,得到的拟合曲线是一个n次函数
的曲线;
也就是说,给你一系列可能是曲线的点,你可以选择不同的函数来拟合这些点,得到拟合曲线函数。
注意看,上面两个偏导=0
的方程组求解,是把所有样本代入进去进行求和:并不是一个样本代入一个方程,而是所有样本一起代入。
上面就是使用一次多项式
曲线,注意,不是二次多项式,因为最小二乘法
就是求距离的平方和最小值,所以接了个平方。
只需要记住,最小二乘法是用来求多项式的系数,所以求导是分别求平方和对各个系数的偏导,令偏导为0,就得到了方程组,把各个点的(xi,yi)一起代入方程组进行求解,就得到了多项式的系数
。不过这个方程组可以使用矩阵的形式A*X=B
来求解,利用opencv中的cv::solve
函数可以求解。cv::solve
函数可参考这篇博客:https://blog.csdn.net/nienelong3319/article/details/80855077
下面的截图是推导:
其中公式里面的i=0
有误,都改为i=1
,因为点是从(x1,y1)
开始的。W3
改为Wm
代码部分
2.1 多项式拟合曲线C++代码:
// 用最小二乘法根据点集拟合多项式系数
// order是设置的多项式次数
// is_x_axis是根据x轴的方向。因为像素坐标系的x,y和车身坐标系的x,y方向不一样
// coeff是求出的系数向量(order+1行,1列)
bool PolyCurveFit(const std::vector<Point2D<float>> &point_set, const int &order,const bool &is_x_axis, cv::Mat *coeff) {if (coeff == nullptr) {SERROR << "The pointer of coefficient matrix is null!";return false;}const int n = static_cast<int>(point_set.size());if (n <= order) {SERROR << "The number of points should be larger than the order.""The number of points = "<< n;return false;}cv::Mat A = cv::Mat::zeros(order + 1, order + 1, CV_64FC1);cv::Mat B = cv::Mat::zeros(order + 1, 1, CV_64FC1);// Fill matrix Afor (int i = 0; i < order + 1; ++i) {for (int j = 0; j < order + 1; ++j) {for (int k = 0; k < n;++k) { // 遍历点,把每个点的x或y坐标都进行求i+j次方// 矩阵A的每个元素都是[x的幂],或[y的幂],最大幂为2*orderA.at<double>(i, j) +=std::pow(is_x_axis ? static_cast<double>(point_set.at(k).x): static_cast<double>(point_set.at(k).y),i + j);}}}// Fill matrix Bfor (int i = 0; i < order + 1; ++i) {// 遍历点,把每个点都进行(x坐标求i次方)*y 或 (y坐标求i次方)*xfor (int k = 0; k < n; ++k) {// 矩阵B的每个元素都是[x的幂*y],或[y的幂*x],最大幂为orderB.at<double>(i, 0) +=std::pow(is_x_axis ? static_cast<double>(point_set.at(k).x): static_cast<double>(point_set.at(k).y),i) *(is_x_axis ? static_cast<double>(point_set.at(k).y): static_cast<double>(point_set.at(k).x));}}(*coeff) = cv::Mat::zeros(order + 1, 1, CV_64FC1);// 使用最小二乘法,最后优化问题化简为,求A*X=B, 即 A*coeff=B,// 矩阵方程式有了,求未知系数矩阵X,使用的是LU矩阵分解算法// 这里就是求解order+1元order次方程组,求得就是系数if (!cv::solve(A, B, *coeff, cv::DECOMP_LU)) {SERROR << "Cannot solve polynomial coefficient!";return false;}return true;
}
上面代码是使用opencv的库函数解出Ax=b
中的x,这里的x就是系数coef
。
如果是使用Eigen
矩阵的话,那么代码这样的:
const int N = Order + 1;
Eigen::MatrixXd matA(N, N);
Eigen::VectorXd matB(N);/* 这里省略给矩阵A和向量B赋值 *///使用LU矩阵分解法求出系数矩阵coef
Eigen::VectorXd coef = matA.lu().solve(matB);
2.2 求出一系列点的多项式之后,给出一个点的x值,就可以根据求出的多项式系数,求出该点的y值:
// 模板参数中的N是N次多项式的最高次数,N次多项式有N+1个多项式系数
// std::array<float, N + 1> poly_coeff是通过上面函数求出的多项式系数,可由Mat中转存到array中
// 下面函数就是以 y=a+b*x+c*x^2+d*x^3+...的形式计算x值对应的y值
template <typename T, std::size_t N>
T GetPolyValue(const std::array<float, N + 1> &poly_coeff, T x) {T value = 0;for (int i = 0; i < N+1; ++i) {value += poly_coeff[i] * std::pow(x, i);}return value;
}
2.3 画出x从0~100拟合曲线,x采样间隔为0.3,使用圆圈在图像上画出这些点
- (1) 如果拟合的多项式的点(x,y)本来就是像素坐标系的点,可以直接在图像中画出来:
for (float x = 0; x < 100; x += 0.3) {float y = GetPolyValue<float, 3>(poly_coeff, x);cv::Point point;point.x = x ;point.y = y;cv::circle(img_, point, 6, cv::Scalar(0, 255, 0));
}
- (2) 如果x,y是车身坐标系的点,那么需要先转化为像素坐标系的点,然后再到图像中标出这些点:
for (float x = 0; x < 100; x += 0.3) {float y = GetPolyValue<float, 3>(poly_coeff, x);cv::Point point;//注意,车身坐标系的x,y和像素坐标系的y,x对应。并且像素坐标系原点在左上角,// 即车辆坐标系的x值Xc越大,其对应的像素坐标系Yp值越往上,Yp值就越小// 车辆坐标系的y值Yc向左为正,Yc值越大,其对应的像素坐标系的Xp值越小point.y = ph0 - static_cast<int>x ;point.x = pw0 - static_cast<int>y ;cv::circle(img_, point, 6, cv::Scalar(0, 255, 0));
}
【数学和算法】最小二乘法理论(附c++代码)相关推荐
- a*算法matlab代码_NSGAII多目标优化算法讲解(附MATLAB代码)
小编今天为大家讲解NSGA-II多目标优化算法,提到多目标优化,大家可能第一个就想到NSGA-II算法,今天小编就带领大家解开NSGA-II的神秘面纱. NSGA-II全称是快速非支配排序遗传算法,这 ...
- pagerank数据集_机器学习十大经典算法-PageRank(附实践代码)
Yo, yo, check it out. 保证看完不晕倒... 如果公式让你脑瓜疼,请忽略公式,或者忽略脑瓜. Kagging咖金:推荐系统之关联规则(附实践代码)zhuanlan.zhihu.c ...
- 数据结构-数组-字符串匹配:Knuth-Morris-Pratt算法(详解附完整代码)
字符串匹配 字符串抽象数据类型 字符串模式匹配 简单的字符串匹配 Knuth-Morris-Pratt算法 背景分析 失配函数 定义 实现方法 函数分析 KMP函数 实现方法 函数分析 失配信息的另一 ...
- MATLAB算法实战应用案例精讲-【智能优化算法】蜻蜓算法(DA)(附MATLAB代码实现)
目录 前言 算法原理 数学建模 算法思想 1.分离 2.对齐 3.聚集 4.捕食
- 【数学建模】MATLAB应用实战系列(135)-优化算法非线性规划(附MATLAB代码)
前言 优化算法是指在满足一定条件下,在众多方案中或者参数中最优方案,或者参数值,以使得某个或者多个功能指标达到最优,或使得系统的某些性能指标达到最大值或者最小值. 现实问题中,很多都需要用到优化.可以 ...
- 彻底剖析激光-视觉-IMU-GPS融合SLAM算法:理论推导、代码讲解和实战
应用背景介绍 自主导航是机器人与自动驾驶的核心功能,而SLAM技术是实现自主导航的前提与关键.现有的机器人与自动驾驶车辆往往会安装激光雷达,相机,IMU,GPS等多种模态的传感器,而且已有许多优秀的激 ...
- 线性最小二乘法(附MATLAB代码)
本文部分转载自优化算法交流地的文字,转载仅作学习使用. 用n次多项式拟合给定数据. 注意:对于非线性曲线,例如指数曲线\(y=a_{1}e^{a_{2}x}\),拟合前需做变量代换,化为对\(a_1, ...
- 【路径规划-TSP问题】基于粒子群结合蚁群算法求解旅行商问题附matlab代码
1 内容介绍 一种基于粒子群优化的蚁群算法求解TSP问题的方法.该方法在求解TSP问题时,利用粒子群优化的思想,对蚁群算法的参数取值进行优化并选择.在粒子群算法中,将蚁群算法的5个参数(q,α,β,ρ ...
- matlab基础入门之教你如何实现最小二乘法(附MATLAB代码)
今天博主主要是从如何使用MATLAB实现最小二乘法,首先给出今天重点使用的两个函数. p=polyfit(x,y,n):最小二乘法计算拟合多项式系数.x,y为拟合数据向量,要求维度相同,n为拟合多项式 ...
最新文章
- 深入redis内部--事件处理机制
- python实现解释器_Python 解释器初探
- linux的常用操作——共享库
- css 透明度_如何使用CSS实现精美视频片头制作
- 【戴嘉乐】(进阶)基于IPFS和Ngrok构建自维护资源网关
- python静态方法怎么调用_python实例方法、静态方法和类方法
- Atitit 提升开发效率法 fx t35 Atitit 提升开发效率法---开发方法架构简化法.docx 目录 1. 主要几个层次上简化开发	1 1.1. ,开发体系方法使用简单方法	1 1.2.
- android程序卡死无响应,Android程序未响应(ANR)问题
- 如何利用家庭闲置宽带赚钱,甜糖 x86 docker 从零开始搭建
- 小程序 消息推送配置token无效(解决方法)订阅消息
- Python与数据库之学员管理系统
- windows和linux系统云服务器桌面远程连接教程
- Win10样式管理与夜间模式
- 网络神经科学 Network neuroscience
- Python爬虫入门实战2:获取CSDN个人博客文章基础信息
- hdu1232 畅通工程 (并查集)(浙师大OJ1307)
- 微信隐藏功能系列:微信定时提醒,2个步骤,让忙碌中的自己松口气
- TCP是“第一个系统”
- 非线性系统辨识:非线性 ARX 和 Hammerstein-Wiener
- 容器里源码安装apache