Оптимальный шаг дифференцирования#
При дифференцировании функции имеются два основных источника погрешности
Ошибка метода — уменьшается при уменьшении \(h\)
Ошибка вычислений — растет при уменьшении \(h\)
Поскольку характер роста ошибок различный, существует некоторое значение \(h^*\), при котором ошибка минимальна. Рассмотим полную ошибку
как функцию от \(h\) и найдем минимум.
Продифферецируем полную ошибку
по \(h\):
Для функции \(f(x) = \sin x\) оценки производных \(M_2 = M_3 = 1\). Также примем \(\Delta f = 10^{-16}\) - аля работаем с типом double и \(sin x\) можно максимально оценить единицей. Тогда
Проделав то же самое для формулы дифференцирования второго порядка, получаем
по \(h\):
При тех же значениях \(M_2, M_3\) и \(\Delta f\) получаем
Т.е. оптимально брать меньший шаг и при этом будем получать меньшую погрешность!
import numpy as np
import matplotlib.pyplot as plt
def diff1(f, x0, h):
return (f(x0 + h) - f(x0)) / h
def diff2(f, x0, h):
return (f(x0 + h) - f(x0 - h)) / (2 * h)
hs = np.logspace(-16, 0, num=50) # h = 1e-16 ... 1
errs1 = []
errs2 = []
for h in hs:
errs1.append(abs(diff1(np.sin, 1, h) - np.cos(1)))
errs2.append(abs(diff2(np.sin, 1, h) - np.cos(1)))
M2 = M3 = 1
plt.figure(figsize=(10, 7))
plt.loglog(hs, errs1, 'k.', ms=16)
plt.loglog(hs, errs2, 'r.', ms=16)
plt.loglog(hs, M2 * hs / 2, 'k-', label='First order', lw=3)
plt.loglog(hs, M3 * hs**2 / 6, 'r-', label='Second order', lw=3)
plt.ylim(1e-14, 1)
plt.xlabel('$h$')
plt.ylabel('$\\varepsilon$')
plt.legend(loc='upper center')
plt.show()