Метод Эйлера: первый шаг в мир ОДУ

Урок про метод Эйлера — простейший способ численно решить дифференциальное уравнение, шагая по касательной.

Метод Эйлера решает y' = f(t, y) с условием y(t₀)=y₀, делая шаги по касательной: y_{n+1} = y_n + h·f(t_n, y_n).

Что значит «решить ОДУ численно»

Дифференциальное уравнение y' = f(t, y) задаёт скорость изменения y в каждой точке, а нам нужна сама функция y(t). Аналитически это удаётся редко; численно — почти всегда. Идея проста: в начальной точке мы знаем и y, и наклон y' = f(t₀, y₀). Сделаем маленький шаг h по этой касательной — получим приближённое y в точке t₀+h. Там пересчитаем наклон и шагнём снова. Так, шаг за шагом, рисуем траекторию решения. Это и есть явный метод Эйлера.

def эйлер(f, t0, y0, h, n):
    t, y = t0, y0
    точки = [(t, y)]
    for _ in range(n):
        y = y + h * f(t, y)     # шаг по касательной
        t = t + h
        точки.append((t, y))
    return точки

import math
# y' = y, y(0) = 1  ->  точное решение y = e^t
for t, y in эйлер(lambda t, y: y, 0.0, 1.0, 0.25, 4):
    print(f"t={t:.2f}  Эйлер={y:.6f}  точное={math.exp(t):.6f}  "
          f"ошибка={abs(y - math.exp(t)):.4f}")

Вывод:

t=0.00  Эйлер=1.000000  точное=1.000000  ошибка=0.0000
t=0.25  Эйлер=1.250000  точное=1.284025  ошибка=0.0340
t=0.50  Эйлер=1.562500  точное=1.648721  ошибка=0.0862
t=0.75  Эйлер=1.953125  точное=2.117000  ошибка=0.1639
t=1.00  Эйлер=2.441406  точное=2.718282  ошибка=0.2769

Эйлер уверенно растёт вместе с решением, но систематически отстаёт: касательная всегда чуть «недотягивает» до выпуклой экспоненты, и ошибки копятся. К t=1 отставание уже 0.28 — заметно. Виноват крупный шаг.

Первый порядок точности

Метод Эйлера имеет первый порядок: глобальная ошибка пропорциональна h. Уменьшим шаг вдвое — ошибка упадёт примерно вдвое. Это медленно: для одной дополнительной цифры нужно в 10 раз больше шагов. Поэтому на практике чистый Эйлер почти не используют для точных расчётов — но он бесценен как понятная отправная точка и кирпичик для более умных методов (Рунге-Кутты).

     точное решение (кривая)
        /
       /  • y2   ← Эйлер отстаёт: каждый шаг
      /  /          идёт по касательной (прямой)
     / • y1         и «срезает» выпуклость
    /•/
   y0
   t0  t1  t2  →   ошибка накапливается с каждым шагом

Неявный Эйлер и устойчивость

Есть вариант — неявный (обратный) метод Эйлера: y_{n+1} = y_n + h·f(t_{n+1}, y_{n+1}). Наклон берётся в конце шага, а не в начале. Неизвестное y_{n+1} теперь в обеих частях — приходится решать уравнение на каждом шаге (например, Ньютоном). Зачем такая морока? Ради устойчивости. Для «убегающих вниз» решений вроде y' = −15y явный Эйлер при крупном шаге разносит ответ в бесконечность, а неявный остаётся устойчивым при любом шаге.

# y' = -15y, y(0)=1. Точное: y = e^(-15t), быстро гаснет к нулю.
import math

def явный(h, n):       # y_{n+1} = y_n + h*(-15 y_n)
    y = 1.0
    for _ in range(n):
        y = y + h * (-15 * y)
    return y

def неявный(h, n):     # y_{n+1} = y_n / (1 + 15h)  -- решено аналитически
    y = 1.0
    for _ in range(n):
        y = y / (1 + 15 * h)
    return y

print("шаг h=0.25 (крупный):")
print(f"  явный   Эйлер: y(1) = {явный(0.25, 4):.4f}   (точное ≈ 0)")
print(f"  неявный Эйлер: y(1) = {неявный(0.25, 4):.6f}  (точное ≈ 0)")

Вывод:

шаг h=0.25 (крупный):
  явный   Эйлер: y(1) = 57.1914   (точное ≈ 0)
  неявный Эйлер: y(1) = 0.001964  (точное ≈ 0)

Картина драматична: точное решение стремится к нулю, неявный Эйлер это уважает (0.002, близко к нулю), а явный при шаге 0.25 взрывается до 57 и дальше уходит в бесконечность. Это не ошибка кода — это неустойчивость явного метода на «жёстком» уравнении (об этом — следующий урок).

Как работает под капотом

Локальная ошибка одного шага Эйлера — ~ h²·y''/2 (отброшенный член Тейлора), но за 1/h шагов они накапливаются в глобальную ~ h — отсюда первый порядок. Устойчивость же — отдельное свойство: для тестового уравнения y'=λy явный Эйлер устойчив лишь при |1 + hλ| ≤ 1, то есть требует малого h при большом |λ|. Неявный Эйлер устойчив при любом h (A-устойчивость) — потому и нужен для жёстких задач. Так в одном простом методе видны обе ключевые оси численного решения ОДУ: точность (порядок) и устойчивость.

Частые ошибки

  • Думать, что малая ошибка за шаг = малая ошибка решения. Ошибки накапливаются; глобальный порядок Эйлера всего первый.
  • Применять явный метод к жёсткому уравнению с крупным шагом. Получите взрыв решения; нужен неявный метод или крошечный шаг.
  • Использовать чистый Эйлер для точных расчётов. Первый порядок слишком груб; берите RK4 (следующий урок).

Итоги

  • Явный Эйлер: y_{n+1} = y_n + h·f(t_n, y_n) — шаг по касательной, первый порядок (ошибка ∝ h).
  • Неявный Эйлер берёт наклон в конце шага; решает уравнение на каждом шаге, зато устойчив при любом h.
  • Точность и устойчивость — разные свойства: явный метод может быть точным, но неустойчивым.
  • Эйлер — фундамент для понимания, но для практики груб; дальше — Рунге-Кутты.
Проверьте себя
1. Формула явного метода Эйлера — это...
Ay_{n+1} = y_n + h·f(t_n, y_n)
By_{n+1} = (y_n + y_{n-1})/2
Cy_{n+1} = h·f(t_n)
Dy_{n+1} = y_n·f(t_n, y_n)
2. Какой порядок точности у метода Эйлера?
Aпервый (глобальная ошибка ∝ h)
Bвторой
Cчетвёртый
Dнулевой
3. Чем неявный Эйлер выгоднее явного на жёстком уравнении y' = −15y?
Aон точнее на гладких участках
Bон устойчив при любом шаге h, тогда как явный при крупном h взрывается
Cон не требует вычисления f
Dон быстрее по времени