龙贝格算法的实现以及与复合梯形公式精度的比较

一、实验名称:
数值积分的实现
二、实验目的:
a.验证龙贝格公式的积分效果
b.验证龙贝格公式对复合梯形公式精度的提高
三、实验内容
(一)用龙贝格公式求定积分
1.用户输入积分区间端点a,b,输入所求积分的精度值precision。
2.求初始步长h=b-a,此时的步长为第0次二分的结果。
3.求二分0次时的梯形公式积分结果T1.
4.进行第一次二分,求此时的梯形积分结果T2。
5.由已经求出的T1和T2计算出辛普生积分结果的S2.
6.继续计算梯形积分公式的积分结果,以此递推求龙贝格积分结果。
7.判断此时的龙贝格积分结果精度是否达到要求,若未达到要求,则继续二分计算,否则输出积分结果。
(二)验证不同积分公式误差的大小
1.用户输入积分区间端点a,b,输入梯形公式二分的次数n。
2.定义数组T,S,C,R分别用来存储梯形,辛普生型,科特斯型,龙贝格型积分结果。
3.调用梯形积分函数,计算T。
4.调用龙贝格积分函数,进行递归计算R。
5.分别输出上述四种类型的积分,同时,每种积分类型的输出中都包括积分结果,误差,有效数字,与上次误差与本次误差的比值。
四、程序关键语句描述
(一)用龙贝格公式求定积分

double f(double x) {
	if (x == 0)
		return 1;
	else return sin(x) / x;
}

上述代码是用来计算函数值f(x),这样就不用每次都把节点值输入到计算机了。判断x是否等于0的原因是计算机不会求极限,因此需要考虑特殊点的函数值。

	h = b - a;
	T1 = h / 2.0 * (f(a) + f(b));
	k = 1;

计算出h的初始步长,T1为二分0次的梯形复合梯形积分结果,k用来判断进行了几次二分,因为至少要二分三次才能计算龙贝格积分结果。

while (1) {
		S = 0;
		x = a + h / 2.0;
		while (1) {	//计算从i = 1到i = n-1的f(xi)的和,并且用S存储
			S = S + f(x);
			x = x + h;
			if (x >= b)	//若已超过区间右端点
				break;
		}
		T2 = T1 / 2.0 + h / 2.0 * S;
		S2 = T2 + 1.0 / 3.0 * (T2 - T1);
		if (k == 1) {
			k++;
			h = h / 2.0;
			T1 = T2;
			S1 = S2;
			continue;
		}
		C2 = S2 + 1.0 / 15.0 * (S2 - S1);
		if (k == 2) {
			k++;
			h = h / 2.0;
			C1 = C2;
			T1 = T2;
			S1 = S2;
			continue;
		}
		R2 = C2 + 1.0 / 63.0 * (C2 - C1);
		if (k == 3) {
			k++;
			h = h / 2.0;
			R1 = R2;
			C1 = C2;
			T1 = T2;
			S1 = S2;
			continue;
		}
		if (fabs(R2 - R1) >= precision) {
			k++;
			h = h / 2.0;
			R1 = R2;
			C1 = C2;
			T1 = T2;
			S1 = S2;
			continue;
		}
		break;
	}

上面的代码中,共有两个while循环,内层while循环用来计算从i = 1到i = n-1的f(xi)的和,并且用S存储,当k=1时,让h减半,也就相当于进行了一次二分,同样的道理,k=2和k=3时进行相同的操作,当k=4时,现在已经计算出来了R2和R1的值,两者做差,判断精度是否满足要求,若不满足则继续二分下去,否则break语句跳出循环。
(三)验证不同积分公式误差的大小

double f(double x) {
	return pow(x,4) + pow(x,3) + pow(x,2) + pow(x,1) + 3 * pow(x,5) + 6 * pow(x,6);
}

上述代码用来计算函数值,本次实验所选取的函数为
f(x) = x + x2 + x3 + x4 + 3x5 + 6x6。
这个函数具有足够的复杂度,而且能够求出原函数,能够明显的观察龙贝格算法对梯形复合公式精度的提高。本实验选取的积分区间为[0,1],积分结果的真实值为1109/420。

