基于最小二乘法的直线拟合原理及C++实现

news2024/11/21 1:31:55

 在数据分析的时候,需要尽可能地排除噪声干扰,以便分析出数据的本质规律。排除噪声干扰的常用手段之一是数据拟合,以直线、抛物线、多次曲线等为数据模型,对数据进行拟合。

本文我们主要讲基于最小二乘法的直线拟合原理,并在此基础上,介绍结合最小二乘法和RANSAC算法的直线拟合算法。

979330a216fba317cdf969351d35abfb.png

01

基于最小二乘法的直线拟合原理

最小二乘法直线拟合的核心思想是:以所有样本值与其对应模型值的平方差和作为目标函数,当目标函数值取得最小值时,认为模型就是所有样本的拟合

我们知道,当导数为0的时候函数取得极值,所以可通过求目标函数对各参数的偏导数并令偏导数为0来求解各参数。在偏导数为0的情况下求得的各参数则构成了最小二乘法的解。

下面我们来推导一下最小二乘法直线拟合的计算公式。

假设有n个点(xi, yi)(0≤i<n),并假设它们的拟合直线为Y=ax+b,那么对于每个xi,它的拟合值为Yi=axi+b。于是目标函数为:

b5b1679d90bf0cad9ff6d8c84d7b984e.png

我们的目标是:求当f(x)取得最小值时的a、b参数。于是分别求f(x)对于a、b的偏导数:

1611e1895cb49e7d75a537c72ad93598.png

令以上偏导数为0,得到一个二元一次方程组:

640b8f5c8fb072d00747007797cff49c.png

记:

2f2a6d1e4b7d187c1d95a261aa7aecc4.png

于是有:

2cfd76d76a873bf5036b04486e899314.png

解以上方程组得到a、b,就是我们要求的直线拟合参数:

616d3a42ead80fa2a6d3d0ee348e9d1d.png

代码实现:

//y=ax+b
void lineplofit(vector<Point2f>& points_list, int points_num, float* a, float* b)
{
    float sum_x2 = 0.0;
    float sum_y = 0.0;
    float sum_x = 0.0;
    float sum_xy = 0.0;


    int num = points_num;


    int i;
    for (i = 0; i < num; ++i)
    {
        sum_x2 += points_list[i].x * points_list[i].x;
        sum_y += points_list[i].y;
        sum_x += points_list[i].x;
        sum_xy += points_list[i].x * points_list[i].y;
    }


    float tmp = num * sum_x2 - sum_x * sum_x;
    if (abs(tmp) > 0.000001f)
    {
        *a = (num * sum_xy - sum_x * sum_y) / tmp;
        *b = (sum_x2 * sum_y - sum_x * sum_xy) / tmp;
    }
    else
    {
        *a = 0;
        *b = 0;
    }
}

02

基于最小二乘法与RANSAC算法的直线拟合原理

上一章节介绍的最小二乘直线拟合算法中,所有样本点都参与计算。但在实际应用过程中因为噪声的存在,往往有个别样本点距离其它大部分点比较远,通常称这些点为离群点,如果离群点也参与直线拟合运算会导致拟合结果出现较大误差。如下图所示:

55f79d75efc02d44d91434e3fdce448f.png

像以上情况,需要把离群点剔除之后再进行直线拟合,否则会出现较大误差,而RANSAC算法就是这样一种用于剔除离群样本点的经典算法。

介绍RANSAC算法之前,首先讲下内点和外点的含义:与模型距离小于设定阈值的样本点称为内点,反之与模型距离大于等于设定阈值的样本点称为外点(外点也即上文所说的离群点)。举个例子说:假如设定阈值为5,那么若一点到直线的距离为2,因为2小于5所以该点为内点,但若一点到直线的距离为8,因为8大于5所以该点为外点。

下面介绍RANSAC的流程和原理,该算法是一个重复多次循环的过程,在多次循环过程中记录内点数最多的内点集合,最后使用该内点数最多的内点集合来估算拟合模型

假设历史记录的最多内点集合为MaxInline,且其内点数为MaxM。

1. 首先根据需要设定一个目标模型,如果是直线拟合则模型为y=ax+b,如果是二次曲线拟合则模型为y=ax2+bx+c,如果是仿射变换拟合则模型为2*3的仿射变换矩阵......

2. 从所有样本点中随机选择计算模型参数需要的“最少个数”样本点,假设这个“最少个数”为n,不同的模型对应的n值不一样,如果是直线拟合则n=2,如果是二次曲线拟合则n=3,如果是仿射变换拟合则n=3......

