实验内容 试建立8个节点的Gauss-Legendre求积公式,要求数值求积公式中的Gauss点以及求积系数都具有8位有效数字;用所建立的Gauss型求积公式计算积分
的近似值,并用四等分区间的复化两点Gauss型求积公式计算该积分的近似值,对二者的精度进行比较,并解释其原因。(提示:用非线性方程迭代解法求得Gauss 点,求积系数根据插值型求积公式的系数计算,具体的积分值可以用数值方法求得)。
算法原理 高斯——勒让德求积公式 插值型求积公式任意给定n + 1个节点,至少有 n 次代数精度。 牛顿-柯特斯公式是等距节点的插值求积公式,当 n 为 偶数时,有n + 1次代数精度。插值求积公式中有n + 1个节点,n + 1个求积系数,共有2n + 2个未知参数,适当选取这些参数可使求积公式具有2n + 1次代数精度,这时就构成了高斯型求积公式。高斯型求积公式就是最高次代数精度的求积公式。高斯公式不局限与等步长分区域,意思是不用二分的方法直接划分区域,在简单的计算下就可以得到高精度的积分式子。插值型求积公式的节点a < x0 < x1 < … < xn < b是高斯点的充分必要条件是以这些节点为零点的多项式。与任何次数不超过n的多项式P(x)带权正交,同时由于其系数都大于0,所以我们也可以得到该求积公式是收敛的。对于勒让德求积公式,就是说我们取权函数P(x)=1,同时区间为[-1, 1],所以勒让德多项式的零点就是我们的求积的高斯点。使插值求积公式有2n+1次代数精度的节点xi,i=0,1,…,n称为高斯点,该插值求积公式称为高斯型求积公式,简称高斯公式。高斯点的确定原则上可以根据它们满足的代数精度要求、通过求解非线性方程组得出。对勒让德多项式取其零点作为求积节点,即可构造出高斯-勒让德(Gauss-Legendre)公式,也简称高斯公式。
复化高斯公式 将求积区间[a,b]分为 n 等分:n=(b-a)/h上使用低阶高斯公式(如一阶中矩形公式): 高斯公式的值随节点数目的增加而收敛到准确积分值。
优点:
缺点:
高斯点难求(即多项式的根难求);
高斯点是无理数,求积系数也是无理数。
使用情况:
程序框图
编程实现 本次编程采用Python的面向对象方法进行编写,其中对算法的初步理解以及求解方程的根的数值方法是本程序的难点所在。 利用所编程序,确定并计算题目中所给积分的数值解: 以下将用Python进行编程实验,分别利用高斯——勒让德求积公式与复化两点高斯型求积公式对积分要求积分进行求解,最后进行结果分析与讨论。 Python代码详解及运行结果如下:
代码详解:
gauss_legendre_int.py: 高斯——勒让德求积公式文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 import numpy as npimport sympyimport mathclass GaussLegendreIntegration : """ 高斯——勒让德求积公式,核心部分求解零点与系数 """ def __init__ (self, int_fun, int_interval, zeros_num=10 ): self.int_fun = int_fun if len (int_interval) == 2 : self.a, self.b = int_interval[0 ], int_interval[1 ] else : raise ValueError("积分区间参数设置不规范,应为[a,b]." ) self.n = int (zeros_num) self.zeros_points = None self.int_value = None self.A_k = None def cal_int (self ): """ 高斯——勒让德求积公式 :return: """ self._cal_Ak_coef_() fun_val = self.int_fun(self.zeros_points) self.int_value = np.dot(self.A_k, fun_val) return self.int_value def _cal_gauss_zeros_points_ (self ): """ 计算高斯零点 :return: """ t = sympy.Symbol('t' ) p_n = (t ** 2 - 1 ) ** self.n / (math.factorial(self.n) * 2 ** self.n) diff_p_n = sympy.diff(p_n, t, self.n) self.zeros_points = np.asarray(sympy.solve(diff_p_n, t), dtype=np.float_) return diff_p_n, t def _cal_Ak_coef_ (self ): """ 计算Ak系数 :return: """ diff_p_n, t = self._cal_gauss_zeros_points_() Ak_poly = sympy.lambdify(t, 2 / ((1 - t ** 2 ) * (diff_p_n.diff(t, 1 ) ** 2 ))) self.A_k = Ak_poly(self.zeros_points) self.A_k = self.A_k * (self.b - self.a) / 2 self.zeros_points = (self.b - self.a) / 2 * self.zeros_points + (self.a + self.b) / 2
test_gauss_legendre.py: 高斯——勒让德求积公式测试文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 import numpy as npimport sympyimport matplotlib.pyplot as pltfrom NumericalIntegration.gauss_legendre_int import GaussLegendreIntegrationdef fun (x ): """ 定义积分的被积函数 :param x:自变量 :return:被积函数 """ return np.cos(x) * np.exp(x) if __name__ == "__main__" : gauss_zeros_num = np.arange(8 , 20 , 1 ) int_accurate = -0.5 * (np.exp(np.pi) + 1 ) print ("精确值:" , int_accurate) precision = [] for num in gauss_zeros_num: legendre = GaussLegendreIntegration(fun, [0 , np.pi], zeros_num=num) int_value = legendre.cal_int() precision.append(int_accurate - int_value) print ("num:%d,积分值:%.15f,误差:%.15e" % (num, int_value, precision[-1 ])) plt.figure(figsize=(8 , 6 )) plt.title("Gauss-Legendre Quadrature Formula Error Curve" , fontdict={"fontsize" : 15 }) plt.xlabel("The Number of Gauss Zero-Points" , fontdict={"fontsize" : 12 }) plt.ylabel("Error" , fontdict={"fontsize" : 12 }) plt.plot(gauss_zeros_num, precision, 'ro-' ) plt.grid(axis='y' , color='b' , linestyle='--' , linewidth=0.5 ) plt.savefig("/Users/leo/Desktop/Gauss-Legendre.png" , dpi=300 , bbox_inches="tight" ) plt.show()
composite_2points_gauss_legendre.py:复化两点高斯型求积公式文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class CompositeGaussLegendreIntegration : """ 复化两点高斯——勒让德求积公式,等距节点数自定义 """ def __init__ (self, int_fun, int_interval, interval_num=4 ): self.int_fun = int_fun if len (int_interval) == 2 : self.a, self.b = int_interval[0 ], int_interval[1 ] else : raise ValueError("积分区间参数设置不规范,应为[a, b]." ) self.interval_num = interval_num self.int_value = None def cal_int (self ): """ 复化两点高斯——勒让德求积公式 :return: """ fun_value = 0 interval_len = (self.b - self.a) / self.interval_num for k in range (self.interval_num - 1 ): fun_value += self.int_fun(self.a + (k + 1 / 2 ) * interval_len) self.int_value = interval_len * fun_value return self.int_value
test_composite_2points_gauss_legendre.py:复化两点高斯型求积公式测试文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 import numpy as npimport matplotlib.pyplot as pltimport symbolfrom NumericalIntegration.composite_2points_gauss_legendre import CompositeGaussLegendreIntegrationdef fun (x ): """ 定义积分被积函数 :param x: 自由变量 :return: """ return np.cos(x) * np.exp(x) if __name__ == "__main__" : interval_num = np.arange(100000 , 1000000 , 100000 ) int_accurate = -0.5 * (np.exp(np.pi) + 1 ) print ("精确解:" , int_accurate) precision = [] for num in interval_num: legendre = CompositeGaussLegendreIntegration(fun, [0 , np.pi], interval_num=num) int_value = legendre.cal_int() precision.append(int_accurate - int_value) print ("num=%d,积分值:%.15f,误差:%.15e" % (num, int_value, precision[-1 ])) plt.figure(figsize=(8 , 6 )) plt.title("Composite Gauss Quadrature Formula Error Curve" , fontdict={"fontsize" : 16 }) plt.xlabel("The Number of Integral Intervals" , fontdict={"fontsize" : 12 }) plt.ylabel("Error" , fontdict={"fontsize" : 12 }) plt.plot(interval_num, precision, 'ro-' ) plt.grid(axis='y' , color='b' , linestyle='--' , linewidth=0.5 ) plt.savefig("/Users/leo/Desktop/CompositeGauss.png" , dpi=300 , bbox_inches="tight" ) plt.show()
运行结果
结果分析与讨论
有两种求积公式的结果可以看出,高斯——勒让德求积公式计算较为复杂,但收敛速度快且精度较高,在20个高斯零点以内可以收敛,且误差缩小到1e-14的个数量级;而复化两点高斯型积分公式收敛速度较慢,需要将区间分为1e6个小区间之后,精度只能达到1e-6个数量级,继续增加其区间,缩小步长时,程序运行速度明显变慢,说明其效率低于高斯——勒让德求积公式(由于n=4,四等分区间时,其结果差异很大,所以舍弃,只能用百万区间划分才能确保精度)。
高斯——勒让德求积公式精度高,收敛速度快的主要原因是:高斯——勒让德求积公式的高斯节点的选取的特殊性,并不采用等距节点,而是利用勒让德多项式的插值节点作为高斯点进行积分,其结果更加精确,且收敛速度更快,也更加容易接近精确解。
反观等距节点插值,对于高次具有强震荡曲线的函数,效果并不理想。主要是由于函数在小区间内的变化率太大,所引起的误差较大,因此需要不断增加其等距节点个数,缩小步长或区间范围,在小区间中用高斯型求积公式进行积分求解才更有效果。
参考 [1]《数值分析》欧阳洁等。
[2] https://www.bilibili.com/video/BV1Lq4y1U7Hj?p=29&spm_id_from=pageDriver
[3] https://wenku.baidu.com/view/b3fc62d74b7302768e9951e79b89680203d86bb7.html
[4] https://blog.csdn.net/poqw2010/article/details/79487919
[5] https://blog.csdn.net/catinlbb/article/details/103534439
[6] https://www.doc88.com/p-3901680450501.html
[7] https://zhuanlan.zhihu.com/p/439768015