double sum(int n, double h) {
	double xi;
	double result = 0;
	for (int i = 1; i <= n - 1; i++) {
		xi = a + i * h;
		result  = result + f(xi);
	}
	return result;
}

这是一个求和函数,在用复合梯形公式求积分的时候会用到,返回的结果为从第一个函数值到第n-1个函数值的累加和。

int valid_num(double r) {
	int l = 10;
	while (1) {
		if (r <= 0.5 * pow(10, 1-l) || l == 0)
			return l;
			l--;
	}
}

计算有效数字的函数,如果积分结果等于真实值,这时的有效数字从理论上来讲应该是正无穷,但显然计算机无法计算正无穷,因此假定有效数字l最高为十位。

void trapezoid() {	//梯形复合公式
	double h;
	for (int i = 0; i < k + 1; i++) {
		h = (b - a) / pow(2, i);
		T[i] = h / 2 * (f(a) + f(b) + 2 * sum(pow(2,i),h));
	}
}

计算复合梯形公式的函数,for循环中,i表示二分的次数,因此pow(2,i)相当于二分出来的小区间的个数n。

void Romberg() {	//龙贝格公式
	int i;
	for (i = 0; i < k; i++) 
		S[i] = 4.0 / 3.0 * T[i + 1] - 1.0 / 3.0 * T[i];
	for (i = 0; i < k - 1; i++)
		C[i] = 16.0 / 15.0 * S[i + 1] - 1.0 / 15.0 * S[i];
	for (i = 0; i < k - 2; i++)
		R[i] = 64.0 / 63.0 * C[i + 1] - 1.0 / 63.0 * C[i];
}

用龙贝格积分公式计算积分值的函数,公式来源于课本。

	for (i = 0,flag = 0; i < k + 1; i++,flag++) {
		printf("现在输出第%d次梯形积分\n",i);
		printf("积分值为%.20lf\n",T[i]);
		printf("误差为%.20lf\n", fabs(T[i] - 1109.0 / 420.0));
		printf("有效数字为%d\n", valid_num(fabs(T[i] - 1109.0 / 420.0)));
		if (flag != 0)
			printf("上次误差与本次误差的比值%lf\n",ter / fabs(T[i] - 1109 / 420.0));
		printf("\n");
		ter = fabs(T[i] - 1109.0 / 420.0);
	}

以梯形积分公式的输出为例,来说明一下输出过程的实现,double型变量ter保存的是本次积分结果的误差,目的是能够方便的计算相邻两次积分误差的比值,便于观察二分次数以及不同积分公式的误差情况。

六、实验体会
通过本次实验,用C语言实现了龙贝格积分公式,成功的验证了龙贝格积分公式的正确性,同时认识到此公式的巧妙之处。在编写程序的时候,涉及到的循环跳转比较多,刚开始感觉很混乱,后来画了一下流程图,感觉豁然开朗,用continue和break进行循环的控制,简化了代码量,同时增强了程序的可读性。
在验证龙贝格公式对梯形复合公式的提高时,我同时验证了几种不同积分公式的余项定理,梯形公式的余项是与h的平方成正比,因此相邻两次梯形积分的误差的比值应接近于4,同理,辛普生余项与h的四次方成正比,相邻两次辛普生积分的误差的比值应接近16,科特斯余项与h的六次方成正比,相邻两次科特斯积分的误差的比值应接近64,通过实验验证,上述理论是正确的。当进行三次二分时,从输出结果中能够看到,此时的龙贝格积分结果的精度比进行十次二分时的梯形复合积分结果的精度还要高,这从侧面说明了,龙贝格公式可以用尽量少的计算,得到较高的精度。同时,我也感受到了龙贝格公式的智慧所在。当进行十次二分时,我们发现第七次科特斯积分结果的输出中,误差比值变为37,第八次科特斯积分结果的输出中,误差比值变为0.6666,前面其余的结果都是符合理论的,只有这两个结果偏离理论,初步分析,应该是随着二分次数的增多,误差呈指数的趋势减小,而计算机又不能进行无限运算,因此怀疑是计算机本身的精度造成了这一现象。这也提示我们,在实际应用中,不能只考虑理论结果,还要考虑运算工具的运算能力,这样才能得到最佳的效率比。
最后,附上代码

