老規矩,數學原理什麼的就不寫了。
直接貼代碼和實例演示,以下代碼基於python和numpy。
在這裏,我將用代碼實現復化梯形算法、復化 Simpson 算法、Romberg 積分算法和三點 Gauss-Legendre求積算法。
往期博客:
數值積分
復化梯形算法
復化梯形公式如下圖所示:
首先
import numpy as np
定義函數
以下便是我定義的函數:
def tx_fh(x0,f,n=2**5):
a=x0[0]
b=x0[1]
if type(f) is np.ndarray:
y=2*np.sum(f)-f[0]-f[-1]
tn=((b-a)/(f.shape[0]-1))*y/2
else:
x=np.linspace(a,b,n+1)
y=2*np.sum(f(x))-f(a)-f(b)
tn=((b-a)/n)*y/2
print(tn)
return tn
參數說明
“x0”指的是自變量的定義域。
“f”可以是函數關係或者是確定值,後續可以看到兩者區別。
“n”指的是將定義域分爲n等份。
實例運行
拿個實例運行一下,在這個實例中,“f”是一個函數關係。
x=np.array([0,1])
f=lambda x:4/(1+x**2)
tx_fh(x,f,2**3)
得出以下結果:
3.1389884944910893
再試驗一下代入的“f”是確定值的情況,這種適用於不知道函數關係的情況。
不過,爲了對比以及方便,我在演示時,用函數關係生成了一個確定值數組“f”。
x=np.array([0,1])
x0=np.linspace(x[0],x[1],9)
f=lambda x:4/(1+x**2)
y0=f(x0)
print('y0=',y0)
tx_fh(x,y0)
得出以下結果:
y0= [4. 3.93846154 3.76470588 3.50684932 3.2 2.87640449 2.56 2.26548673 2. ]
3.1389884944910893
復化 Simpson 算法
復化 Simpson 公式如下圖所示:
定義函數
以下便是我定義的函數:
def simpson_fh(x0,f,n=2**5):
a=x0[0]
b=x0[1]
if type(f) is np.ndarray:
f1=f[1::2].copy()
f2=f[0::2].copy()
y=4*np.sum(f1)+2*np.sum(f2)-f[0]-f[-1]
sn=2*((b-a)/(f.shape[0]-1))*y/6
else:
x=np.linspace(a,b,2*n+1)
x1=x[1::2].copy()
x2=x[0::2].copy()
y=4*np.sum(f(x1))+2*np.sum(f(x2))-f(a)-f(b)
sn=((b-a)/n)*y/6
print(sn)
return sn
參數說明
“x0”指的是自變量的定義域。
“f”可以是函數關係或者是確定值,後續可以看到兩者區別。
不過,如果要輸入確定值數組的話,數組內的元素必須是2n+1個,即奇數個,才能符合公式要求。
我在編寫的時候因爲主要是給自己用,所以也沒寫斷言什麼的來判斷輸入的是否正確,默認給的是對的。
“n”指的是將定義域分爲2n等份。
實例運行
拿個實例運行一下,在這個實例中,“f”是一個函數關係。
x=np.array([0,1])
f=lambda x:4/(1+x**2)
simpson_fh(x,f,2**2)
得出以下結果:
3.141592502458707
再試驗一下代入的“f”是確定值的情況,這種適用於不知道函數關係的情況。
x=np.array([0,1])
x0=np.linspace(x[0],x[1],9)
f=lambda x:4/(1+x**2)
y0=f(x0)
print('y0=',y0)
simpson_fh(x,y0)
得出以下結果:
y0= [4. 3.93846154 3.76470588 3.50684932 3.2 2.87640449 2.56 2.26548673 2. ]
3.141592502458707
綜上可知,在相同的等份數下(輸入的x、y0都是一樣的,僅僅算法不一樣),相比於復化梯形,復化 Simpson更爲精確(因爲上面那個函數的精確積分爲pi)。
Romberg 積分算法
Romberg 積分算法的計算過程如下圖所示:
定義函數
以下便是我定義的函數:
def romberg(x0,f,n):
k=0
xlb=np.zeros((4,n+3))
for i in range(n+3):
xlb[0,i]=tx_fh(x0,f,2**i)
for i in range(n+2):
xlb[1,i+1]=4*xlb[0,i+1]/3-xlb[0,i]/3
for i in range(n+1):
xlb[2,i+2]=16*xlb[1,i+2]/15-xlb[1,i+1]/15
while k<n:
xlb[3,k+3]=64*xlb[2,k+3]/63-xlb[2,k+2]/63
k+=1
k=np.arange(n+3)
xl=np.vstack([k,xlb])
print(xl.T)
print(xlb[3][3:])
return xlb[3][3:]
參數說明
“x0”指的是自變量的定義域。
“f”可以是函數關係或者是確定值。
“n”指的是得到n個Romberg 積分值,也就是得到R1~Rn。
實例運行
x=np.array([0.0000001,1])
f=lambda x:np.sin(x)/x
romberg(x,f,1)
得出以下結果(下列數組對照上一張圖,可知每一個值所對應的數學意義):
[[0. 0.9207354 0. 0. 0. ]
[1. 0.93979319 0.94614578 0. 0. ]
[2. 0.94451342 0.94608683 0.9460829 0. ]
[3. 0.94569076 0.94608321 0.94608297 0.94608297]]
array([0.94608297])
上面這個“array([0.94608297])”就是我們要的結果。
三點 Gauss-Legendre求積算法
三點 Gauss-Legendre求積公式如下圖所示:
定義函數
以下便是我定義的函數:
def gauss_l3(x0,f):
a=x0[0]
b=x0[1]
t1=-(b-a)*0.6**0.5/2+(b+a)/2
t2=(b+a)/2
t3=(b-a)*0.6**0.5/2+(b+a)/2
y=((5*f(t1)+8*f(t2)+5*f(t3))/9)*(b-a)/2
print(y)
return y
參數說明
“x0”指的是自變量的定義域。
“f”指的是函數(以後再實現不是函數,而是確定值的)。
實例運行
x=np.array([0.0000001,1])
f=lambda x:np.sin(x)/x
gauss_l3(x,f)
得出以下結果:
0.9460830340784266
可以看出,相比於Romberg 積分算法,三點 Gauss-Legendre求積算法不用迭代,所以能夠更快的得到答案。
總結
在相同的等份數下,相比於復化梯形,復化 Simpson更爲精確。
相比於Romberg 積分算法,三點 Gauss-Legendre求積算法不用迭代,所以能夠更快的得到答案。
2020.4.2
數學的作業壓力纔是第一生產力!
本來不想加入“確定值”來加強魯棒性,可頂不住數學作業有“確定值”這種題型。