插值(一)——多项式插值(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.rhkb.cn/news/257740.html

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

相关文章

【机器学习案例4】为机器学习算法编码分类数据【含源码】

目录 编码分类数据 序数编码 标签编码 一次性编码 目标编码 目标编码的优点 目标编码的缺点 在现实生活中,收集的原始数据很少采用我们可以直接用于机器学习模型的格式,即数值型数据。因此,需要进行一些预处理,以便以正确的格式呈现数据、选择信息丰富的数据或降低其…

VitePress-12-markdown中使用vue的语法

前言 VitePress 中&#xff0c;markdown文档最终都会转换成为 html文件&#xff0c;我们在访问的时候&#xff0c;也是直接访问的 xxx.html 文件。而且&#xff0c;markdown文档会被作为 [vue单文件] 进行处理&#xff0c;因此&#xff0c;我们我们可以在文档中使用 vue 语法&…

C++ new 和 malloc 的区别?

相关系列文章 C new 和 malloc 的区别&#xff1f; C内存分配策略​​​​​​​ 目录 1.引言 2.区别 2.1.申请的内存分配区域 2.2.类型安全和自动大小计算 2.3.构造函数和析构函数的调用 2.4.异常处理 2.5.配对简便性 2.6.new 的重载 2.7.关键字和操作符 3.总结 1.引…

WebSocket原理详解

目录 1.引言 1.1.使用HTTP不断轮询 1.2.长轮询 2.websocket 2.1.概述 2.2.websocket建立过程 2.3.抓包分析 2.4.websocket的消息格式 3.使用场景 4.总结 1.引言 平时我们打开网页&#xff0c;比如购物网站某宝。都是点一下列表商品&#xff0c;跳转一下网页就到了商品…

OpenGL-ES 学习(4)---- OpenGL-ES 坐标体系

坐标体系 我们知道 OpenGL -ES 坐标系中每个顶点的 x&#xff0c;y&#xff0c;z 坐标都应该在 -1.0 到 1.0 之间&#xff0c;超出这个坐标范围的顶点都将不可见。 将一个物体&#xff08;图像&#xff09;渲染到屏幕上&#xff0c;通常经过将物体坐标转换为标准化设备坐标&am…

高德地图上绘制热力图的方法

百度地图和高德地图的JavaScript API都提供了热力图的绘制方法&#xff0c;都是将热力图作为新的图层&#xff0c;叠加到地图上。但是百度地图的经纬度体系与我们的经纬度存在偏差&#xff0c;高德的与我们相符&#xff0c;应当使用高德地图JavaScript API。 因为是JavaScript…

Elasticsearch:特定领域的生成式 AI - 预训练、微调和 RAG

作者&#xff1a;来自 Elastic Steve Dodson 有多种策略可以将特定领域的知识添加到大型语言模型 (LLM) 中&#xff0c;并且作为积极研究领域的一部分&#xff0c;正在研究更多方法。 对特定领域数据集进行预训练和微调等方法使 LLMs 能够推理并生成特定领域语言。 然而&#…

Mysql的安装、使用、优势与教程

一.安装 1.在小皮的设置界面检测3306端口&#xff0c;保障3306端口可用&#xff1b; 2、在小皮的首面界面&#xff0c;启动MySQL&#xff1b; 3、进行环境变量设置&#xff0c;找到MySQL的路径&#xff0c;进行复制&#xff1b; 4、在Windows的搜索栏内&#xff0c;输入“环境…

Linux 驱动开发基础知识——总线设备驱动模型(七)

个人名片&#xff1a; &#x1f981;作者简介&#xff1a;学生 &#x1f42f;个人主页&#xff1a;妄北y &#x1f427;个人QQ&#xff1a;2061314755 &#x1f43b;个人邮箱&#xff1a;2061314755qq.com &#x1f989;个人WeChat&#xff1a;Vir2021GKBS &#x1f43c;本文由…

Linux——网络通信TCP通信常用的接口和tco服务demo

文章目录 TCP通信所需要的套接字socket()bind()listen()acceptconnect() 封装TCP socket TCP通信所需要的套接字 socket() socket()函数主要作用是返回一个描述符&#xff0c;他的作用就是打开一个网络通讯端口&#xff0c;返回的这个描述符其实就可以理解为一个文件描述符&a…

Vue核心基础5:数据监测、收集表单数据、过滤器

1 数据监测 【代码】 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>总结</title><scrip…

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

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

【最详解】如何进行点云的凹凸缺陷检测(opene3D)(完成度80%)

文章目录 前言实现思路想法1想法2想法3 补充实现想法1想法2代码 想法3代码 总结 前言 读前须知&#xff1a; 首先我们得确保你已经完全知晓相关的基本的数学知识&#xff0c;其中包括用最小二乘法拟合曲二次曲面&#xff0c;以及曲面的曲率详细求解。若还是没弄清楚&#xff0…

(17)Hive ——MR任务的map与reduce个数由什么决定?

一、MapTask的数量由什么决定&#xff1f; MapTask的数量由以下参数决定 文件个数文件大小blocksize 一般而言&#xff0c;对于每一个输入的文件会有一个map split&#xff0c;每一个分片会开启一个map任务&#xff0c;很容易导致小文件问题&#xff08;如果不进行小文件合并&…

Vue插槽

Vue插槽 一、插槽-默认插槽1.作用2.需求3.问题4.插槽的基本语法5.代码示例6.总结 二、插槽-后备内容&#xff08;默认值&#xff09;1.问题2.插槽的后备内容3.语法4.效果5.代码示例 三、插槽-具名插槽1.需求2.具名插槽语法3.v-slot的简写4.代码示例5.总结 四、作用域插槽1.插槽…

【AIGC】Stable Diffusion的ControlNet插件

ControlNet 介绍 ControlNet 插件是 Stable Diffusion 中的一个重要组件&#xff0c;用于提供对模型的控制和调整。以下是 ControlNet 插件的主要特点和功能&#xff1a; 模型控制&#xff1a; ControlNet 允许用户对 Stable Diffusion 中的模型进行精细的控制和调整。用户可以…

Linux_文件系统

假定外部存储设备为磁盘&#xff0c;文件如果没有被使用&#xff0c;那么它静静躺在磁盘上&#xff0c;如果它被使用&#xff0c;则文件将被加载进内存中。故此&#xff0c;可以将文件分为内存文件和磁盘文件。 内存文件 磁盘文件 软、硬链接 一.内存文件 1.1 c语言的文件接口 …

【web | CTF】BUUCTF [护网杯 2018] easy_tornado

天命&#xff1a;这题是框架性的漏洞&#xff0c;Python的web服务器框架&#xff0c;应该已经比较古老了 开局先看一下三个文件 简单阅读后会发现&#xff0c;这里存在文件包含漏洞&#xff0c;可以直接读取文件&#xff0c;但是有一个哈希值校验 一开始我以为是扫描文件后得到…

python 自我检测题--part 1

1. Which way among them is used to create an event loop ? Window.mainloop() 2. Suppose we have a set a {10,9,8,7}, and we execute a.remove(14) what will happen ? Key error is raised. The remove() method removes the specified element from the set. Th…

基于FPGA的ECG信号滤波与心率计算verilog实现,包含testbench

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 ECG信号的特点与噪声 4.2 FPGA在ECG信号处理中的应用 4.3 ECG信号滤波原理 4.4 心率计算原理 4.5 FPGA在ECG信号处理中的优势 5.算法完整程序工程 1.算法运行效果图预览 其RTL结构如…