Полу-неявный Эйлер: одно изменение, огромный эффект
Поменяй местами две строки кода — и нестабильный интегратор станет надёжным. Это не магия, а симплектичность.
Полу-неявный (симплектический) метод Эйлера — вариант Эйлера, где координата обновляется по уже пересчитанной скорости; он не сохраняет энергию точно, но и не накапливает её систематически.
В чём отличие
Явный Эйлер сначала двигал координату по старой скорости, потом обновлял скорость. Полу-неявный делает наоборот: сначала обновляет скорость, а координату двигает уже по новой:
$$v_{n+1} = v_n + a_n \, \Delta t, \qquad x_{n+1} = x_n + v_{n+1} \, \Delta t.$$
Разница в одну строку, но последствия колоссальны. Этот метод относится к классу симплектических — сохраняющих особую геометрическую структуру механики. Практически это значит, что энергия не уходит в бесконечность и не затухает в ноль, а слегка колеблется вокруг истинного значения. Для игр это решающее свойство: маятник качается стабильно часами, планета летает по орбите без раскрутки.
Сравним энергию
Тот же осциллятор $a = -x$, тот же шаг $\Delta t = 0.1$, но новый порядок строк. Сравните энергию с прошлым уроком.
x, v, dt = 1.0, 0.0, 0.1
for n in range(11):
E = 0.5*v*v + 0.5*x*x
if n % 2 == 0:
print(f"n={n:2d} x={x:6.3f} v={v:6.3f} E={E:.4f}")
a = -x
v = v + a*dt # сначала скорость
x = x + v*dt # потом координата по НОВОЙ скорости
Вывод:
n= 0 x= 1.000 v= 0.000 E=0.5000 n= 2 x= 0.970 v=-0.199 E=0.4903 n= 4 x= 0.901 v=-0.390 E=0.4824 n= 6 x= 0.797 v=-0.566 E=0.4775 n= 8 x= 0.661 v=-0.718 E=0.4763 n=10 x= 0.498 v=-0.843 E=0.4790
Энергия теперь не убегает: она колеблется около $0.48$ и не растёт. Если прогнать симуляцию на пять полных периодов, у явного Эйлера энергия раздувается в $\approx 22.7$ раза, а у полу-неявного остаётся $\approx 1.0003$ от исходной — практически идеально. Один и тот же шаг, одна переставленная строка — и разница между «взрывом» и стабильностью.
Как работает под капотом
Интуиция такая: явный Эйлер всегда использует устаревшую скорость и систематически промахивается в одну сторону. Полу-неявный «подсматривает» обновлённую скорость, и его ошибки на разных фазах колебания частично компенсируют друг друга — энергия не дрейфует, а осциллирует. Строго это доказывается через теорию симплектических отображений: метод сохраняет площадь в фазовом пространстве $(x, v)$, и потому траектория не может бесконечно раскручиваться или сжиматься.
Важная оговорка: симплектический метод сохраняет не саму энергию, а близкую к ней величину («теневую» энергию). Поэтому реальная энергия слегка колеблется, но остаётся ограниченной на сколь угодно долгом интервале. Для подавляющего большинства игр и интерактивных симуляций полу-неявный Эйлер — золотой стандарт: дёшев, как явный, но стабилен.
Частые ошибки
- Случайно вернуться к явному порядку. При рефакторинге легко переставить строки обратно и получить «взрывающийся» маятник. Порядок «сначала $v$, потом $x$» — это контракт.
- Ждать точного сохранения энергии. Энергия колеблется — это нормально; важно, что она не дрейфует.
- Применять для жёстких систем без оглядки. При слишком большом шаге даже симплектический метод теряет устойчивость; шаг всё равно надо выбирать с умом.
Итог
- Полу-неявный Эйлер: сначала $v_{n+1}=v_n+a_n\Delta t$, затем $x_{n+1}=x_n+v_{n+1}\Delta t$.
- Он симплектический: энергия колеблется, но не дрейфует — система стабильна надолго.
- На пяти периодах явный Эйлер раздувает энергию в $\sim 22$ раза, полу-неявный — почти не меняет.
- Цена та же, что у явного, поэтому это стандартный выбор для игр.