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

news2024/11/27 21:52:50

插值

插值的作用是可以将原本比较难计算的函数转换为误差在一定范围内的多项式,比如在单片机中直接计算 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.coloradmin.cn/o/1450712.html

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

相关文章

EF Core 模型优先——根据类对象创建数据表

需要的nuget包&#xff1a; Microsoft.EntityframeworkCore.SqlServer &#xff08;根据自己的数据库类型选择对应的nuget包&#xff09; Microsoft.EntityframeworkCore.Tools Microsoft.VisualStudio.Web.CodeGeneration.Design 说明&#xff1a; &#xff08;1&#xf…

【C++】C++11上

C11上 1.C11简介2.统一的列表初始化2.1 {} 初始化2.2 initializer_list 3.变量类型推导3.1auto3.2decltype3.3nullptr 4.范围for循环5.final与override6.智能指针7. STL中一些变化8.右值引用和移动语义8.1左值引用和右值引用8.2左值引用与右值引用比较8.3右值引用使用场景和意义…

机器学习中的10种非线性降维技术对比总结

降维意味着我们在不丢失太多信息的情况下减少数据集中的特征数量&#xff0c;降维算法属于无监督学习的范畴&#xff0c;用未标记的数据训练算法。 尽管降维方法种类繁多&#xff0c;但它们都可以归为两大类:线性和非线性。 线性方法将数据从高维空间线性投影到低维空间(因此…

排序算法---计数排序

原创不易&#xff0c;转载请注明出处。欢迎点赞收藏~ 计数排序&#xff08;Counting Sort&#xff09;是一种线性时间复杂度的排序算法&#xff0c;其核心思想是通过统计待排序元素的个数来确定元素的相对位置&#xff0c;从而实现排序。 具体的计数排序算法步骤如下&#xff…

【Go语言】Go项目工程管理

GO 项目工程管理&#xff08;Go Modules&#xff09; Go 1.11 版本开始&#xff0c;官方提供了 Go Modules 进行项目管理&#xff0c;Go 1.13开始&#xff0c;Go项目默认使用 Go Modules 进行项目管理。 使用 Go Modules的好处是不再需要依赖 GOPATH&#xff0c;可以在任意位…

[leetcode刷题] 组合

对于递归回溯我觉得是需要多写多分析&#xff0c;递归三部曲&#xff1a;1.返回值和参数&#xff1b;2.终止条件&#xff1b;3.单层递归逻辑 1.通常情况下返回值都是void&#xff0c;参数的话根据实际需求设计&#xff0c;如果设置了全局变量那输入参数就可以少写几个&#xf…

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

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

009集——磁盘详解——电脑数据如何存储在磁盘

很多人也知道数据能够保存是由于设备中有一个叫做「硬盘」的组件存在&#xff0c;但也有很多人不知道硬盘是怎样储存这些数据的。这里给大家讲讲其中的原理。 首先我们要明白的是&#xff0c;计算机中只有0和1&#xff0c;那么我们存入硬盘的数据&#xff0c;实际上也就是一堆0…

猫头虎分享已解决Bug ‍ || Go Error: no Go files in /path/to/directory

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

JVM(5)面试篇

1 什么是JVM&#xff1f; 关联课程内容 基础篇-初识JVM基础篇-Java虚拟机的组成 回答路径 JVM的定义作用功能组成 1、定义&#xff1a; JVM 指的是Java虚拟机&#xff08; Java Virtual Machine &#xff09;。JVM 本质上是一个运行在计算机上的程序&#xff0c;他的职责是…

猫头虎分享已解决Bug ‍ || Python Error: IndentationError: expected an indented block

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

Ollama 可以在 Windows 上运行了

Ollama 可以在 Windows 上运行了 0. 引言1. 下载 Ollma 安装文件2. 安装 Ollama3. 使用 Ollama 0. 引言 Ollama 终于可以在 Windows 上运行了&#xff0c;一直以来都是 “Coming soon”。 运行 Mixtral 8*7B 试了一下&#xff0c;推理速度和推理效果都很不错。 而且模型的下…

【mysql】数据约束

一、数据约束&#xff1a; 什么是约束&#xff1f; 为了确保表中的数据的完整性(准确性、正确性)&#xff0c;为表添加一些限制。是数据库中表设计的一个最基本规则。使用约束可以使数据更加准确&#xff0c;从而减少冗余数据&#xff08;脏数据&#xff09;。 数据库完整性约…

PowerShell搭建vue起始项目

Windows PowerShell搭建vue起始项目 搜索PowerShell,以管理员身份运行。 复制文件夹路径 cd 到这个文件夹位置 命令行创建项目&#xff1a;vue create 项目名 这里写自己的项目名就行&#xff0c;我写的yeb vue create yeb 创建成功后是这样的 有颜色的就是选中的&#xff…

【区块链技术开发语言】在ubuntu18 系统环境下命令操作安装GO语言开发环境

要在Ubuntu 18系统上安装GO语言开发环境,您可以按照以下步骤进行: 打开终端(Ctrl + Alt + T)。 使用以下命令下载GO语言安装包: 或者手动打开链接下载: wget https://golang.org/dl/go1.17.5.linux-amd64.tar.gz确保替换链接中的版本号为最新版本。 解压下载的安装包…

Stable Diffusion系列(五):原理剖析——从文字到图片的神奇魔法(扩散篇)

文章目录 DDPM论文整体原理前向扩散过程反向扩散过程模型训练过程模型生成过程概率分布视角参数模型设置论文结果分析 要想完成SD中从文字到图片的操作&#xff0c;必须要做到两步&#xff0c;第一步是理解文字输入包含的语义&#xff0c;第二步是利用语义引导图片的生成。下面…

计网体系结构

计算机网络的概述 概念 网络&#xff1a;网状类的东西或系统。 计算机网络&#xff1a;是一个将分散的、具有独立性功能的计算机系统&#xff0c;通过通信设备与线路连接起来&#xff0c;由功能完善的软件实现资源共享和信息传递的系统。即计算机网络是互连(通过通信链路互连…

Recovering a Small String-Codeforces

题目链接&#xff1a;Problem - A - Codeforces 解题思路&#xff1a;分三种情况 第一个字母a,最后一个字母z 前两个字母a 最后两个字母z 其他根据大小算出剩下的字母 下面是c代码&#xff1a; #include<iostream> using namespace std; int main() {int t, n;cin…

投稿状态Editor evaluating revision

See: https://nejm.net/talk/2303 https://muchong.com/t-15298340-1-pid-11 https://muchong.com/t-15298340-1-pid-11 https://muchong.com/t-15312191-1

ESP32学习(3)——连接WIFI

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