常微分方程数值求解【python】
簡述
這里只考慮最為簡單的一種常微分方程
dydx=f(x,y)\frac{dy}{dx} = f(x,y)dxdy?=f(x,y)
然后這里的實(shí)例都是以下面這個方程來做展示的。
dydx=y?(1?y2)\frac{dy}{dx} = y*(1-y^2)dxdy?=y?(1?y2)
初值給定
y(0)=2y(0) = 2y(0)=2
這個方程的精確解結(jié)果是下面這個方程
y(x)=4?e2x4?e2x?3y(x) = \sqrt{\frac{4*e^{2x}}{4*e^{2x}-3}}y(x)=4?e2x?34?e2x??
文章目錄
- 簡述
- 歐拉公式求解
- 簡單的理論推理
- 代碼實(shí)現(xiàn)
- 實(shí)現(xiàn)后的效果
- 代碼
- 誤差畫圖
- 誤差畫圖代碼
- 改進(jìn)版歐拉公式
- 理解這個公式
- 改進(jìn)版本的畫圖
- 歐拉算法和改進(jìn)版歐拉算法的比較
- 加上絕對值再來看
- 累積誤差和分步的誤差
- 圖像
- 代碼
歐拉公式求解
歐拉公式非常簡潔。(歐拉果然大佬!!!)
yn+1=yn+h?f(xn,yn)xn=x0+n?hy_{n+1} = y_n + h*f(x_n, y_n)\\ x_n=x_0+n*hyn+1?=yn?+h?f(xn?,yn?)xn?=x0?+n?h
- h是步長
簡單的理論推理
其實(shí)非常直觀,將上面的第一個式子變形一下,有
dydx=f(x,y)=yn+1?ynh\frac{dy}{dx} = f(x,y)=\frac{y_{n+1}- y_n}{h}dxdy?=f(x,y)=hyn+1??yn??
是不是非常熟悉,
代碼實(shí)現(xiàn)
實(shí)現(xiàn)后的效果
| 0 | 2.0 | 2 | 0.0 |
| 0.1 | 1.6096571705090292 | 1.4 | 0.20965717050902932 |
| 0.2 | 1.4181045558702239 | 1.2656 | 0.15250455587022382 |
| 0.3 | 1.303667649645571 | 1.1894433603584 | 0.11422428928717099 |
| 0.4 | 1.2281238419433613 | 1.1401081630148027 | 0.08801567892855866 |
| 0.5 | 1.175177856120688 | 1.1059224047188059 | 0.06925545140188216 |
| 0.6 | 1.1365806251915203 | 1.0812532167953721 | 0.05532740839614814 |
| 0.7 | 1.1076620270914186 | 1.0629683037980915 | 0.0446937232933271 |
| 0.8 | 1.085560957285936 | 1.0491601738751934 | 0.03640078341074249 |
| 0.9 | 1.0684188538447474 | 1.0385912416407315 | 0.029827612204015974 |
| 1 | 1.0549729219451955 | 1.0304204608015664 | 0.02455246114362919 |
代碼
import numpy as np# dy/dx = f(x,y) # Euler formula f = lambda x: x * (1 - x ** 2) F = lambda x: ((4 * np.exp(2 * x) / (4 * np.exp(2 * x) - 3))) ** 0.5 x = 0 y = 2 h = 0.1 while x <= 1:loss = F(x) - yprint(x, '|', F(x), '|', y, '|', loss)y = y + h * f(y)x += 0.1誤差畫圖
誤差畫圖代碼
import numpy as np import matplotlib.pyplot as plt# dy/dx = f(x,y) # Euler formula f = lambda x: x * (1 - x ** 2) F = lambda x: ((4 * np.exp(2 * x) / (4 * np.exp(2 * x) - 3))) ** 0.5 x = 0 y = 2 h = 0.1 losses = [] while x <= 1:loss = F(x) - y# print(x, '|', F(x), '|', y, '|', loss)losses.append(loss)y = y + h * f(y)x += 0.1plt.plot(np.arange(0, 1.1, 0.1), losses) plt.plot(np.arange(0, 1.1, 0.1), losses, 'r*') plt.show()改進(jìn)版歐拉公式
之前提到的歐拉公式簡單,容易實(shí)現(xiàn),但是效率卻有點(diǎn)低。
所以有歐拉公式的改進(jìn)版本。
yˉn+1=yn+h?f(xn,yn)yn+1=yn+h2?(f(xn,yn)+f(xn+1,yˉn+1))\bar y_{n+1} = y_n + h * f(x_n, y_n)\\ y_{n+1} = y_n + \frac{h}{2} * (f(x_n, y_n) + f(x_{n+1}, \bar y_{n+1}))yˉ?n+1?=yn?+h?f(xn?,yn?)yn+1?=yn?+2h??(f(xn?,yn?)+f(xn+1?,yˉ?n+1?))
x的話,任然是按照步長移動的,所以,這里就不作贅述了。
理解這個公式
書上的話,是用定積分的方式推理出來的。
但是這個具體含義,也沒有給出什么東西來。
但是實(shí)際上,這個公式的邏輯含義是非常漂亮的!
將上面的兩個公式聯(lián)立起來。
yn+1?ynh=12?(yˉn+1?ynh+f(xn+1,yˉn+1))\frac{y_{n+1}-y_n}{h} = \frac{1}{2}* (\frac{\bar y_{n+1} -y_n}{h} + f(x_{n+1}, \bar y_{n+1}))hyn+1??yn??=21??(hyˉ?n+1??yn??+f(xn+1?,yˉ?n+1?))
之前的歐拉方法是用這里的 yˉn+1?ynh\frac{\bar y_{n+1} - y_n}{h}hyˉ?n+1??yn??來近似dydx\frac{dy}{dx}dxdy?
而現(xiàn)在我們逼近的其實(shí)是,用歐拉公式得到的導(dǎo)數(shù),和實(shí)際上的在該點(diǎn)的導(dǎo)數(shù)值的均值。
從這個角度上來看,其實(shí)會是更貼近于正確的結(jié)果。
- 注意到: 我們這里用了yn+1y_{n+1}yn+1?或者說是yny_nyn?是正確解。
- 用這個其實(shí)是合理的,因?yàn)?#xff0c;這里的算法是基于之前的歐拉算法的,而歐拉算法本來就是會近似逼近正確的結(jié)果。所以,yny_nyn?本身就是會逐漸接近于正確的結(jié)果。所以之前的假設(shè)是合理的。
- 而且,留心到,這是在原來的基礎(chǔ)上,做了新的加速的過程。
歐拉公式的預(yù)估值,和代入其的校正值的平均值,就是改進(jìn)歐拉方法的計(jì)算結(jié)果
改進(jìn)版本的畫圖
import numpy as np import matplotlib.pyplot as plt# dy/dx = f(x,y) # Advanced Euler formula f = lambda x: x * (1 - x ** 2) F = lambda x: ((4 * np.exp(2 * x) / (4 * np.exp(2 * x) - 3))) ** 0.5 x = 0 y = 2 h = 0.1 losses = [] while x <= 1:loss = F(x) - y# print(x, '|', F(x), '|', y, '|', loss)losses.append(loss)y1 = y + h * f(y)y2 = y + h * f(y1)y = (y1+y2)/2x += 0.1plt.plot(np.arange(0, 1.1, 0.1), losses) plt.plot(np.arange(0, 1.1, 0.1), losses, 'r*') plt.show()歐拉算法和改進(jìn)版歐拉算法的比較
import numpy as np import matplotlib.pyplot as plt# dy/dx = f(x,y) # Advanced Euler formula f = lambda x: x * (1 - x ** 2) F = lambda x: ((4 * np.exp(2 * x) / (4 * np.exp(2 * x) - 3))) ** 0.5 x = 0 y = 2 h = 0.1def AdvancedEular():x = 0y = 2h = 0.1losses = []while x <= 1:loss = F(x) - y# print(x, '|', F(x), '|', y, '|', loss)losses.append(loss)y1 = y + h * f(y)y2 = y + h * f(y1)y = (y1 + y2) / 2x += 0.1return lossesdef Eular():x = 0y = 2h = 0.1losses = []while x <= 1:loss = F(x) - y# print(x, '|', F(x), '|', y, '|', loss)losses.append(loss)y = y + h * f(y)x += 0.1return lossesadEl = AdvancedEular() El = Eular() plt.plot(np.arange(0, 1.1, 0.1), adEl, label='Advance Eular') plt.plot(np.arange(0, 1.1, 0.1), adEl, 'r*')plt.plot(np.arange(0, 1.1, 0.1), El, label='Eular') plt.plot(np.arange(0, 1.1, 0.1), El, 'b*') plt.legend() plt.show()加上絕對值再來看
import numpy as np import matplotlib.pyplot as plt# dy/dx = f(x,y) # Advanced Euler formula f = lambda x: x * (1 - x ** 2) F = lambda x: ((4 * np.exp(2 * x) / (4 * np.exp(2 * x) - 3))) ** 0.5 x = 0 y = 2 h = 0.1def AdvancedEular():x = 0y = 2h = 0.1losses = []while x <= 1:loss = F(x) - y# print(x, '|', F(x), '|', y, '|', loss)losses.append(abs(loss))y1 = y + h * f(y)y2 = y + h * f(y1)y = (y1 + y2) / 2x += 0.1return lossesdef Eular():x = 0y = 2h = 0.1losses = []while x <= 1:loss = F(x) - y# print(x, '|', F(x), '|', y, '|', loss)losses.append(abs(loss))y = y + h * f(y)x += 0.1return lossesadEl = AdvancedEular() El = Eular() plt.plot(np.arange(0, 1.1, 0.1), adEl, label='Advance Eular') plt.plot(np.arange(0, 1.1, 0.1), adEl, 'r*')plt.plot(np.arange(0, 1.1, 0.1), El, label='Eular') plt.plot(np.arange(0, 1.1, 0.1), El, 'b*') plt.legend() plt.show()累積誤差和分步的誤差
前面的話,我們是用一個不那么正確的結(jié)果來放到公式中來推導(dǎo)出正確的結(jié)果。下面我們嘗試每一步都用正確的結(jié)果來推導(dǎo)出對應(yīng)y值。
| 0 | 2.0 | 2 | 0.0 |
| 0.1 | 1.6096571705090292 | 1.4 | 0.20965717050902932 |
| 0.2 | 1.4181045558702239 | 1.35356132529304 | 0.06454323057718381 |
| 0.30000000000000004 | 1.303667649645571 | 1.274731273707409 | 0.028936375938162007 |
| 0.4 | 1.2281238419433613 | 1.2124696651611984 | 0.015654176782162965 |
| 0.5 | 1.175177856120688 | 1.1656997597866852 | 0.009478096334002872 |
| 0.6 | 1.1365806251915203 | 1.1303985272996449 | 0.006182097891875404 |
| 0.7 | 1.1076620270914186 | 1.103413438852542 | 0.004248588238876527 |
| 0.7999999999999999 | 1.085560957285936 | 1.082527495787655 | 0.003033461498280987 |
| 0.8999999999999999 | 1.0684188538447474 | 1.0661899261885104 | 0.0022289276562370564 |
| 0.9999999999999999 | 1.0549729219451955 | 1.0532987133870213 | 0.0016742085581742394 |
圖像
代碼
import numpy as np import matplotlib.pyplot as plt# dy/dx = f(x,y) # Euler formula f = lambda x: x * (1 - x ** 2) F = lambda x: ((4 * np.exp(2 * x) / (4 * np.exp(2 * x) - 3))) ** 0.5 x = 0 y = 2 h = 0.1def stepEular():x = 0y = 2h = 0.1losses = []while x <= 1:ty = F(x)loss = ty - yprint(x, '|', ty, '|', y, '|', loss)losses.append(abs(loss))y = ty + h * f(ty)x += 0.1return lossesdef Eular():x = 0y = 2h = 0.1losses = []while x <= 1:loss = F(x) - y# print(x, '|', F(x), '|', y, '|', loss)losses.append(abs(loss))y = y + h * f(y)x += 0.1return lossesadEl = stepEular() El = Eular() plt.plot(np.arange(0, 1.1, 0.1), adEl, label='Step Eular') plt.plot(np.arange(0, 1.1, 0.1), adEl, 'r*')plt.plot(np.arange(0, 1.1, 0.1), El, label='Eular') plt.plot(np.arange(0, 1.1, 0.1), El, 'b*') plt.legend() plt.show()總結(jié)
以上是生活随笔為你收集整理的常微分方程数值求解【python】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OpenMP在Windows下用VS使用
- 下一篇: 【mysql】已经创建表后,修改某列的默