【數學知識】常微分方程的數值求解及python實現1

引言

在工程和科學技術的實際問題中,常需要求解微分方程。不管是本科生參加數學建模競賽還是研究生進行自己的課題研究,常微分方程都是經常出現的。
然而,只有簡單的和典型的微分方程可以求出解析解,而在實際問題中的微分方程往往無法求出解析解。

常微分方程認識

定義與常見形式

凡含有參數,未知函數和未知函數導數 (或微分) 的方程,稱爲微分方程,有時簡稱爲方程,未知函數是一元函數的微分方程稱作常微分方程,未知函數是多元函數的微分方程稱作偏微分方程
常微分方程定義:
F(x,y,y,y,...,y(n))=0 F(x,y,y',y'',...,y^{(n)})=0
在高等數學中我們見過以下常微分方程:

  • 初值問題(1):
    {y=f(x,y)axby(a)=y0 \left\{ \begin{array}{l} y'=f(x,y) \quad a\le x \le b \\ y(a) = y_0 \end{array} \right.

  • 初值問題(2):
    {y=f(x,y,y)axby(a)=y0,y(a)=α \left\{ \begin{array}{l} y''=f(x,y,y') \quad a\le x \le b \\ y(a) = y_0 , y'(a)=\alpha \end{array} \right.

  • 邊值問題
    {y=f(x,y,y)axby(a)=y0,y(b)=yn \left\{ \begin{array}{l} y''=f(x,y,y') \quad a\le x \le b \\ y(a) = y_0 , y'(b)=y_n \end{array} \right.

初值問題(1)的解存在的條件

  • 存在和唯一性定理:如果連續函數f(x,y)關於y滿足Lipschitz條件,即:
    \exists正數LL,使得x[a,b]\forall x \in [a,b],均有
    f(x,y1)f(x,y2)Ly1y2|f(x,y_1)-f(x,y_2)|\le L|y_1-y_2|
    則初值問題(1)的解存在且唯一

初值問題(1)的數值解

對於初值問題(1):
{y=f(x,y)axby(a)=y0 \left\{ \begin{array}{l} y'=f(x,y) \quad a\le x \le b \\ y(a) = y_0 \end{array} \right.
要求它的數值解,就是求未知函數y(x)y(x)在區間[a,b][a,b]上的一系列離散點(節點)
a=x0x1x2xn=b a = x_0 \le x_1 \le x_2 \le \cdots \le x_n = b
上函數值y(xk)y(x_k)的近似值yk(k=1,2,,n)y_k(k=1,2,\cdots,n),這就是初值問題(1)的數值解。
求它的數值解的關鍵在於y(x)y'(x)的數值計算問題或者它的等價的積分方程y(x)=y0+axf(t,y(t))dxy(x) = y_0 +\int_{a}^{x}f(t,y(t)) dx的數值計算問題。

求解微分方程的數值方法分爲:數值微分和數值積分兩種方法。

實際應用中,通常取求解區間[a,b][a,b]的等分點作爲離散點,即
xk=a+kh,k=0,1,,n,whereh=nba x_k = a + kh, \quad k = 0,1,\cdots,n, where \quad h = \frac{n}{b-a}

推導初值問題的數值方法的途徑:

  • Talor展開
  • 利用差商離散導數
  • 利用數值積分方法

求初值問題數值解的方法是步進法,即從已知的初值y0y_0出發,通過一定的計算求y1y_1,然後由y1y_1y0y_0y1y_1求出y2y_2,依次計算到yny_n,這時,可以分爲

  • 單步法:只利用yky_k來計算yk+1y_{k+1}
  • 多步法:計算yk+1y_{k+1}時,除了利用yky_k,還需要用到已算出的若干ykjy_{k-j}(j=1,2,,l1j=1,2,\cdots,l-1),並稱爲ll步法

常微分方程數值求解方法

歐拉(尤拉) (Euler) 公式推導

方法一:利用Taylor展開

因爲
xk+1xk=h x_{k+1}-x_k=h


y(xk+1)=y(xk+h) y(x_{k+1})=y(x_k+h)

可將y(xk+1)=y(xk+h)y(x_{k+1})=y(x_k+h)按照1階泰勒公式展開,得
y(xk+1)=y(xk)+11!y(xk)(xk+1xk) y(x_{k+1})=y(x_k)+\frac{1}{1!}y'(x_k)(x_{k+1}-x_k)

也就是

y(xk+1)=y(xk)+hy(xk) y(x_{k+1})=y(x_k)+hy'(x_k)
又根據初值條件
{y=f(x,y)axby(a)=y0 \left\{ \begin{array}{l} y'=f(x,y) \quad a\le x \le b \\ y(a) = y_0 \end{array} \right.

可以得到數值解序列{yk}\{y_k\}的計算遞推公式:
{y0=y(a)yk+1=yk+hf(xk,yk),k=0,1,,n1 \left\{ \begin{array}{l} y_0 = y(a) \\ y_{k+1}=y_k+hf(x_k,y_k), k = 0,1,\cdots , n-1 \end{array} \right.

方法二:利用差商離散導數

首先,導數的定義爲:
f(x)=f(x)f(xΔx)Δx f'(x)=\frac{f(x)-f(x-\Delta x)}{\Delta x}

我們對xkx_k處的導數用差商來近似代替,如向前差商:
f(xk)y(xk+1)y(xk)h f'(x_k) \approx \frac{y(x_{k+1})-y(x_k)}{h}

則微分方程初值問題可以化爲:
{y(xk+1)y(xk)hf(xk,y(xk)),k=0,1,,n1y(a)=y0 \begin{cases} \frac{y(x_{k+1})-y(x_k)}{h} \approx f(x_k,y(x_k)) , k = 0,1,\cdots , n-1 \\ y(a) = y_0 \end{cases}

將近似號改爲等號,精確解y(xk)y(x_k)改爲近似序列yky_k,滿足:
{y0=y(a)yk+1=yk+hf(xk,yk),k=0,1,,n1 \left\{ \begin{array}{l} y_0 = y(a) \\ y_{k+1}=y_k+hf(x_k,y_k), k = 0,1,\cdots , n-1 \end{array} \right.

第三種方法——利用數值積分方法這裏就不介紹了
總的來說,上面推導的結果,就是歐拉(尤拉) (Euler) 公式,這裏重寫一遍:
{y0=y(a)yk+1=yk+hf(xk,yk),k=0,1,,n1 \left\{ \begin{array}{l} y_0 = y(a) \\ y_{k+1}=y_k+hf(x_k,y_k), k = 0,1,\cdots , n-1 \end{array} \right.

歐拉(尤拉) (Euler) 公式及其變形/改進

上一節詳細推到了歐拉(尤拉) (Euler) 公式,這裏介紹它的幾種不同的形式:

  • 顯式歐拉公式:原始的歐拉公式
    {y0=y(a)yk+1=yk+hf(xk,yk),k=0,1,,n1 \left\{ \begin{array}{l} y_0 = y(a) \\ y_{k+1}=y_k+hf(x_k,y_k), k = 0,1,\cdots , n-1 \end{array} \right.

  • 隱式歐拉公式:使用向後差商代替導數得到
    {y0=y(a)yk+1=yk+hf(xk+1,yk+1),k=0,1,,n1 \left\{ \begin{array}{l} y_0 = y(a) \\ y_{k+1}=y_k+hf(x_{k+1},y_{k+1}), k = 0,1,\cdots , n-1 \end{array} \right.

  • 梯形公式:將顯式歐拉公式和隱式歐拉公式作算術平均
    {y0=y(a)yk+1=yk+h2[f(xk,yk)+f(xk+1,yk+1)],k=0,1,,n1 \left\{ \begin{array}{l} y_0 = y(a) \\ y_{k+1}=y_k+\frac{h}{2}[f(x_{k},y_{k})+f(x_{k+1},y_{k+1})], k = 0,1,\cdots , n-1 \end{array} \right.

  • 預估-校正Euler方法

即首先計算出初步的近似值yk+1\overline{y_{k+1}}:
yk+1=yk+hf(xk,yk) \overline{y_{k+1}}=y_k+hf(x_k,y_k)

然後用該預估值替代梯形公式右端中的yk+1y_{k+1}進行計算,從而得到最後的校正值yk+1y_{k+1}
yk+1=yk+h2[f(xk,yk)+f(xk+1,yk+1)] y_{k+1}=y_k+\frac{h}{2}[f(x_{k},y_{k})+f(x_{k+1},\overline{y_{k+1}})]

歐拉(尤拉) (Euler) 公式Python實現

導入包,代碼如下:

from pylab import *
import warnings
warnings.filterwarnings('ignore')
import matplotlib.pyplot as plt
%matplotlib inline

定義函數euler_xianshi(f,a=0,b=1,ya=1,h=0.1,print_result=True)使用顯式歐拉公式進行微分方程數值計算代碼如下:

def euler_xianshi(f,a=0,b=1,ya=1,h=0.1,print_result=True):
    '''顯式歐拉公式, Explicit Euler formula
    f:需要求解的微分方程,y'=f(x,y)
        f的格式:
        def f(x,y):
            ...
            return dy
    a:求解區間起始值
    b:求解區間終止值
    ya:起始條件,ya=y(a)
    h:求解步長(區間[a,b]n等分)
    print_result:顯示顯式歐拉公式的每一步計算結果
    Returns
    ----------
    res:返回顯式歐拉公式的求解結果
    '''
    res = []
    xi = a 
    yi = ya
    while xi<=b: # 在求解區間範圍
        y = yi + h*f(xi,yi)
        if print_result:
            print('xi:{:.2f}, yi:{:.6f}'.format(xi,yi))
        res.append(y)
        xi, yi = xi+h, y
    
    return res

例1:求解下列微分方程

{y=xy+10x1y(0)=1 \left\{ \begin{array}{l} y'= x-y+1 \quad 0\le x \le 1 \\ y(0) = 1 \end{array} \right.

  • 第1步:定義函數表示求解的微分方程
def f(x,y):
    '''
    求解的微分方程:y'=x-y+1
    '''
    return x-y+1
  • 第2步:應用前面定義的euler_xianshi函數進求解
a=0 # 求解區間起點
b=1 # 求解區間終點
y0=1    # 初值條件
h=0.05  # 步長
y = euler_xianshi(f,a,b,y0,h,print_result=True)
  • 第3步:計算真實值
import math
import numpy as np
def fun_y(x):
    return x+np.exp(-x)

x_true = np.arange(a,b,0.1*h)
y_true = fun_y(x_true)
  • 第4步:比較數值計算結果和真實值
x = np.arange(a,b,h)
plt.plot(x_true,y_true,label="true", color="red")
plt.scatter(x,y,label="predict")
plt.legend()

結果如下:
在這裏插入圖片描述

【作者簡介】陳藝榮,男,目前在華南理工大學電子與信息學院廣東省人體數據科學工程技術研究中心攻讀博士,擔任IEEE Access、IEEE Photonics Journal的審稿人。兩次獲得美國大學生數學建模競賽(MCM)一等獎,獲得2017年全國大學生數學建模競賽(廣東賽區)一等獎、2018年廣東省大學生電子設計競賽一等獎等科技競賽獎項,主持一項2017-2019年國家級大學生創新訓練項目獲得優秀結題,參與兩項廣東大學生科技創新培育專項資金、一項2018-2019年國家級大學生創新訓練項目獲得良好結題,發表SCI論文4篇,授權實用新型專利8項,受理髮明專利13項。
我的主頁
我的Github
我的CSDN博客
我的Linkedin

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