數值積分 python代碼實現

老規矩,數學原理什麼的就不寫了。

直接貼代碼和實例演示,以下代碼基於python和numpy。

在這裏,我將用代碼實現復化梯形算法復化 Simpson 算法Romberg 積分算法三點 Gauss-Legendre求積算法

往期博客:

線性方程組的迭代法 python代碼實現

函數插值法之牛頓插值法 python代碼實現

復化梯形算法

復化梯形公式如下圖所示:

復化梯形公式

首先

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 公式如下圖所示:

復化 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 積分算法的計算過程如下圖所示:

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求積公式如下圖所示:

三點 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

數學的作業壓力纔是第一生產力!

本來不想加入“確定值”來加強魯棒性,可頂不住數學作業有“確定值”這種題型。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章