数值分析——拉格朗日插值及牛顿插值算法的实现

要求用C语言或者C++实现拉格朗日插值及牛顿插值,牛顿插值一定要体现插商的继承性,当增加新的节点时,不能全部重新计算插商,而是要根据前面已经计算出的插商,来计算我们现在需要的插商。(文末有两种插值的代码)

编写过程及重要代码的分析:

(一)拉格朗日插值

1.定义变量N,用来接收用户输入的插值节点个数。

2.定义数组Xn[N]和Yn[N],用来接收用户输入的节点(x,y)。

3.定义变量x,用来接收用户输入的待插值点。

4.编写拉格朗日插值函数,函数具体可细分如下:

a.分别计算每个基函数的分子和分母,用两层for循环实现,计算时需注意不能 出现类似于“Xn[j] - Xn[i],j == i”的现象,用if语句判断i和j的值是否相等。

b.计算最终的插值结果,让基函数的分子除以分母再乘以节点的Yn[i].

c.输出插值结果,可以预先设置f(x),这样就能输出插值误差。

5.当插值节点为三个时,可以用事后估计法来计算误差。

(二)牛顿插值

1.定义变量N,用来接收用户输入的插值节点个数。

2.定义float类型的数组X[N],Y[N]存储用户输入的节点(x,y)。

3.定义float类型数组L[N]用来存储差商结果的最后一行,其中L[0] = Y[N-1],即数组Y[]的最后一个元素。

4.计算插商并且把最后一行保存到数组L[N]中,每一次插商的中间结果用数组Y[N]暂时保存,因为后面不会再用到Y[N]。

5.定义变量x接收用户输入的待插值点。

6.按照公式

计算插值结果,插商已经存储在数组L[N]中。

7.输出插值结果,并让用户选择是继续增加节点还是结束程序。

8.若用户继续增加节点,则令N自加,并提示用户输入新增加的节点。

9.利用插商的承袭性计算f(xn+1,xn,…,x1)

10.用户再次输入新的待插值点,回到步骤7。

(三)验证插商的性质

1.验证插商的承袭性。

2.验证课本25页公式(24)。

3.验证插商的对称性。

(四)比较拉格朗日插值和牛顿插值

1.输入相同的插值节点,观察牛顿插值和拉格朗日插值结果的异同。

程序关键语句描述

(一)拉格朗日插值

牛顿插值Java代码 牛顿插值法算法设计_数值分析

以上是本程序关键语句,用来计算基函数。整段程序由两层for循环构成,内层for循环又是由两个if语句组成。第一个if语句判断k和i是否相等,避免出现Xn[k] - Xn[i] == 0的情况,而且这种情况也不符合公式要求。第二个if语句是判断第一个if语句是否得到执行,也就是说是否计算了基函数的分子和分母。如果if语句成立,则计算最终插值结果。基函数分别计算分子和分母,再用分子除以分母,而没有一边计算,一边相除。主要是考虑减小误差,因为两相差较大的数相除,会产生难以预料的误差。

牛顿插值Java代码 牛顿插值法算法设计_牛顿插值_02


以上代码是当插值节点为三个时,进行的误差事后估计。

(二)牛顿插值

牛顿插值Java代码 牛顿插值法算法设计_牛顿插值Java代码_03


以上代码通过两层for循环计算插商,插商的中间结果暂时保存在数组Y[N]中,

牛顿插值Java代码 牛顿插值法算法设计_牛顿插值Java代码_04


上述公式中的插商值保存在数组L[N]中。

牛顿插值Java代码 牛顿插值法算法设计_牛顿插值_05


输入x后计算最终的插值结果,考虑到具有承袭性,因此用变量m保存上次的值,下次计算时直接用m乘以(x - X[N - 1})即可。

牛顿插值Java代码 牛顿插值法算法设计_数值分析_06


动态增加节点的代码,因为前面已经用数组L[N]保存了插商的结果,所以现在可以利用插商的承袭性来计算f(xn+1,xn,…,x1)。

(三)验证插商的性质

牛顿插值Java代码 牛顿插值法算法设计_牛顿插值_07


以上为验证插商承袭性的核心代码,主要思路是先输入N个节点,输出此时的插商结果,然后再输入一个节点,利用插商的承袭性,计算并输出新的插商结果。再输入一遍前面输入的(N+1)个节点(此时N已经自加),判断不利用承袭性计算的插商和利用承袭性计算的插商结果是否相同。