//用龙贝格公式验证积分结果的正确性
#include "pch.h"
#include<stdio.h>
#include<math.h>
double f(double x) {
	if (x == 0)
		return 1;
	else return sin(x) / x;
}
int main()
{
	double a, b, precision, h, x;//a,b为区间的端点,presicion为给定的积分精度,h是每个积分小区间的长度
	double S;
	double T1, T2, S1, S2, C1, C2, R1, R2;
	int k;	//
	printf("请输入区间端点a,b\n");
	scanf("%lf%lf", &a, &b);
	printf("请输入精度\n");
	scanf("%lf", &precision);
	h = b - a;
	T1 = h / 2.0 * (f(a) + f(b));
	k = 1;
	while (1) {
		S = 0;	
		x = a + h / 2.0;
		while (1) {	//计算从i = 1到i = n-1的f(xi)的和,并且用S存储
			S = S + f(x);
			x = x + h;
			if (x >= b)	//若已超过区间右端点
				break;
		}
		T2 = T1 / 2.0 + h / 2.0 * S;
		S2 = T2 + 1.0 / 3.0 * (T2 - T1);
		if (k == 1) {
			k++;
			h = h / 2.0;
			T1 = T2;
			S1 = S2;
			continue;
		}
		C2 = S2 + 1.0 / 15.0 * (S2 - S1);
		if (k == 2) {
			k++;
			h = h / 2.0;
			C1 = C2;
			T1 = T2;
			S1 = S2;
			continue;
		}
		R2 = C2 + 1.0 / 63.0 * (C2 - C1);
		if (k == 3) {
			k++;
			h = h / 2.0;
			R1 = R2;
			C1 = C2;
			T1 = T2;
			S1 = S2;
			continue;
		}
		if (fabs(R2 - R1) >= precision) {
			k++;
			h = h / 2.0;
			R1 = R2;
			C1 = C2;
			T1 = T2;
			S1 = S2;
			continue;
		}
		break;
	}
	printf("精度为%lf的积分结果为:%.9lf\n", precision, R2);
}

 // 比较龙贝格公式对积分精度的提高效果
#include "pch.h"
#include<stdio.h>
#include<math.h>

