【实验性质】
综合性验
【实验目的】
掌握Lagrange插值算法、Newton插值算法;理解Newton插值算法相对于Lagrange插值算法的优点。
【实验内容】
先用C语言自带的系统函数sin x求出 的值,然后分别用Lagrange、Newton方法求出
的值,并与用C语言sin x函数计算出的
作比较。
【理论基础】
- Lagrange插值公式经过n + 1个插值节点(𝑥0, 𝑦0),(𝑥1, 𝑦1),… …,(𝑥𝑛,𝑦𝑛)的n次Lagrange插值多项式为:
【提示:用两个向量分别存放X、Y坐标;设计一个专门求第k个拉格朗日基函数在某点函数值的函数;基函数的个数=点数;累加项的个数=点数】
2.NewTon插值公式经过n1个插值节点(𝑥0, 𝑦0),(𝑥1, 𝑦1),… …,(𝑥𝑛, 𝑦𝑛)的n次Newton插值多项式为:
P𝑛(𝑥) = 𝑓(𝑥0) + 𝑓[𝑥0, 𝑥1](𝑥 − 𝑥0) + ⋯ +f[𝑥0, 𝑥1, … … , 𝑥𝑛](x − 𝑥0)(x − 𝑥1) … … (x − 𝑥𝑛−1)
上式中插商f[𝑥0, 𝑥1, … … , 𝑥𝑛]的计算常用到如下的Newton差商表:
其中差商是一个递推的定义:
由上表可知:每增加一个插值节点,都需从一阶插商算起。但在具体应用时,有两种方案:
2.1基于两个n+1维的向量进行计算当节点数n+1固定时,使用两个维数为n+1的向量x、y来存放插值计算的中间结果。其中x存放插
值节点的X坐标;y开始存放插值节点的Y坐标,计算过程中存放各阶差商值。
计算差商时,一次计算差商表的一列。计算 f x x[ 0, 1]时用到向量y的第2个元素和第1个元素,计算 f x x[ 1, 2]时用到向量y的第3个元素和第2个元素,不必再用到y的第一个元素,由此可把 f x x[ 0, 1]放到 y的第一个元素上,把 f x x[ 1, 2]放到y 的第2 个元素上,…。计算二阶差商时,把二阶差商放到y[1]、
y[2]、…。这样牛顿插值公式中用到的f[x0]、f[x0,x1]、f[x0,x1,x2]、……,依次均存放在Y[1]上。算法实现参考:
NewTon差商表
【特点:用两个向量处理计算,向量 y 存放差商;每次计算的差商都从 Y 的第一个元素开始存放;】
2.2基于向量+矩阵进行计算考虑动态增加插值结点,比如已有x0,x1,再增加x2时,依次计算f[x1,x2],f[x0,x1,x2]。当增
加第n+1个节点时,用x[n+1]存放该点X坐标,用矩阵y[n+1][1]存放点的Y 坐标,用y[n+1][i+1]存放本行的i阶差商(1≤i≤n),有:
Y(n,i)=(Y的本行前1列―Y的前1行前1列)/(X的本行-?)=(Y(n,i-1)-Y(n-1,i-1))/(x[n]-x[n-(i-1)])
【特点:用矩阵存放各阶差商,矩阵的阶数=点的个数】
【实验过程】
1.分别用Lagrange插值和Newton插值求解,要求给出代码与计算结果。
头文件:
#ifndef INTERPOLATIONPOLYNOMIAL_H
#define INTERPOLATIONPOLYNOMIAL_H
#include "colvector.h"
class InterpolationPolynomial
{
public:
InterpolationPolynomial();
//求第K个拉格朗日插值基函数在xt处的值
double LagrangeBaseFunctionValue(ColVector &x,int k,double xt);
//用拉格朗日插值多项式求xt处的值
void LagrangeInterpolationValue(ColVector &x,ColVector &y,double xt,int &flag,double &yt);
//求牛顿差商表,矩阵存放结果,逐行计算
void NewtonDifference_1(ColVector &x,ColVector &y,Matrix &n_d);
//求牛顿差商表,矩阵存放结果,逐列计算
void NewtonDifference_2(ColVector &x,ColVector &y,Matrix &n_d);
//求牛顿差商表,向量存放结果,逐列计算(倒序计算)
void NewtonDifference_3(ColVector &x,ColVector &y);
//用牛顿插值多项式求xt处的值
void NewtonInterpolationValue(ColVector &x,ColVector &y,double xt,int &flag,double &yt);
};
#endif // INTERPOLATIONPOLYNOMIAL_H
主函数:
//实验五
#include <iostream>
#include <windows.h>
#include "colvector.h"
#include "matrix.h"
#include <windows.h>
#include "linearequations.h"
#include "interpolationpolynomial.h"
using namespace std;
int main()
{
SetConsoleOutputCP(CP_UTF8);
cout<<"=============牛顿插值多项式============="<<endl;
double a1[]={0,M_PI/6,M_PI/4,M_PI/3,M_PI/2};
double b1[]={sin(0),sin(M_PI/6),sin(M_PI/4),sin(M_PI/3),sin(M_PI/2)};
ColVector x(5),y(5);
x.initMatrix(a1);
y.initMatrix(b1);
Matrix nd;
double xt=M_PI/5,yt=0;
int flag=1;
InterpolationPolynomial obj;
obj.NewtonDifference_1(x,y,nd);
cout<<nd<<endl;
obj.NewtonDifference_2(x,y,nd);
cout<<nd<<endl;
obj.NewtonInterpolationValue(x,y,xt,flag,yt);
cout<<nd<<endl;
// obj.LagrangeInterpolationValue(x,y,xt,flag,yt);
if(flag==1){
double temp=sin(M_PI/5);
cout<<"sin(pi/5),本程序结果为:"<<yt<<endl;
cout<<x<<endl;
cout<<"sin(pi/5),c++自带程序结果为:"<<temp<<" errror "<<fabs(temp-yt)<<endl;
}else{
cout<<"使用牛顿插值失败"<<endl;
}
return 0;
}
源文件:
#include "interpolationpolynomial.h"
InterpolationPolynomial::InterpolationPolynomial()
{
}
double InterpolationPolynomial::LagrangeBaseFunctionValue(ColVector &x,int k,double xt){
double result=1;
int n=x.getRowSize();
for(int j=1;j<=n;j++){
if(j==k){
continue;
}
result=result*(xt-x[j])/(x[k]-x[j]);
}
return result;
}
//用拉格朗日插值多项式求xt处的值
void InterpolationPolynomial::LagrangeInterpolationValue(ColVector &x,ColVector &y,double xt,int &flag,double &yt){
flag=1;
int n=x.getRowSize();
if(n!=y.getRowSize()){
flag=0;
return;
}
for(int i=1;i<=n;i++){
double temp=x[i];
for(int j=i+1;j<=n;j++){
if(temp==x[j]){
flag=0;
return;
}
}
}
yt=0;
for(int k=1;k<=n;k++){
yt+=LagrangeBaseFunctionValue(x,k,xt)*y[k];
}
}
//求牛顿差商表,矩阵存放结果,逐行计算
void InterpolationPolynomial::NewtonDifference_1(ColVector &x,ColVector &y,Matrix &n_d){
int n=x.getRowSize();
n_d=Matrix(n,n);
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
if(j==1){
n_d(i,j)=y[i];
}
else{
n_d(i,j)=(n_d(i,j-1)-n_d(i-1,j-1))/(x[i]-x[i-(j-1)]);
}
}
}
}
//求牛顿差商表,矩阵存放结果,逐列计算
void InterpolationPolynomial::NewtonDifference_2(ColVector &x,ColVector &y,Matrix &n_d){
int n=x.getRowSize();
n_d=Matrix(n,n);
//处理第一列
for(int i=1;i<=n;i++){
n_d(i,1)=y[i];
}
//从第二列开始
for(int j=2;j<=n;j++){
for(int i=j;i<=n;i++){
n_d(i,j)=(n_d(i,j-1)-n_d(i-1,j-1))/(x[i]-x[i-(j-1)]);
}
}
}
//求牛顿差商表,向量存放结果,逐列计算(倒序计算)
void InterpolationPolynomial::NewtonDifference_3(ColVector &x,ColVector &y){
int n=x.getRowSize();
//y的原始值【第一列】是0阶差商
//cout<<y<<endl;
//计算j阶差商
for(int j=2;j<=n;j++){
for(int i=n;i>=j;i--){
y[i]=(y[i]-y[i-1])/(x[i]-x[i-(j-1)]);
}
}
}
//用牛顿插值多项式求xt处的值
void InterpolationPolynomial::NewtonInterpolationValue(
ColVector &x,ColVector &y,double xt,int &flag,double &yt){
flag=1;
//点数
int n=x.getRowSize();
if(n!=y.getRowSize()){
flag=0;
return;
}
//横坐标互异
for(int i=1;i<=n;i++){
double temp=x[i];
for(int j=i+1;j<=n;j++){
if(temp==x[j]){
flag=0;
return;
}
}
}
//求差商表
Matrix nd(n,n);
NewtonDifference_1(x,y,nd);
//计算
/*double result=nd(1,1),temp=1;
for(int i=2;i<=n;i++){
temp=temp*(xt-x[i-1]);
result=result+nd(i,i)*temp;
}
yt=result;*/
//秦九韶算法
// double result=nd(n,n);
// for(int i=n-1;i>=1;i--){
// result=result*(xt-x[i])+nd(i,i);
// }
// yt=result;
//差商结果放到原来的向量y中
NewtonDifference_3(x,y);
double result=y[n];
for(int i=n-1;i>=1;i--){
result=result*(xt-x[i])+y[i];
}
yt=result;
//向量y【1】存放差商表对角线元素+累加计算
//算第j列数据
// result = y[1];
// for(int j=2;j<=n;j++){
// //先计算新一轮的差商
// for(int i=1;i<=n-(j-1);i++){
// y[i]=(y[i+1]-y[i])/(x[i+j-1]-x[i]);
// }
// temp=temp*(xt-x[j-1]);
// result=result+temp*y[1];
// }
// yt=result;
}
运行结果:
2.用表格比较Lagrange法、Newton法以及C语言自带函数对的求值结果。
Lagrange法 | Newton法 | C语言自带函数 | |
| 0.58781 | 0.58781 | 0.58779 |
3.分析实验出现的问题,总结解决办法。
- 数据点过于稀疏,导致插值多项式的拟合效果不好。 解决办法:增加数据点的密度,使用更多的数据点进行插值计算。
- 构造插值多项式时,计算复杂度较高。 解决办法:使用编程软件进行计算,利用计算机的高速运算能力加快计算速度。
- 插值多项式的阶数过高,导致计算不稳定。 解决办法:控制插值多项式的阶数,避免过高的阶数,从而提高插值的稳定性。
实验总结:在插值多项式的求解过程中,Lagrange插值算法和Newton插值算法都是常用的方法。Newton插值算法相对于Lagrange插值算法的优点是构造插值多项式的计算过程简单,可以通过递推公式一次性得到所有的差商。在实际应用中,根据实验数据的特点和求解的需求,选择合适的插值算法进行计算,并注意处理可能出现的问题,确保插值结果的准确性和稳定性。
【实验心得】
首先,通过Lagrange插值算法,我了解到了如何通过已知数据点的横纵坐标构造出Lagrange插值多项式。这种方法相对简单直观,但需要通过计算拉格朗日基函数和差商来逐项求解。而在求解插值的时候,需要根据需要插值的点的横坐标,计算出对应的纵坐标。
然后,我学习了Newton插值算法,发现相对于Lagrange插值算法,Newton插值算法具有更好的优点。通过使用递推公式,Newton插值算法可以一次性求解出所有的差商,避免了Lagrange插值算法中需要逐项计算的过程。这样,计算的复杂度大大降低,速度也更快。
通过本次实验,我深刻理解到了插值多项式的求解过程,并且明确了Newton插值算法相对于Lagrange插值算法的优点。
得 分_____________
评阅日期_____________
教师签名_____________