对称性的验证,以三个节点为例,我们用同样的插商计算函数,我们只要改变输入的节点的顺序,即可验证插商是否满足对称性。

牛顿插值Java代码 牛顿插值法算法设计_数值分析_08


以上代码是验证公式24的正确性,最终求得的f即为,将用此公式求得的和用插商求得的比较,如果相等,即可验证公式(24)。

下面上代码,这个是拉格朗日插值

#include "pch.h"
#include <iostream>
#include <math.h>

using namespace std;
int main()
{
	double x;
	int N;
	int num_y = 0;//对y进行计数
	int flag = 0;
	double Pn = 0, Rn = 0;//Pn是插值结果,Rn是误差
	double inter1 = 1, inter2 = 1;//inter1,inter2均为中间结果,无实际意义
	double Xn[10];
	double Yn[10];
L1:
	Pn = 0;
	Rn = 0;
	inter1 = inter2 = 1;
	num_y = flag = 0;
	cout << "请输入节点个数:";
	cin >> N;
	cout << "请依次输入各个节点(xi,yi)" << endl;
	for (int j = 0; j < N; j++)
		cin >> Xn[j] >> Yn[j];

	cout << "请入插值x1:";

	cin >> x;
	for (int k = 0; k < N; k++) {
		for (int i = 0; i < N; i++)
			if (i != k) {
				inter1 = (x - Xn[i]) * inter1;//基函数的分子
				inter2 = (Xn[k] - Xn[i]) * inter2;//基函数的分母
				flag = 1;
			}
		if (flag == 1) {
			Pn = inter1 / inter2 * Yn[num_y] + Pn;
			num_y++;
			inter1 = 1;
			inter2 = 1;
			flag = 0;
		}
	}
	Rn = abs(log(x) - Pn);	
	cout << "插值结果" << Pn << endl;
	cout << "插值误差" << Rn << endl;
	goto L1;
	
	if (N == 3) {
		float y1, y2;
		y1 = (Yn[1] - Yn[0]) / (Xn[1] - Xn[0]) * (x - Xn[1]) + Yn[1];
		y2 = (Yn[2] - Yn[0]) / (Xn[2] - Xn[0]) * (x - Xn[2]) + Yn[2];
		Rn = (x - Xn[1]) / (Xn[2] - Xn[1]) * (Yn[2] - Yn[1]);
		printf("通过误差的事后估计法计算出的误差为%f", Rn);
	}
}

这个是牛顿插值

#include "pch.h"
#include <stdio.h>
#include<math.h>
int main()
{
	int N;
	int i;
	int choice;
	float m, n;	//m,n均为中间值,无实际意义
	float x;
	float Pn;//插值结果
	float Rn;
	float X[10], Y[10], L[10];
	printf("请输入节点个数:");
	scanf("%d", &N);
	printf("请依次输入各个节点\n");
	for (i = 0; i < N; i++)
		scanf("%f%f", &X[i], &Y[i]);
	L[0] = Y[N - 1];
	for (int j = 0; j < N; j++) {
		for (i = 0; i < N - 1 - j; i++)
			Y[i] = (Y[i+1] - Y[i]) / (X[i+j+1] - X[i]);
		L[j + 1] = Y[i-1];
	}
	L1:
	printf("请输入x:");
	scanf("%f", &x);
	m = 1;
	Pn = L[0];
	for (i = 1; i < N;i++) {
			m = (x - X[N - i]) * m;//算法的简化
		Pn = Pn + L[i] * m;
	}	
	printf("插值结果为%f\n", Pn);
	printf("插值误差为%f\n",fabs(Pn - log(x)));
	printf("按1继续增加节点,按0结束程序:");
	scanf("%d", &choice);
	if (choice == 0)
		return 0;
	/*以下是动态增加节点的代码*/
	N++;
	printf("请输入新增加的节点");
	scanf("%f%f", &X[N - 1], &Y[N - 1]);
	m = Y[N - 1];
	for (i = 0; i < N - 1; i++) {
		n = (m - L[i]) / (X[N - 1] - X[N - 2 - i]);
		L[i] = m;
		m = n;
	}
	L[i] = m;
	goto L1;
	/*以上是动态增加节点的代码*/
}