对给定微分方程
{ y ′ = f ( x , y ) y ( x 0 ) = y 0 (1) \begin{cases} y' = f(x,y)\\ y(x_0) = y_0 \end{cases}\tag{1} {y′=f(x,y)y(x0)=y0(1)
在xn到xn+1上积分,得到
y n + 1 = y n + ∫ x n x n + 1 f ( t , y ( t ) ) d t (2) y_{n+1} = y_n + \int_{x_n}^{x_{n+1}}f(t,y(t))dt \tag{2} yn+1=yn+∫xnxn+1f(t,y(t))dt(2)
如果用左矩形公式做了个(2)式中积分的近似,那可以得到欧拉公式。
y n + 1 = y n + h f ( x n , y n ) (3) y_{n+1} = y_n +hf(x_n,y_n) \tag{3} yn+1=yn+hf(xn,yn)(3)
但用左矩形公式,结果当然是不够好的,现用梯形方法替代等式(2)式中积分,得到
y n + 1 = y n + h 2 ( f ( x n , y n ) + f ( x n + 1 , y n + 1 ) ) (4) y_{n+1} = y_n +\frac{h}{2}(f(x_n,y_n)+f(x_{n+1},y_{n+1}))\tag{4} yn+1=yn+2h(f(xn,yn)+f(xn+1,yn+1))(4)
可以看到等式右边出现了一项 f ( x n + 1 , y n + 1 ) f(x_{n+1},y_{n+1}) f(xn+1,yn+1),里面也有一项需要求的目标值 y n + 1 y_{n+1} yn+1,为使计算简单,将这一项 f f f里的 y n + 1 y_{n+1} yn+1用旧欧拉方法,也就是(3)式得到的值(避免混淆可以记为 y p y_p yp)代入,上等式的结果记为 y c y_c yc。最终可以采取平均的形式
{ y p = y n + h f ( x n , y n ) y c = y n + h f ( x n + 1 , y p ) y n + 1 = ( y p + y c ) / 2 (5) \begin{cases} y_{p} = y_n +hf(x_n,y_n) \\ y_{c} = y_n +hf(x_{n+1},y_p)\\ y_{n+1} = (y_p +y_c)/2 \end{cases}\tag{5} ⎩⎪⎨⎪⎧yp=yn+hf(xn,yn)yc=yn+hf(xn+1,yp)yn+1=(yp+yc)/2(5)
综上总结,改进欧拉方法就是旧欧拉方法和梯形近似积分的结合。
(题目来源于李庆杨《数值分析》计算实习题)
给定初值问题
{ y ′ = 1 x 2 − y x , x ∈ [ 1 , 2 ] y ( 1 ) = 1 \begin{cases} y' = \frac{1}{x^2} - \frac{y}{x} , x\in [1,2]\\ y(1) = 1 \end{cases} {y′=x21−xy,x∈[1,2]y(1)=1
用欧拉方法和改进欧拉法(h = 0.05)求 x ∈ [ 1 , 2 ] x \in [1,2] x∈[1,2]的数值解。
import sympy as sympy
class Euler: #欧拉算法类
def __init__(self, h, x0, y0, f ,a,b): #h为步长,x0和y0为初值,f为微分方程y' = f(x,y),ab为所要计算的区间
self.h = h
self.y0 = y0
self.x0 = x0
self.n = int((b-a)/h)
self.f = f
def euler(self):
Y = [] #存储y值
X = [] #存储x值
Y.append(self.y0)
X.append(self.x0)
for i in range(self.n):
y_euler = Y[i] + self.h*self.f.subs([(x,self.x0),(y,Y[i])]) #欧拉方法值
self.x0 = self.x0+self.h #注意这里都要用self.
Y.append(y_euler)
X.append(self.x0)
return X,Y
class ProEuler: #改进欧拉类
def __init__(self, h, x0, y0, f ,a,b): #h为步长,x0和y0为初值,f为微分方程y' = f(x,y),ab为所要计算的区间
self.h = h
self.y0 = y0
self.x0 = x0
self.n = int((b-a)/h)
self.f = f
def euler(self):
Y = [] #存储y值
X = [] #存储x值
Y.append(self.y0)
X.append(self.x0)
for i in range(self.n):
y_euler1 = Y[i] + self.h*self.f.subs([(x,self.x0),(y,Y[i])]) #欧拉方法得到第一个y_n+1值
self.x0 = self.x0+self.h #注意这里都要用self.
y_euler2 = Y[i] + self.h*self.f.subs([(x,self.x0),(y,y_euler1)]) #梯形公式得到第二个y_n+1'的值
y_pro_euler = (y_euler1+y_euler2)/2 #两者相加平均值,就是改进欧拉方法的结果
Y.append(y_pro_euler)
X.append(self.x0)
return X,Y
x,y = sympy.symbols("x y")
f = 1/(x**2)-y/x #在此修改函数表达式
t1 = Euler(0.05,1,1,f,1,2) #欧拉方法实例
x1,y1 = t1.euler()
t2 = ProEuler(0.05,1,1,f,1,2) #改进欧拉方法实例
x2,y2 = t2.euler()
for i in range(len(x1)):
print("xi:","%.2f"%x2[i], " y_euler:", "%.7f"%y1[i]," y_proeuler:","%.7f"%y2[i])
输出结果
xi: 1.00 y_euler: 1.0000000 y_proeuler: 1.0000000
xi: 1.05 y_euler: 1.0000000 y_proeuler: 0.9988662
xi: 1.10 y_euler: 0.9977324 y_proeuler: 0.9957694
xi: 1.15 y_euler: 0.9937033 y_proeuler: 0.9911415
xi: 1.20 y_euler: 0.9883060 y_proeuler: 0.9853210
xi: 1.25 y_euler: 0.9818488 y_proeuler: 0.9785748
xi: 1.30 y_euler: 0.9745748 y_proeuler: 0.9711148
xi: 1.35 y_euler: 0.9666770 y_proeuler: 0.9631101
xi: 1.40 y_euler: 0.9583090 y_proeuler: 0.9546959
xi: 1.45 y_euler: 0.9495938 y_proeuler: 0.9459812
xi: 1.50 y_euler: 0.9406304 y_proeuler: 0.9370539
xi: 1.55 y_euler: 0.9314983 y_proeuler: 0.9279848
xi: 1.60 y_euler: 0.9222616 y_proeuler: 0.9188316
xi: 1.65 y_euler: 0.9129722 y_proeuler: 0.9096406
xi: 1.70 y_euler: 0.9036719 y_proeuler: 0.9004497
xi: 1.75 y_euler: 0.8943943 y_proeuler: 0.8912892
xi: 1.80 y_euler: 0.8851667 y_proeuler: 0.8821837
xi: 1.85 y_euler: 0.8760109 y_proeuler: 0.8731530
xi: 1.90 y_euler: 0.8669441 y_proeuler: 0.8642129
xi: 1.95 y_euler: 0.8579802 y_proeuler: 0.8553758
xi: 2.00 y_euler: 0.8491299 y_proeuler: 0.8466517