Дискретизация и численные ошибки

Дискретизация и ошибки: почему шаг dt решает судьбу симуляции.

Ошибка дискретизации — расхождение между симуляцией и истинным решением, возникающее оттого, что мы заменили непрерывные производные конечными шагами.

Производная через конечную разность

Скорость — это производная координаты: насколько быстро меняется x. На бумаге это предел при бесконечно малом шаге. В компьютере бесконечно малого нет, поэтому берём конечный шаг dt и пишем приближённо: v ≈ (x(t+dt) - x(t)) / dt. Чем меньше dt, тем точнее приближение, но тем больше шагов нужно сделать.

Любую производную можно так заменить. Вторую производную (ускорение, кривизну) приближают через три соседние точки: f'' ≈ (f(x+h) - 2f(x) + f(x-h)) / h². Эта формула — рабочая лошадка всего раздела про волны и поля.

Два вида ошибок

Ошибка усечения (truncation) возникает оттого, что мы оборвали ряд Тейлора: заменили кривую её куском прямой на шаге dt. Она уменьшается, когда уменьшаем шаг. Ошибка округления (round-off) возникает оттого, что компьютер хранит числа с конечной точностью (примерно 15–16 значащих цифр). Она, наоборот, накапливается, когда шагов становится слишком много.

Отсюда — оптимальный шаг: слишком крупный даёт большую ошибку усечения, слишком мелкий — много шагов и рост округления плюс долгий счёт. Покажем это на дрейфе энергии орбиты за два года при разных dt:

import math
def orbit_energy_drift(dt, method):
    GM = 4*math.pi**2
    x, y = 1.0, 0.0
    vx, vy = 0.0, 2*math.pi
    def acc(x,y):
        r=math.hypot(x,y); a=-GM/r**3; return a*x,a*y
    def E():
        r=math.hypot(x,y); return 0.5*(vx*vx+vy*vy)-GM/r
    E0 = E()
    ax, ay = acc(x,y)
    n = int(2.0/dt)
    for _ in range(n):
        if method=="euler":
            ax,ay=acc(x,y)
            x+=vx*dt; y+=vy*dt
            vx+=ax*dt; vy+=ay*dt
        else:
            x+=vx*dt+0.5*ax*dt*dt
            y+=vy*dt+0.5*ay*dt*dt
            ax2,ay2=acc(x,y)
            vx+=0.5*(ax+ax2)*dt; vy+=0.5*(ay+ay2)*dt
            ax,ay=ax2,ay2
    return abs((E()-E0)/E0)
print("Дрейф энергии за 2 года орбиты при разных dt:")
print("  dt       Эйлер         Верле")
for dt in (0.02, 0.01, 0.005):
    e = orbit_energy_drift(dt, "euler")
    v = orbit_energy_drift(dt, "verlet")
    print(f"{dt:.3f}    {e:8.4f}      {v:.2e}")
print("Эйлер копит ошибку; Верле — крошечная и не растёт.")

Вывод:

Дрейф энергии за 2 года орбиты при разных dt:
  dt       Эйлер         Верле
0.020      0.5424      4.51e-09
0.010      0.4425      1.69e-11
0.005      0.3423      6.35e-14
Эйлер копит ошибку; Верле — крошечная и не растёт.

Порядок метода

Метод имеет порядок p, если при уменьшении шага вдвое ошибка падает в 2^p раз. Эйлер — метод первого порядка: половинный шаг даёт примерно половинную ошибку (в выводе 0.54 → 0.44 → 0.34, грубо линейно). Верле — второго порядка по координате, и для орбиты его дрейф ничтожен (10⁻⁹ и меньше). Порядок — это «во сколько раз окупается» уменьшение шага.

Устойчивость

Кроме точности есть устойчивость. Для волн и теплопроводности существует критический шаг: если dt больше его, симуляция не просто неточна — она взрывается, числа улетают в бесконечность за несколько шагов. Это условие Куранта (CFL), мы встретим его в разделе про волны. Устойчивость и точность — разные требования: метод может быть устойчив, но неточен, и наоборот.

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

Под капотом каждый шаг — это разложение Тейлора, оборванное на каком-то члене. Эйлер удерживает только первый член (линейное приближение), Верле — до второго (учитывает кривизну). Чем больше членов удержано, тем выше порядок и точнее шаг, но тем больше вычислений. Поэтому методы высокого порядка (Рунге-Кутта 4-го) делают несколько оценок ускорения за один шаг.

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

  • Думать, что мелкий шаг всегда лучше. За некоторым пределом растёт округление и время счёта, а точность не улучшается.
  • Игнорировать устойчивость. Для волн/тепла превышение CFL рушит симуляцию мгновенно, и это не «баг в коде».
  • Сравнивать методы по одному шагу. Разница видна на длинной дистанции: Эйлер копит ошибку, симплектические методы — нет.

Итоги

  • Производные заменяют конечными разностями; цена — ошибка дискретизации.
  • Есть ошибка усечения (падает с dt) и округления (растёт с числом шагов).
  • Порядок метода показывает, как быстро падает ошибка при уменьшении шага.
  • Устойчивость — отдельное требование: превышение критического dt рушит симуляцию.
Проверьте себя
1. Что такое ошибка усечения (truncation)?
AОшибка из-за конечной точности хранения чисел
BОшибка из-за замены кривой куском прямой на шаге dt (обрыв ряда Тейлора)
CОшибка ввода данных пользователем
DОшибка перевода единиц
2. Почему слишком мелкий шаг dt тоже плох?
AТочность всегда растёт, минусов нет
BРастёт накопленная ошибка округления и резко увеличивается время счёта
CСимуляция становится неустойчивой
DНарушается закон сохранения импульса
3. Метод имеет порядок p, если при уменьшении шага вдвое ошибка...
Aне меняется
Bпадает в 2^p раз
Cрастёт в p раз
Dстановится нулевой