int k;
double a, b;
double T[11];
double S[11];
double C[10];
double R[10];
double f(double x) {
	return pow(x,4) + pow(x,3) + pow(x,2) + pow(x,1) + 3 * pow(x,5) + 6 * pow(x,6);
}
double sum(int n, double h) {
	double xi;
	double result = 0;
	for (int i = 1; i <= n - 1; i++) {
		xi = a + i * h;
		result  = result + f(xi);
	}
	return result;
}
int valid_num(double r) {
	int l = 10;
	while (1) {
		if (r <= 0.5 * pow(10, 1-l) || l == 0)
			return l;
			l--;
	}
}
void trapezoid() {	//梯形复合公式
	double h;
	for (int i = 0; i < k + 1; i++) {
		h = (b - a) / pow(2, i);
		T[i] = h / 2 * (f(a) + f(b) + 2 * sum(pow(2,i),h));
	}
}
void Romberg() {	//龙贝格公式
	int i;
	for (i = 0; i < k; i++) 
		S[i] = 4.0 / 3.0 * T[i + 1] - 1.0 / 3.0 * T[i];
	for (i = 0; i < k - 1; i++)
		C[i] = 16.0 / 15.0 * S[i + 1] - 1.0 / 15.0 * S[i];
	for (i = 0; i < k - 2; i++)
		R[i] = 64.0 / 63.0 * C[i + 1] - 1.0 / 63.0 * C[i];
}
int main()
{
	printf("请输入a,b\n");
	scanf("%lf%lf", &a, &b);
	printf("请输入二分次数k(k > 2)\n");
	scanf("%d", &k);
	trapezoid();
	Romberg();
	int i,flag;
	double ter,rate;
	printf("\n");
	//printf("T\n");
	for (i = 0,flag = 0; i < k + 1; i++,flag++) {
		printf("现在输出第%d次梯形积分\n",i);
		printf("积分值为%.20lf\n",T[i]);
		printf("误差为%.20lf\n", fabs(T[i] - 1109.0 / 420.0));
		printf("有效数字为%d\n", valid_num(fabs(T[i] - 1109.0 / 420.0)));
		if (flag != 0)
			printf("上次误差与本次误差的比值%lf\n",ter / fabs(T[i] - 1109 / 420.0));
		printf("\n");
		ter = fabs(T[i] - 1109.0 / 420.0);
	}
	//printf("S\n");
	for (i = 0,flag = 0; i < k; i++,flag++) {
		printf("现在输出第%d次辛普生积分\n", i);
		printf("积分值为%.20lf\n", S[i]);
		printf("误差为%.20lf\n", fabs(S[i] - 1109 /420.0)); 
		printf("有效数字为%d\n", valid_num(fabs(S[i] - 1109.0 / 420.0)));
		if (flag != 0)
			printf("上次误差与本次误差的比值%lf\n", ter / fabs(S[i] - 1109 / 420.0));
		printf("\n");
		ter = fabs(S[i] - 1109.0 / 420.0);
	}
	//printf("C\n");
	for (i = 0,flag = 0; i < k - 1; i++,flag++) {
		printf("现在输出第%d次科特斯积分\n", i);
		printf("积分值为%.20lf\n", C[i]);
		printf("误差为%.20lf\n", fabs(C[i] - 1109 / 420.0));
		printf("有效数字为%d\n", valid_num(fabs(C[i] - 1109.0 / 420.0)));
		if (flag != 0)
			printf("上次误差与本次误差的比值%lf\n", ter / fabs(C[i] - 1109 / 420.0));
		printf("\n");
		ter = fabs(C[i] - 1109.0 / 420.0);
	}
	//printf("R\n");
	for (i = 0; i < k - 2; i++) {
		printf("现在输出第%d次龙贝格积分\n", i);
		printf("积分值为%.10lf\n", R[i]);
		printf("误差为%.10lf\n", fabs(R[i] - 1109 / 420.0));
		printf("有效数字为%d\n", valid_num(fabs(R[i] - 1109.0 / 420.0)));
	}
}


其实,用龙贝格公式验证积分结果的正确性还有一种写法,可以用goto语句实现,代码如下。这个代码可以对照着文末的流程图来看,这样会对这个程序理解的更深入。

