第四十二篇 自适应高斯勒让德法则
之前的文章中,就已经提出了调整小区间的长度来改变积分函数的值。因为,如果函数在某些区域变化的很快,可能需要一个更窄的区间,而如果函数是缓慢变化,更改成一个长区间代替几个小区间,但精度是完全一样的。手动执行这些修改显然会很繁琐,所以在这这篇中,将介绍一种自适应算法来自动调整区间的长度。
这种自适应方法的基础是通过比较两个具有不同精度的法则获得的结果而得到每个区间上的误差估计。假设更精确的法则给出“准确”的结果,因此误差为两个法则的差值。如果这个误差估计超过了指定的容许范围,则将考虑将区间分成两个,并重复这个过程。
该算法从整个范围的的单个区间开始,迭代将其分成更小的区间,直到所有区间都满足误差容许值。
一个典型的例子在下图。第一张图涉及到整个范围的积分。字母F(表示“false”)表示超过了容差,因此该范围被分成两个宽度相等的区间。图(b)显示了第二次迭代,涉及到对两个区间的积分。字母T(表示“true”)表示右条带满足误差准则;但是F表示左边的条带没有,所以这个范围又被分成两个更小的等宽区间。图6©显示了第三次迭代,涉及对范围左半部分的两个区间进行积分。得到右半边已经满足了容差但左边一半没有,需要进一步细分区间,.如图(d)所示,,所有带现在标记T,因此所有区间都满足容差。在每条上使用更精确的法则,然后总结和输出。一旦区间满足误差公差并标记为T,就不会再次验证。
该算法显示了绝对误差和相对误差标准的必要性。从工程角度看,相对误差是最重要的,因为它将误差限制在某些指定的比例。然而,绝对误差也需要作为一种保障,在任何区间的面积变得非常接近于零时,相对误差容错性可能不合理。在这种情况下绝对错误将优先考虑。
程序如下
#重复g高斯勒让德法则
import numpy as np
import math
import B
nr1=1
answer1=np.zeros((nr1));err1=np.zeros((nr1));limits1=np.zeros((nr1,2))
answer2=np.zeros((nr1));err2=np.zeros((nr1));limits2=np.zeros((nr1,2))
conv1=np.array([False])
conv2=np.array([False])
#法则2比法则1更准确,nsp2>nsp1
limits1[0,:]=(0.0,1.0);abserr=1e-3;relerr=1e-3;nsp1=1;nsp2=4
samp1=np.zeros((nsp1,1));samp2=np.zeros((nsp2,1));wt1=np.zeros((nsp1));wt2=np.zeros((nsp2))
B.gauss_legendre(samp1,wt1);B.gauss_legendre(samp2,wt2)
print('自适应高斯法则')
print('绝对值错误容差',abserr)
print('相对错误容差',relerr)
print('积分上下限',limits1)
print('低阶高斯勒让德法则',nsp1)
print('高阶高斯勒让德法则',nsp2)
def f63(x):
f63=x**(1.0/7.0)/(x**2.0+1.0)
return f63
while(True):
area=0;tot_err=0;ct=0;cf=0
for i in range(1,nr1+1):
if (not conv1[i-1])==True:
a=limits1[i-1,0];b=limits1[i-1,1]
nsp1=samp1.shape[0];nsp2=samp2.shape[0]
wr=b-a;hr=0.5*wr;area1=0;area2=0
for j in range(1,nsp1+1):
area1=area1+wt1[j-1]*hr*f63(a+hr*(1.0-samp1[j-1,0]))
for j in range(1,nsp2+1):
area2=area2+wt2[j-1]*hr*f63(a+hr*(1.0-samp2[j-1,0]))
errest=area1-area2;tol=max(abserr,relerr*abs(area2))
ans=area2;verdict=False
if abs(errest)<tol:
verdict=True
answer1[i-1]=ans;conv1[i-1]=verdict;err1[i-1]=errest
if bool(conv1[i-1])==True:
ct=ct+1
else:
cf=cf+1
area=area+answer1[i-1];tot_err=tot_err+err1[i-1]
if cf==0:
print('重复数量',nr1)
print(' 小分区上下限 区域面积 误差')
for i in range(1,nr1+1):
for j in range(1,3):
print('{:12.4}'.format(limits1[i-1,j-1]),end='')
print('{:16.8}'.format(answer1[i-1]),end='')
print('{:16.8}'.format(err1[i-1]))
print('解和总误差',area,tot_err)
break
limits2[:]=limits1[:];answer2[:]=answer1[:];conv2[:]=conv1[:];err2[:]=err1[:]
nr2=nr1;nr1=ct+2*cf
answer1=np.zeros((nr1));conv1=np.zeros((nr1));err1=np.zeros((nr1));limits1=np.zeros((nr1,2))
conv1[:]=False;inew=0
for i in range(1,nr2+1):
if conv2[i-1]==True:
inew=inew+1
limits1[inew-1,:]=limits2[i-1,:];answer1[inew-1]=answer2[i-1]
err1[inew-1]=err2[i-1];conv1[inew-1]=True
else:
inew=inew+1;limits1[inew-1,0]=limits2[i-1,0]
limits1[inew-1,1]=(limits2[i-1,0]+limits2[i-1,1])*0.5
inew=inew+1
limits1[inew-1,0]=(limits2[i-1,0]+limits2[i-1,1])*0.5
limits1[inew-1,1]=limits2[i-1,1]
answer2=np.zeros((nr1));conv2=np.zeros((nr1));err2=np.zeros((nr1));limits2=np.zeros((nr1,2))
终端输出结果