插值(一)——多项式插值(C++)

插值

插值的作用是可以将原本比较难计算的函数转换为误差在一定范围内的多项式,比如在单片机中直接计算 x 、 log ⁡ 2 x \sqrt{x}、\log_2x x log2x之类的函数是比较麻烦的,但是使用插值的方法就可以将其转换为误差可控的只有乘法和加减法的多项式,从而简化计算。

多项式插值

多项式插值是利用多项式来拟合一系列离散的数据点,从而达到简化计算的目的。本文主要介绍最“暴力”的插值方法。

  1. 设定所需构造的插值多项式:
    D n ( x ) = a 0 + a 1 x + a 2 x 2 + ⋯ + a n x n D_n(x)=a_0+a_1x+a_2x^2+\cdots +a_nx^n Dn(x)=a0+a1x+a2x2++anxn
  2. 由插值条件可得:
    D n ( x i ) = y i , i = 0 , 1 , ⋯ n D_n(x_i)=y_i,i=0,1,\cdots n Dn(xi)=yi,i=0,1,n
  3. 由此可以得到对应的方程组:
    { 1 a 0 + a 1 x 0 + a 2 x 0 2 + ⋯ + a n x 0 n = y 0 1 a 0 + a 1 x 1 + a 2 x 1 2 + ⋯ + a n x 1 n = y 1 ⋯ 1 a 0 + a 1 x n + a 2 x n 2 + ⋯ + a n x n n = y n \begin{cases} 1a_0+a_1x_0+a_2x_0^2+\cdots +a_nx_0^n=y_0\\ 1a_0+a_1x_1+a_2x_1^2+\cdots +a_nx_1^n=y_1\\ \cdots\\ 1a_0+a_1x_n+a_2x_n^2+\cdots +a_nx_n^n=y_n \end{cases} 1a0+a1x0+a2x02++anx0n=y01a0+a1x1+a2x12++anx1n=y11a0+a1xn+a2xn2++anxnn=yn
  4. 用于插值规定了输入的 x i x_i xi要互不相同,因此得到的行列式D具有唯一性,且为范德蒙德行列式。
    D = ∣ 1 x 0 x 0 2 ⋯ x 0 n 1 x 1 x 1 2 ⋯ x 1 n ⋮ ⋮ ⋮ ⋱ ⋮ 1 x n x n 2 ⋯ x n n ∣ = ∏ 0 ≤ j ≤ i ≤ n ( x i − x j ) D=\begin{vmatrix} 1&x_0&x_0^2&\cdots &x_0^n\\ 1&x_1&x_1^2&\cdots &x_1^n\\ \vdots&\vdots&\vdots&\ddots&\vdots\\ 1&x_n&x_n^2&\cdots &x_n^n \end{vmatrix}=\prod_{0\le j\le i \le n}(x_i-x_j) D= 111x0x1xnx02x12xn2x0nx1nxnn =0jin(xixj)
    不难看出这个行列式唯一且不为0。
  5. 只需要解出方程组即可,比如克拉默法则:
    D i = ∣ 1 x 0 ⋯ x 0 i − 1 y 0 x 0 i + 1 ⋯ x 0 n 1 x 1 ⋯ x 1 i − 1 y 1 x 1 i + 1 ⋯ x 1 n ⋮ ⋮ ⋯ ⋮ ⋮ ⋮ ⋯ ⋮ 1 x n ⋯ x n i − 1 y n x n i + 1 ⋯ x n n ∣ D_i=\begin{vmatrix} 1&x_0&\cdots &x_0^{i-1}& y_0 &x_0^{i+1}&\cdots &x_0^n\\ 1&x_1&\cdots &x_1^{i-1}& y_1 &x_1^{i+1}&\cdots &x_1^n\\ \vdots&\vdots&\cdots&\vdots&\vdots&\vdots&\cdots&\vdots\\ 1&x_n&\cdots &x_n^{i-1}& y_n &x_n^{i+1}&\cdots &x_n^n\\ \end{vmatrix} Di= 111x0x1xnx0i1x1i1xni1y0y1ynx0i+1x1i+1xni+1x0nx1nxnn
    然后 a i = D i D a_i=\frac{D_i}{D} ai=DDi即可解出所有的系数

代码

代码里面有部分求行列式值的算法是参考此博客的,使用了里面的使用代数余子式分解的方法求解对应的值,因为本文只是讨论本方法的插值效果,故没有考虑运行速度,仅是为了简化非主要部分的代码才采用代数余子式求行列式的值的方法,如果需要提升运行速度,可以选择该博客里的另一个方法进行替代。

//多项式插值
#include<iostream>
#include<cmath>
//使用代数余子式进行求解
double determinant_value(double **D,int n)
{//递归终点if(n==1){return  D[0][0];}else if(n==2){return D[1][1]*D[0][0]-D[0][1]*D[1][0];}else{double D_value=0;for(int k=0;k<n;k++){double **D_temp=new double *[n-1];int i2=1;//由于是根据第0行第j列展开,所以原本的行列式直接从第一行开始拷贝for(int i1=0;i1<n-1;i1++){D_temp[i1]=new double[n-1];int j2=0;for(int j1=0;j1<n-1;j1++){if(j2==k){j2++;}//去除第j列D_temp[i1][j1]=D[i2][j2++];}i2++;}D_value+=(((k+2)&0x0001)?(-1):1)*D[0][k]*determinant_value(D_temp,n-1);//计算代数余子式与对应项的乘积for(int i=0;i<n-1;i++){delete[] D_temp[i];}delete[] D_temp;}return D_value;}
}
//多项式插值系数计算,输入插值坐标x,y,存储系数Di的数组和插值点数n
void polynomial_interpolation(double *x,double *y,double *D_i,int n)
{double **D=new double*[n];double D_value;double **D_temp=new double*[n];//其实这里求D可以直接利用范德蒙德行列式的性质求解for(int i = 0;i < n;i++){D[i]=new double[n];D_temp[i]=new double[n];for(int j = 0;j < n;j++){if(j==0){D[i][j]=1;}else{D[i][j]=D[i][j-1]*x[i];}}}D_value=determinant_value(D,n);//下面是为了求D_i,每次只需要修改两列数据for (int i = 0; i < n;i++){if(i==0){for (int i1 = 0; i1 < n;i1++){D_temp[i1][0]=y[i1];for(int j1 = 1;j1 < n;j1++){D_temp[i1][j1]=D[i1][j1];}}}else{for(int i2 = 0;i2 < n;i2++){D_temp[i2][i-1]=D[i2][i-1];D_temp[i2][i]=y[i2];}}//求多项式系数D_i[i] = determinant_value(D_temp, n) / D_value;}for (int i = 0; i < n; i++){delete[] D[i];delete[] D_temp[i];}delete[] D;delete[] D_temp;
}
double fx(double x)
{//这里填入被插值函数//如果插值区间包括分母为0的情况需要手动调整return sqrt(x + 3);
}
//利用得到的多项式系数计算函数值
double Dx(double x,int n,double *D_i)
{double x_temp = 1.0;double y_temp = 0;for(int j = 0;j < n;j++){y_temp += D_i[j]*x_temp;x_temp*= x;}return y_temp;
}
int main()
{int n;//插值点数std::cout<<"请输入插值点数:";std::cin>>n;double error[3]={0.0f,0.0f,0.0f};//误差评价double *x = new double [n];double *y = new double [n];double *D_i = new double [n];double a=3, b=10;//定义插值区间//生成插值数据for (int i = 0;i<n;i++){x[i] = a + (b-a)*i/(n-1);y[i] = fx(x[i]);}// std::cout<<"请输入插值点坐标x:"<<std::endl;// for(int i = 0;i < n;i++)// {//     std::cin>>x[i];// }// std::cout<<"请输入插值点坐标y:"<<std::endl;// for(int i = 0;i < n;i++)// {//     std::cin>>y[i];// }polynomial_interpolation(x, y, D_i, n);std::cout<<"插值多项式为:y(x)="<<D_i[0];for(int i = 1;i < n;i++){if(D_i[i]>0){std::cout << "+"<<D_i[i] <<"x^"<<i;}else{std::cout << D_i[i] <<"x^"<<i;}}std::cout << std::endl;//验证误差,抽取区间内的100个点进行误差检测for(int i = 0;i < 100;i++){double x_temp = a + (b-a)*i/(100-1);double y_temp = fx(x_temp);double y_temp2 = Dx(x_temp, n, D_i);if(fabs(y_temp-y_temp2)>error[0]){error[0] = fabs(y_temp-y_temp2);}error[1] += fabs((y_temp-y_temp2)/y_temp);error[2] += ((y_temp-y_temp2))*((y_temp-y_temp2));}//err[0]得到的是在区间内最大的误差//err[1]得到的是平均最大相对误差//err[2]是均方根误差error[1] = error[1]/100;error[2] = sqrt(error[2]/100);for(int i = 0;i < 3;i++){std::cout<<"误差"<<i+1<<":"<<error[i]<<std::endl;}//std::cout<<fx(3.4)<<"  "<<Dx(3.4, n, D_i)<<std::endl;delete[] x;delete[] y;delete[] D_i;return 0;
}

结果与分析

运行上面的代码如下:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
这里的误差1为区间内最大的误差,误差2是平均相对误差,误差3是均方根误差,这里的误差评判标准仅作参考。
不难发现当插值点数为3和5时误差都非常小,可以根据精度需求进行选取,但是当插值点数为10时,误差将会非常大,所以选取合适的插值点数是十分必要的,否则会因为龙格现象,导致插值函数在插值点附近的值波动很大,从而导致误差很大。
这种插值方法是非常不建议使用的,因为随着阶数增长其计算量会非常大,在我的电脑上3和5都是瞬间计算完成,但是到了10就需要几秒了,到了12就算很久都算不出来

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

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

相关文章

MySQL学习记录——팔 函数

文章目录 1、日期函数2、字符串函数3、数学函数4、其它函数 1、日期函数 //获取日期 select current_date(); //获取时间 select current_time(); //获取时间戳, 格式为日期时间 select current_timestamp(); //获取当前时间, 格式为日期时间 select now(); //获取参数的日期部…

Leetcode-1572. 矩阵对角线元素的和

题目&#xff1a; 给你一个正方形矩阵 mat&#xff0c;请你返回矩阵对角线元素的和。 请你返回在矩阵主对角线上的元素和副对角线上且不在主对角线上元素的和。 示例 1&#xff1a; 输入&#xff1a;mat [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;25 解释&#xff1a;对角线…

RK3568笔记十六:Framebuffer实验

若该文为原创文章&#xff0c;转载请注明原文出处。 本意是移植LVGL&#xff0c;但在编译DRM过程中一直编译失败&#xff0c;然后就想Framebuffer是否可以用&#xff0c;所以测试一下。 一、framebuffer介绍 FrameBuffer中文译名为帧缓冲驱动&#xff0c;它是出现在2.2.xx内…

leetcode(二分查找)34.在排序数组中查找元素的第一个和最后一个位置(C++详细解释)DAY11

文章目录 1.题目示例提示 2.解答思路3.实现代码结果 4.总结 1.题目 给你一个按照非递减顺序排列的整数数组 nums&#xff0c;和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target&#xff0c;返回 [-1, -1]。 你必须设计…

SECS/GEM的HSMS通讯?金南瓜方案

High Speed SECS Message Service (HSMS) 是一种基于 TCP/IP 的协议&#xff0c;它使得 SECS 消息通信更加快速。这通常用作设备间通信的接口。 HSMS 状态逻辑变化&#xff08;序列&#xff09;&#xff1a; 1.Not Connected&#xff1a;准备初始化 TCP/IP 连接&#xff0c;但尚…

【C深度解剖】取模与取余

简介&#xff1a;本系列博客为C深度解剖系列内容&#xff0c;以某个点为中心进行相关详细拓展 适宜人群&#xff1a;已大体了解C语法同学 作者留言&#xff1a;本博客相关内容如需转载请注明出处&#xff0c;本人学疏才浅&#xff0c;难免存在些许错误&#xff0c;望留言指正 作…

SpringCloud之Nacos用法笔记

SpringCloud之Nacos注册中心 Nacos注册中心nacos启动服务注册到Nacosnacos服务分级模型NacosRule负载均衡策略根据集群负载均衡加权负载均衡Nacos环境隔离-namespace Nacos与eureka的对比临时实例与非临时实例设置 Nacos配置管理统一配置管理微服务配置拉取配置自动刷新远端配置…

1232.缀点成线(Java)

题目描述&#xff1a; 给定一个数组 coordinates &#xff0c;其中 coordinates[i] [x, y] &#xff0c; [x, y] 表示横坐标为 x、纵坐标为 y 的点。请你来判断&#xff0c;这些点是否在该坐标系中属于同一条直线上。 输入&#xff1a; coordinates [[1,2],[2,3],[3,4],[4,5]…

机器学习:数据集划分笔记

数据集划分是机器学习中非常关键的步骤&#xff0c;能直接影响模型的训练效果和泛化能力。它的主要目的是为了评估模型对新数据的泛化能力&#xff0c;即模型在未见过的数据上能表现良好。 数据集通常被划分为三个部分&#xff1a;训练集&#xff08;Training set&#xff09;、…

问题:实行网络化管理,为此需要做好以下几个方面的工作。() #知识分享#其他#职场发展

问题&#xff1a;实行网络化管理&#xff0c;为此需要做好以下几个方面的工作。() A、建立“公共部门—私人部门—第三部门”的合作网络 B、采用平等协商、双向互动、共同参与的决策方式&#xff0c;参与式决策应当成为网络化管理中的主要决策方式 C、建立“公共部门—私人部…

vue axios 请求后端无法传参问题

vue请求后端无法传参问题 问题描述处理过程总结 问题描述 在学习vue时&#xff0c;使用axios调用后端&#xff0c;发现无法把参数正确传到后端&#xff0c;现象如下&#xff1a; 使用vue发起请求&#xff0c;浏览器上已经有传参&#xff0c;但是后端没接收到对应的用户名密码&…

算法之贪心

1.部分背包问题 代码1&#xff1a; 代码2&#xff1a; 但如果金币不能分割&#xff0c;那贪心就不是最优解&#xff0c;正确的做法是搜索或动态规划。 2.排队接水 3.在规定时间内参加最多的比赛 4.合并果子 使用memset初始化int数组时&#xff0c;第二个参数如果是0&#xff0…

uniapp 开发一个密码管理app

密码管理app 介绍 最近发现自己的账号密码真的是太多了&#xff0c;各种网站&#xff0c;系统&#xff0c;公司内网的&#xff0c;很多站点在登陆的时候都要重新设置密码或者通过短信或者邮箱重新设置密码&#xff0c;真的很麻烦 所以准备开发一个app用来记录这些站好和密码…

软件风险分类整理

软件项目风险分类整理 1.需求分析 2.软件设计 3.编码和单元测试 4.集成和测试 5.验收和维护 6.团队管理 7.成本管理 8.组织管理

事务及在SpringBoot项目中使用的两种方式

1.事务简介 事务&#xff08;transaction&#xff09;是访问并可能操作各种数据项的一个数据库操作序列&#xff0c;这些操作要么全部执行&#xff0c;要么全部不执行&#xff0c;是一个不可分割的工作单位。 事物的四大特性: 原子性&#xff08;Atomicity&#xff09;&#xf…

ubuntu22.04@laptop OpenCV Get Started: 010_blob_detection

ubuntu22.04laptop OpenCV Get Started: 010_blob_detection 1. 源由2. blob应用Demo2.1 C应用Demo2.2 Python应用Demo 3. 重点分析3.1 Threshold3.2 Area3.3 Circularity3.4 Convexity3.5 Inertia Ratio 4. 总结5. 参考资料6. 补充 1. 源由 Blob是图像中的一组连接像素&#…

Java中的String类的常用方法(对于字符串的常用操作)

目录 一、获取指定索引的字符 二、 获取指定字符或者字符串的索引位置 三、判断字符串是否以指定内容开头或结尾 四、替换指定的字符或者是字符串 五、获取字符串的子串 六、将字符串转换为字符数组 七、比较字符串的内容是否相等 八、连接字符串 九、比较两个字符串的大…

ESP32学习(3)——连接WIFI

1.简介 Wi-Fi是基于IEEE 802.11标准的无线网络技术 让联网设备以无线电波的形式&#xff0c;加入采用TCP/IP通信协议的网络. Wi-Fi设备有两种模式&#xff1a; 1.Access Point(AP) 模式&#xff0c;此为无线接入点&#xff0c;家里的光猫就是结合WiFi和internet路由功能的AP。…

【Java程序员面试专栏 分布式中间件】Redis 核心面试指引

关于Redis部分的核心知识进行一网打尽,包括Redis的基本概念,基本架构,工作流程,存储机制等,通过一篇文章串联面试重点,并且帮助加强日常基础知识的理解,全局思维导图如下所示 基础概念 明确redis的特性、应用场景和数据结构 什么是Redis,Redis有哪些应用场景 Redi…

【十九】【C++】 priority_queue简单使用和仿函数

priority_queue文档介绍翻译 优先队列是一种容器适配器&#xff0c;专门设计成其中的第一个元素始终是根据某种严格的弱排序准则最大的元素。 这种上下文类似于堆&#xff0c;其中元素可以在任何时刻插入&#xff0c;而只能检索最大堆元素&#xff08;在优先队列中顶部的元素&a…