Euler 法
一、直观理解:顺着当前切线走一小步
Euler 法是求解常微分方程初值问题最直观的数值方法。对方程
真实解在点 的切线斜率为 。Euler 法假设在一个小时间步 内斜率不变,于是沿切线走到下一点:
可以把它想象成登山时只看脚下坡度:每一步都按“此刻坡度”前进固定距离。步子够小,路线还能大致贴合山势;步子太大,就会严重偏离真实路径。
Euler 法的价值主要有三点:
- 教学与推导:几乎所有更高阶方法都可以看成对“只用起点斜率”的改进;
- 快速基线:实现极简单,便于检查方程右端、初值与量纲是否写对;
- 理论对照:全局误差 的收敛阶清晰,适合做步长减半实验。
在正式建模中,Euler 很少作为最终高精度求解器,但常作为“最低成本可运行原型”和误差分析的参照。
二、适用场景与优缺点
| 类型 | 说明 |
|---|---|
| 适用场景 | 需要快速验证 ODE 模型能否跑通;教学演示收敛阶;非刚性、短时间、精度要求不高的粗算;为多步法提供最粗糙的启动对照;手写算法以便完全控制每一步。 |
| 核心优点 | 公式简单、内存占用极小;每步只计算一次 ;易于扩展到方程组与向量形式;便于理解局部截断误差与稳定性概念。 |
| 局限与缺点 | 全局精度仅一阶,同样步长下误差通常远大于 RK4;稳定域小,刚性方程需要极小步长;长时间积分误差累积明显;显式 Euler 对振荡问题可能人为放大振幅。 |
使用前应确认:
- 问题可写成一阶系统(高阶方程先降阶);
- 在积分区间上足够光滑,或至少分段可积;
- 已估计解的变化时间尺度,从而给出合理的 量级;
- 准备用解析解、守恒量或步长减半做验证,而不是只看一张图。
三、核心原理:局部截断误差与全局误差
1. 递推公式
设节点 。显式 Euler:
对方程组 ,形式完全相同,只是 变为向量。
2. 误差阶
由 Taylor 展开,
Euler 用 代替真实增量,局部截断误差为 。经过 步累积后,全局误差一般为 :
因此步长减半,全局误差大约减半——这是检验实现是否正确的标准实验。
3. 稳定性直观
对模型方程 (),Euler 要求 才绝对稳定。若 很大(刚性), 必须非常小,否则数值解会振荡发散,即使真实解在快速衰减。
4. 与隐式 Euler 的对比(概念)
隐式 Euler:,稳定域更大,适合刚性,但每步需求解非线性方程。建模中若遇到刚性,优先考虑 ode15s,而不是手写复杂隐式迭代——除非题目要求展示格式。
四、关键决策
-
步长如何选
先按问题时间尺度取 ,再做 与 对比。关键输出(峰值、末端值)相对变化小于可接受阈值(如 1%–5%)再停止加密。 -
何时弃用 Euler
需要较高精度、长时间、或疑似刚性时,改用改进 Euler、RK4 或ode45/ode15s。 -
误差控制策略
固定步长 Euler 没有内置误差估计。至少报告:步长、步数、与参照解(解析/ode45)的最大绝对/相对误差。 -
变量约束
浓度、人口等应监控非负性;Euler 大步长时容易“跳”到负值。 -
输出记录
保存 全轨迹,而不仅是终点,便于画图与定位误差爆发位置。
五、MATLAB 示例
以 为例,真解 。比较不同步长,并扩展到二维线性系统。
%% 标量问题:显式 Euler vs 解析解
f = @(t, y) -y;
t0 = 0; tEnd = 2; y0 = 1;
hs = [0.2, 0.1, 0.05];
figure; hold on; grid on;
% 解析解曲线
tt = linspace(t0, tEnd, 400);
plot(tt, exp(-tt), "k-", "LineWidth", 1.5);
maxErr = zeros(size(hs));
for i = 1:numel(hs)
h = hs(i);
t = t0:h:tEnd;
y = zeros(size(t));
y(1) = y0;
for n = 1:numel(t)-1
y(n+1) = y(n) + h * f(t(n), y(n));
end
plot(t, y, "o-", "DisplayName", sprintf("h=%.2f", h));
maxErr(i) = max(abs(y - exp(-t)));
end
legend(["解析解", compose("h=%.2f", hs)], "Location", "best");
xlabel("t"); ylabel("y");
title("Euler 法:步长越小越贴近 e^{-t}");
disp("各步长最大绝对误差:");
disp(table(hs(:), maxErr(:), 'VariableNames', {'h', 'maxAbsErr'}));
% 观测收敛阶:误差比应接近 2
fprintf("err(h=0.2)/err(h=0.1) = %.3f(理论约 2)\n", maxErr(1)/maxErr(2));
fprintf("err(h=0.1)/err(h=0.05) = %.3f(理论约 2)\n", maxErr(2)/maxErr(3));
%% 向量形式:简谐振子 y1'=y2, y2'=-y1
% 真解为圆;Euler 会缓慢缓慢螺旋,体现能量漂移
fOsc = @(t, y) [y(2); -y(1)];
h = 0.05;
t = 0:h:20;
Y = zeros(2, numel(t));
Y(:,1) = [1; 0];
for n = 1:numel(t)-1
Y(:, n+1) = Y(:, n) + h * fOsc(t(n), Y(:, n));
end
figure;
plot(Y(1,:), Y(2,:), "b-"); hold on; grid on; axis equal;
theta = linspace(0, 2*pi, 200);
plot(cos(theta), sin(theta), "k--");
xlabel("y_1"); ylabel("y_2");
legend("Euler", "真解单位圆", "Location", "best");
title("Euler 在保守系统上的能量漂移");
封装成可复用函数:
function [t, y] = euler_ode(f, tSpan, y0, h)
% f: @(t,y) 列向量右端
t = tSpan(1):h:tSpan(2);
if t(end) < tSpan(2) - 10*eps
t = [t, tSpan(2)];
end
nStep = numel(t) - 1;
m = numel(y0);
y = zeros(m, nStep+1);
y(:,1) = y0(:);
for n = 1:nStep
hn = t(n+1) - t(n);
y(:, n+1) = y(:, n) + hn * f(t(n), y(:, n));
end
y = y.'; % 与 ode45 一样:每行一个时间点
end
六、如何解释结果
报告 Euler 结果时建议写明:
- 方程、初值、积分区间与步长 ;
- 每步右端计算次数(Euler 为 1)与总步数;
- 与解析解、
ode45或更小步长解的误差表; - 步长减半后误差比是否接近 2(支持一阶收敛);
- 若用于真实模型:关键指标(峰值时间、稳态)对 的敏感性;
- 明确说明 Euler 是基线方法,最终结论是否改用更高阶求解器复核。
若发现误差比远小于 2 或发散,优先检查:右端符号、步长是否超出稳定域、时间网格是否含终点、向量维度是否按列运算。
七、常见误区
1. 把 Euler 当作“默认高精度方法”
一阶精度在长时间或强非线性问题中往往不够。竞赛论文若只写 Euler,审稿直觉会质疑数值可靠性。
2. 只减小步长却从不对比
没有 与 的表,就无法证明收敛。单纯“步长取得很小”不是充分论证。
3. 忽视稳定域
刚性或大 Lipschitz 常数时,误差主要来自不稳定而非截断阶,盲目加密可能仍振荡。
4. 用相图外观判断方法正确
Euler 可能把闭轨画成外扩螺线。结构不正确时,应换方法或减小 ,不能强行解释成“系统不稳定”。
5. 单位与时间步混用
必须与 同单位。把“每天”的参数配上“小时”步长,会导致速率错位一个数量级。
八、小结
Euler 法的核心思想可以概括为:
在每一步用当前位置的瞬时变化率做线性外推,以最简单的方式离散微分方程。
它适合理解数值 ODE、做快速原型和收敛阶演示,但精度与稳定性都明显依赖步长。建模实践中,应把 Euler 定位为可解释的基线,用步长减半与更高阶方法交叉验证后,再采信定量结论。