3. 使用上一步随机选取的n个样本点计算模型参数,如果是直线拟合则求a、b,如果是二次曲线拟合则求a、b、c,如果是仿射变换拟合则求2*3矩阵的6个参数......

4. 对每个点,计算其到模型的距离,并判断距离如果小于阈值则该点为内点并将其加入内点集合Inline,否则加入外点集合。同时将Inline的点数m与历史记录的最多内点数MaxM进行比较,如果m>MaxM,则执行MaxM=m且MaxInline=Inline。

5. 判断MaxM是否超过总样本数的一定比例(比如80%),或循环次数是否达到设定的最大循环数。如果MaxM未超过总样本数一定比例且循环次数未达到最大循环数,则跳回以上第2步重新开始往下执行,否则停止循环。

6. 判断是否满足MaxM≥n,如果满足则使用MaxInline集合内的点来估算拟合模型,否则认为RANSAC算法失败。

以上步骤中,需要计算样本点到模型的距离,对于不同的模型其距离计算方法是不一样的。如果是直线模型,则直接计算点(x0,y0)到直线y=ax+b的垂直距离即可:

2065dbff45833a6111e104887c188b33.png

判断内点的距离阈值需要设定一个合适的值,才能有较好的剔除离群点效果,因此可以多次尝试不同的阈值,本文根据最小距离MinD到最大距离MaxD的比例差距,把距离阈值的设定转换为0~1的比例值α设定,减小了设定范围,因此可以更容易找到合适的阈值:

2c8e11213fc8755a5cc537ad9c55d298.png

代码实现:

#define RANSAC_K 2


//获取0~n-1范围内的num个随机数
static void GetRansacRandomNum(int n, int num, int p[])
{
    int i = 0, j;


    int r = rand() % n;
    p[0] = r;
    i++;


    while (1)
    {
        int status = 1;
        r = rand() % n;


        for (j = 0; j < i; j++)
        {
            if (p[j] == r)
            {
                status = 0;
                break;
            }
        }


        if (status == 1)
        {
            p[i] = r;
            i++;
        }


        if (i == num)
            break;
    }
}


void RansacPolyfitLine(vector<Point2f> p, int iter_num, float alpha, float* a, float* b)
{
    int r_idx[RANSAC_K];


    vector<Point2f> pick_p;


    srand((unsigned)time(NULL));


    int max_inline_num = 0;


    vector<Point2f> inline_p;
    vector<Point2f> max_inline_p;
    vector<float> d_list;


    int n = p.size();


    for (int i = 0; i < iter_num; i++)   //总共迭代iter_num次
    {
        GetRansacRandomNum(n, RANSAC_K, r_idx);  //生成RANSAC_K个不重复的0~n-1的随机数


        pick_p.clear();
        //随机选择2个点
        for (int j = 0; j < RANSAC_K; j++)
        {       
            pick_p.push_back(p[r_idx[j]]);
        }


        float aa = 0, bb = 0;
        //使用以上随机选择的两个点来计算一条直线
        lineplofit(pick_p, RANSAC_K, &aa, &bb);


        float mind = 99999999.9f;
        float maxd = -99999999.9f;
        d_list.clear();
        //计算所有点到以上计算直线的距离,并记录最大最小距离
        for (int j = 0; j < n; j++)
        {
            float d = abs(aa * p[j].x - p[j].y + bb) / sqrtf(aa * aa + 1.0f);
            d_list.push_back(d);
            mind = MIN(mind, d);
            maxd = MAX(maxd, d);
        }
        //根据0~1的α值和最大最小距离计算阈值
        float threld = mind + (maxd - mind) * alpha;


        inline_p.clear();
        for (int j = 0; j < n; j++)
        {
            //判断如果点距离小于阈值则将该点加入内点集合
            if (d_list[j] < threld)
            {               
                inline_p.push_back(p[j]);                          
            }
        }
        //判断如果以上内点集合的点数大于历史最大内点数,则替换历史最大内点数集合
        if (max_inline_num < inline_p.size())
        {
            max_inline_num = inline_p.size();
            max_inline_p.swap(inline_p);
        }
    }
    //判断如果历史最大内点数大于等于2,则使用历史最大内点数集合来计算直线
    if (max_inline_num >= RANSAC_K)
    {
        lineplofit(max_inline_p, max_inline_p.size(), a, b);
    }
    else  //否则RANSAC算法失败
    {
        *a = 0;
        *b = 0;
    }


}