// 龙贝格算法2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include<stdio.h>
#include<math.h>
/*
int k;
double a, b;
double T[11];
double S[11];
double C[10];
double R[10];
double f(double x) {
	if (x == 0)
		return 1;
	return sin(x) / x;
}
double sum(int n, double h) {
	double xi;
	double result = 0;
	for (int i = 1; i <= n - 1; i++) {
		xi = a + i * h;
		result = result + f(xi);
	}
	return result;
}
void trapezoid() {	//梯形复合公式
	double h;
	for (int i = 0; i < k + 1; i++) {
		h = (b - a) / pow(2, i);
		T[i] = h / 2 * (f(a) + f(b) + 2 * sum(pow(2, i), h));
	}
}
void Romberg() {	//龙贝格公式
	int i;
	for (i = 0; i < k; i++)
		S[i] = 4.0 / 3.0 * T[i + 1] - 1.0 / 3.0 * T[i];
	for (i = 0; i < k - 1; i++)
		C[i] = 16.0 / 15.0 * S[i + 1] - 1.0 / 15.0 * S[i];
	for (i = 0; i < k - 2; i++)
		R[i] = 64.0 / 63.0 * C[i + 1] - 1.0 / 63.0 * C[i];
}
*/
double f(double x) {
	if (x == 0)
		return 1;
	return sin(x) / x;
}
int main()
{
	double a, b,r,h,x;
	double S;
	double T1, T2, S1, S2,C1,C2,R1,R2;
	int k;
	printf("请输入区间端点a,b\n");
	scanf("%lf%lf", &a, &b);
	printf("请输入精度\n");
	scanf("%lf", &r);
	h = b - a;
	T1 = h / 2.0 * (f(a) + f(b));
	k = 1;
L1:
	S = 0;
	x = a + h / 2.0;
L2:
	S = S + f(x);
	x = x + h;
	if (x < b)
		goto L2;
	T2 = T1 / 2.0 + h / 2.0 * S;

	S2 = T2 + 1.0 / 3.0 * (T2 - T1);
	if (k == 1) {
		k++;
		h = h / 2.0;
		T1 = T2;
		S1 = S2;
		goto L1;
	}
	C2 = S2 + 1.0 / 15.0 * (S2 - S1);
	if (k == 2) {
		C1 = C2;
		k++;
		h = h / 2.0;
		T1 = T2;
		S1 = S2;
		goto L1;
	}
	R2 = C2 + 1.0 / 63.0 * (C2 - C1);
	if (k == 3) {
		R1 = R2;
		C1 = C2;
		k++;
		h = h / 2.0;
		T1 = T2;
		S1 = S2;
		goto L1;
	}
	if (fabs(R2 - R1) >= r) {
		R1 = R2;
		C1 = C2;
		k++;
		h = h / 2.0;
		T1 = T2;
		S1 = S2;
		goto L1;
	}
	printf("精度为%lf的积分结果为:%.7lf\n", r,R2);
}

在这里插入图片描述

  • 5
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是用 Python 实现的三种数值积分算法: ```python # 复合辛普森公式 def composite_simpson(f, a, b, n): h = (b - a) / n x = [a + i * h for i in range(n + 1)] y = [f(x[i]) for i in range(n + 1)] sum1 = sum([y[i] for i in range(1, n, 2)]) # 奇数项之和 sum2 = sum([y[i] for i in range(2, n, 2)]) # 偶数项之和 return h / 3 * (y[0] + y[n] + 4 * sum1 + 2 * sum2) # 复合梯形公式 def composite_trapezoidal(f, a, b, n): h = (b - a) / n x = [a + i * h for i in range(n + 1)] y = [f(x[i]) for i in range(n + 1)] return h / 2 * (y[0] + y[n] + 2 * sum([y[i] for i in range(1, n)])) # 龙贝格算法 def romberg(f, a, b, n): r = [[0] * (n + 1) for _ in range(n + 1)] h = b - a r[0][0] = (f(a) + f(b)) * h / 2 for i in range(1, n + 1): h /= 2 r[i][0] = r[i - 1][0] / 2 + h * sum([f(a + j * h) for j in range(1, 2 ** i, 2)]) for k in range(1, i + 1): r[i][k] = (4 ** k * r[i][k - 1] - r[i - 1][k - 1]) / (4 ** k - 1) return r[n][n] ``` 对于这三种算法,我们可以从以下几个方面进行评价: 1. 精度:这三种算法都是数值积分算法,其最终结果与真实值的偏差是评价其精度的重要指标。在同样条件下,复合辛普森公式精度通常比复合梯形公式更高,而龙贝格算法精度则更高一些。 2. 收敛速度:算法的收敛速度是指算法所需步骤的数量,以及每一步所需计算的运算量。在同样条件下,龙贝格算法的收敛速度最快,复合辛普森公式次之,复合梯形公式最慢。 3. 稳定性:数值积分算法通常会涉及到除和加减乘除等运算,由于计算机浮点数的特性,这些运算可能会产生误差。因此,算法的稳定性是指算法对于输入数据的变化所导致的结果的稳定程度。在同样条件下,复合辛普森公式复合梯形公式的稳定性较好,而龙贝格算法的稳定性较差。 综上所述,不同的数值积分算法在不同的场景下有其各自的优势和不足。我们需要根据具体的问题场景和要求,选择合适的算法来进行数值积分

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值