常微分方程初值问题的数值积分法是一种通过数值方法求解给定初始条件下的常微分方程(Ordinary Differential Equations, ODEs)的问题。
,其中
是定义域的起始点,
是初始值。
分割为若干小步,即选择合适的步长
。通常,较小的步长能够提高数值解的精度,但也增加计算成本。
其中,
是第
步的函数值,
是步长,
是在点
处的导数。
这些方法中,步长
是一个关键参数,它决定了离散化的程度,选择合适的步长对于数值解的准确性和稳定性非常重要。
【计算方法与科学建模】常微分方程初值问题的数值积分法:欧拉方法(向前Euler及其python实现)
处,通过向前差商
近似替代微分方程
中的导数项,得到
向后 Euler 方法的核心思想是从微分方程的
出发,使用向后差商
近似微商
,然后通过这个近似来得到递推公式。具体而言,递推公式为:
这里,
是在
处的近似解,
是步长。
对比向前 Euler 方法和向后 Euler 方法,可以注意到两者的关键区别:
。
出现在方程的右侧,需要通过求解非线性方程来获得。
,其中
是迭代的初值。
计算
的逼近值。
的近似解。
向后 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()