03

直线拟合结果

当离群点比较少时,最小二乘直线拟合算法与结合最小二乘、RANSAC的直线拟合算法结果差不多,如下图,蓝线为最小二乘直线拟合算法的结果,绿线为结合最小二乘、RANSAC的直线拟合算法的结果,两线基本重合。

9f3b7832b98c25cdc3ee0194af99c289.png

当离群点比较多时,最小二乘直线拟合算法的结果会出现较大偏差(蓝线),结合最小二乘、RANSAC的直线拟合算法则不会(绿线):

6b03e79eb5b2cf9f0bd786d179939ea5.png

04

题外话

个人要把一个公众号做好做大真的很难,之前也是疲惫了吧,再加上工作也比较忙,所以好久不更新公众号了。现在决定继续更新,是因为看到自己的公众号虽然很久没更新但还能陆陆续续帮助到一些人,心里面还是很开心的,这不就是自己的初心吗:提升自己,总结自己,帮助他人

看到这里的你,如果觉得我的文章对你有帮助,麻烦帮帮我做一下宣传和转发,让更多有需要的人看到。你们的肯定是我继续更新地最大动力!

欢迎扫码关注本微信公众号,接下来会不定时更新更加精彩的内容,敬请期待~

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

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

相关文章

企业选择CRM系统的三个好处

跟随着全面放开的脚步&#xff0c;国内经济正在强势复苏&#xff0c;每家企业都在抢订单、找客户&#xff0c;想要提高企业竞争力还是要借助CRM客户管理系统&#xff0c;CRM系统客户信息管理的价值有哪些&#xff1f;从哪些方面助力企业发展。 一、高效率的管理线索 1.便捷录…

如何通过软件定义方案轻松实现卫星通信干扰测试?

GNSS信号本身的脆弱性和卫星信号传输中面临的风险一直被人们所关注着&#xff0c;例如干扰和欺骗&#xff0c;而GNSS接收设备也普遍缺乏对各种干扰的抵抗性与稳定性。根据GPS的创始架构师Brad Parkinson的说法&#xff0c;任何GNSS星座正变得越来越容易受到蓄意信号干扰或高级欺…

测试用例+自动化测试 —— 博客系统

目录 一、设计测试用例 二、自动化测试 1、导入依赖 1、登录页面 3、列表页面 4、详情页面 5、写博客页面 6、完善 三、总结 一、设计测试用例 二、自动化测试 使用selenium4 Junit5单元测试框架&#xff0c;来进行简单的自动化测试。 1、导入依赖 创建Maven项目&am…

Apache Flink 文件上传漏洞 (CVE-2020-17518)

文章目录 一、Apache Flink简介二、漏洞简介三、漏洞复现四、上传jar包getshell 一、Apache Flink简介 Apache Flink 是一个框架和分布式处理引擎&#xff0c;用于在无边界和有边界数据流上进行有状态的计算。Flink 能在所有常见集群环境中运行&#xff0c;并能以内存速度和任…

不限提问次数,免费无限制使用ChatGPT的手把手详细教程,国内最新免费使用ChatGPT教程

目录 一、使用效果 二、注册使用教程 1.打开Edge浏览器扩展 2.选择Edge浏览器外接程序 3.搜索WeTab 4.进入管理扩展 5.启用扩展 6.进入WeTab新标签页 7.打开Chat AI 8.注册 9.使用 ChatGPT是OpenAI推出的人工智能语言模型&#xff0c;能够通过理解和学习人类的语言来…

IC代理商教你如何通过壳盖辨别翻新二手芯片

老师傅会告诉你看经验看的多了&#xff0c;自然就能区分了。可经验从哪里来呢&#xff1f;ic代理商将从壳盖、定位孔和针脚三个方面来讲&#xff0c;干货满满做好笔记。 壳盖指的是芯片印制的一面&#xff0c;上面有芯片的型号和定位孔&#xff0c;全新的壳盖看着是磨砂的&…

你想要的PDF预览新方式,微信小程序绝对不容错过

前言 随着微信小程序的不断发展和变革&#xff0c;越来越多的功能被开发出来&#xff0c;其中预览 PDF 文件功能也已经成为小程序的常见应用之一。今天&#xff0c;我们将针对微信小程序预览 PDF 这一功能&#xff0c;为大家详细解析和介绍。 实现思路 在小程序界面中添加一个…

Mac苹果电脑杀毒软件CleanMyMac X

