常微分方程初值问题的数值积分法是一种通过数值方法求解给定初始条件下的常微分方程(Ordinary Differential Equations, ODEs)的问题。
确定微分方程:
确定初始条件:
选择数值方法:
离散化定义域:
数值迭代:
判断停止条件:
输出结果:
欧拉方法(Euler Method):
改进的欧拉方法(Improved Euler Method 或梯形法 Trapezoidal Rule):
Runge-Kutta 方法:
这些方法中,步长 h h h 是一个关键参数,它决定了离散化的程度,选择合适的步长对于数值解的准确性和稳定性非常重要。
【计算方法与科学建模】常微分方程初值问题的数值积分法:欧拉方法(向前Euler及其python实现)
向后 Euler 方法的核心思想是从微分方程的 y ′ ( X n + 1 ) = f ( x n + 1 , y ( X n + 1 ) ) y'(X_{n+1}) = f(x_{n+1}, y(X_{n+1})) y′(Xn+1)=f(xn+1,y(Xn+1)) 出发,使用向后差商 y ( X n + 1 ) − y ( X n ) h \frac{y(X_{n+1}) - y(X_n)}{h} hy(Xn+1)−y(Xn) 近似微商 y ′ ( X n + 1 ) y'(X_{n+1}) y′(Xn+1),然后通过这个近似来得到递推公式。具体而言,递推公式为:
y n + 1 = y n + h f ( X n + 1 , y n + 1 ) , n = 0 , 1 , … y_{n+1} = y_n + hf(X_{n+1}, y_{n+1}), \quad n = 0, 1, \ldots \ yn+1=yn+hf(Xn+1,yn+1),n=0,1,…
这里, y n + 1 y_{n+1} yn+1 是在 X n + 1 X_{n+1} Xn+1 处的近似解, h h h 是步长。
对比向前 Euler 方法和向后 Euler 方法,可以注意到两者的关键区别:
显式 vs. 隐式:
求解方式:
具体的迭代过程:
初始值:使用向前 Euler 公式给出一个初值,例如 y n + 1 ( 0 ) = y n + h f ( x n + 1 , y n ) y_{n+1}^{(0)} = y_n + hf(x_{n+1}, y_n) yn+1(0)=yn+hf(xn+1,yn),其中 y n + 1 ( 0 ) y_{n+1}^{(0)} yn+1(0) 是迭代的初值。
迭代公式:使用迭代公式 y n + 1 ( k + 1 ) = y n + h f ( x n + 1 , y n + 1 ( k ) ) , k = 0 , 1 , … y_{n+1}^{(k+1)} = y_n + hf(x_{n+1}, y_{n+1}^{(k)}), \quad k = 0, 1, \ldots yn+1(k+1)=yn+hf(xn+1,yn+1(k)),k=0,1,…计算 y n + 1 y_{n+1} yn+1 的逼近值。
重复迭代,直到满足收敛条件,得到 y n + 1 y_{n+1} yn+1 的近似解。
向后 Euler 方法在处理某些问题(例如刚性问题)时可能更为稳定,但由于涉及隐式方程的求解,其计算成本可能较高。
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import fsolve
def forward_euler(f, y0, a, b, h):
"""
使用向前欧拉法求解一阶常微分方程初值问题
Parameters:
- f: 函数,表示微分方程的右侧项,形式为 f(x, y)
- y0: 初始条件,表示在 x=a 处的函数值
- a: 区间起点
- b: 区间终点
- h: 步长
Returns:
- x_values: 区间 [a, b] 上的离散节点
- y_values: 对应节点上的函数值的近似解
"""
num_steps = int((b - a) / h) + 1 # 计算步数
x_values = np.linspace(a, b, num_steps) # 生成离散节点
y_values = np.zeros(num_steps) # 初始化结果数组
y_values[0] = y0 # 设置初始条件
# 使用向前欧拉法进行逐步迭代
for i in range(1, num_steps):
x = x_values[i - 1]
y = y_values[i - 1]
y_values[i] = y + h * f(x, y)
return x_values, y_values
def backward_euler(f, y0, a, b, h):
"""
使用向后欧拉法求解一阶常微分方程初值问题
Parameters:
- f: 函数,表示微分方程的右侧项,形式为 f(x, y)
- y0: 初始条件,表示在 x=a 处的函数值
- a: 区间起点
- b: 区间终点
- h: 步长
Returns:
- x_values: 区间 [a, b] 上的离散节点
- y_values: 对应节点上的函数值的近似解
"""
num_steps = int((b - a) / h) + 1 # 计算步数
x_values = np.linspace(a, b, num_steps) # 生成离散节点
y_values = np.zeros(num_steps) # 初始化结果数组
y_values[0] = y0 # 设置初始条件
# 使用向后欧拉法进行逐步迭代
for i in range(1, num_steps):
x = x_values[i]
# 定义非线性方程
equation = lambda y_next: y_next - y_values[i - 1] - h * f(x, y_next)
# 利用 fsolve 求解非线性方程,得到 y_values[i]
y_values[i] = fsolve(equation, y_values[i - 1])[0]
return x_values, y_values
# 示例:求解 y' = y -2x/y,初始条件 y(0) = 1 在区间 [0, 1] 上的近似解
def example_function(x, y):
return y - 2 * x / y
a, b = 0, 1 # 区间 [a, b]
y0 = 1 # 初始条件 y(0) = 1
h = 0.05 # 步长
x_values0, y_values0 = forward_euler(example_function, y0, a, b, h)
x_values, y_values = backward_euler(example_function, y0, a, b, h)
# 绘制结果
plt.plot(x_values0, y_values0, label='Forward Euler')
plt.plot(x_values, np.sqrt(1 + 2 * x_values), label='Exact Solution')
plt.plot(x_values, y_values, label='Backward Euler')
plt.title('h = {}'.format(h))
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.show()