CleanMyMac X上手完全没难度。CleanMyMac X拥有非常精美的UI设计&#xff0c;左侧是功能菜单&#xff0c;各个功能板块简洁明了&#xff0c;我想对于小白用户来说上手也是没难度的。 具有强大的防御和恶意程序清除功能。CleanMyMacX不仅是一款Mac清洁软件&#xff0c;也是一款专…

c++ 11标准模板(STL) std::set(十)

定义于头文件 <set> template< class Key, class Compare std::less<Key>, class Allocator std::allocator<Key> > class set;(1)namespace pmr { template <class Key, class Compare std::less<Key>> using se…

基于html+css的图展示84

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

NÜWA:多模态预训练模型,大杀四方!(附源代码下载)

关注并星标 从此不迷路 计算机视觉研究院 ​​​ 公众号ID&#xff5c;ComputerVisionGzq 学习群&#xff5c;扫码在主页获取加入方式 论文地址&#xff1a;https://arxiv.org/abs/2111.12417 源代码&#xff1a;https:// github.com/microsoft/NUWA 计算机视觉研究院专栏 作者…

GO开篇:手握Java走进Golang的世界

文章目录 一、Golang简介1、Go的诞生2、Go的官网域名3、Go的发展4、Go的设计思想5、Go的特点6、Go的性能7、Go的吉祥物 二、Go和Java的宏观对比1、编译型语言 or 解释型语言2、微观对比 三、Go应用场景1、开源上的应用 四、总结和后续 一、Golang简介 Go&#xff08;又称 Gola…

基于java+springboot+layui的流浪动物交流信息平台设计实现

基于javaspringbootlayui的流浪动物交流信息平台设计实现 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源码联系方…

ES6对象新增了哪些扩展?

一、属性的简写 ES6中&#xff0c;当对象键名与对应值名相等的时候&#xff0c;可以进行简写 const baz {foo:foo}// 等同于 const baz {foo} 方法也能够进行简写 const o {method() {return "Hello!";} };// 等同于const o {method: function() {return &qu…

时局不利,如何化解职场焦虑?

部分数据来源&#xff1a;ChatGPT 在不景气的经济环境下&#xff0c;大多数求职者都面临极大的压力&#xff0c;而技术人员又是其中之一。他们不仅需要不断学习新技能&#xff0c;还需要面对工作市场的竞争&#xff0c;并努力将自己的技能提升到所需的水平。一旦被拒绝或无法找…

半导体设计使用FTP外发文件存在风险,如何安全有效替代?

近几年&#xff0c;基于我国“科技强国”战略目标的实行&#xff0c;以半导体、人工智能、新能源等为代表的的科技型领域及行业快速发展。在半导体行业&#xff0c;以行业产业链来区分&#xff0c;整个行业包括上游材料和设备支撑、中游芯片设计和制造&#xff0c;以及下游移动…

用ArcGIS绘制研究区地图

科研tips&#xff1a;ArcGIS中国地图构建教程 有同学提问&#xff1a;怎么画论文最常用的研究区地图呢&#xff1f; 论文用图对准确性和美观度有一定要求&#xff0c;而ArcGIS具有强大的地图制作功能&#xff0c;可以利用该软件快速制作研究区地图。 01 地图的导入 &#…

C语言CRC-16 DNP格式校验函数

C语言CRC-16 DNP格式校验函数 CRC-16校验产生2个字节长度的数据校验码&#xff0c;通过计算得到的校验码和获得的校验码比较&#xff0c;用于验证获得的数据的正确性。基本的CRC-16校验算法实现&#xff0c;参考&#xff1a; C语言标准CRC-16校验函数。 不同应用规范通过对输…

基于 SpringBoot实现文档管理编辑器

访问【WRITE-BUG数字空间】_[内附完整源码和文档] 本项目实现功能如下&#xff1a;注册、登录和个人资料修改&#xff1b;文档编辑&#xff1a;Markdown 文档的阅读和编辑、发布&#xff1b;文档管理&#xff1b; 使用 Cookies 保存登录状态&#xff1b;在数据库中使用 MD5 保…

【AUTOSAR】【以太网】SD

目录 一、概述 二、限制与约束 三、功能说明 3.1 需求 3.1.1 通用需求 3.1.2 以太网通信 3.1.3 状态处理 3.1.4 与SoAd的交互 3.1.5 订阅事件组重试处理 3.2 报文格式 3.2.1 Entries Array 3.2.2 Opotion Array 3.2.3 示例 3.3 服务发现条目 3.3.1 服务查找相关…