Практическая оценка погрешности#
Не всегда на практике удается воспользоваться априорной оценкой вида
так как не всегда удается оценить \(M_p\) для подынтегральной функции. В этом случае, обычно, пользуются правилом Рунге для определения подходящего шага \(h\).
Правило Рунге#
Предположим, что нам известен порядок метода \(p\), которым мы хотим найти значение интеграла с заданной точностью \(\varepsilon\). Тогда результат, вычисленный этим методом с шагом \(h\) будет иметь вид
Здесь \(I^*\) — точное значение интеграла. Для достаточно малых \(h\) можно записать
где \(C_h\) — почти константа, то есть слабо зависит от \(h\).
Вычислим интеграл с шагом \(h\) и с шагом \(h/2\):
Тогда
Таким образом, погрешность интегрирования на сетке \(h/2\) может быть оценена как
Алгоритм оценки погрешности#
Алгоритм применения правила Рунге следующий:
Задать небольшое число интевалов, скажем \(n = 10\)
Вычислить \(I_h, I_{h/2}\) и оценить ошибку
Если \(|\Delta_{h/2}| < \varepsilon\), прекратить вычисления. Иначе измельчить сетку вдвое \(h := h/2\) и перейти к шагу 2.
Ответом служит \(I_{h/2}\).
import numpy as np
def simpson(f, h):
assert (len(f)-1) % 2 == 0
return h/3. * (f[0] + 4 * sum(f[1:-1:2]) + \
2 * sum(f[2:-2:2]) + f[-1])
def runge(f, a, b, eps=1e-12):
n = 4
Ih = simpson(f(np.linspace(a, b, n+1)), (b-a) / n)
while True:
Ih_2 = simpson(f(np.linspace(a, b, 2*n+1)), (b-a) / (2*n))
Dh_2 = (Ih - Ih_2) / (2**4 - 1) # p = 4 для Симпсона
print('I(h) = %.16f, err(h) = %.6e' % (Ih_2, Dh_2))
if abs(Dh_2) < eps:
break
n *= 2; Ih = Ih_2
if n > 10000: print('Too large n'); break
return Ih_2
def f(x): return 1 / (1 + x**2)
a = 0; b = 0.5; exact = np.arctan(b)
runge(f, a, b) - exact
I(h) = 0.4636479223346336, err(h) = 3.157185e-07
I(h) = 0.4636476285453064, err(h) = 1.958596e-08
I(h) = 0.4636476102217171, err(h) = 1.221573e-09
I(h) = 0.4636476090771032, err(h) = 7.630759e-11
I(h) = 0.4636476090055746, err(h) = 4.768578e-12
I(h) = 0.4636476090011042, err(h) = 2.980246e-13
2.9809488211185453e-13
Условия корректности применения правила Рунге#
Для проверки корректности использования правила Рунге достаточно проверять условия
что метод фактически сходится с порядком \(p\),
что константа \(C_h\) слабо зависит от \(h\):
что выполнено эмпирическое правило
def runge_checks(f, a, b, eps=1e-12):
n = 4
Ih = simpson(f(np.linspace(a, b, n+1)), (b-a) / n)
Dh = None;
while True:
h_2 = (b-a) / (2*n)
Ih_2 = simpson(f(np.linspace(a, b, 2*n+1)), h_2)
Dh_2 = (Ih - Ih_2) / (2**4 - 1) # p = 4 для Симпсона
Ch_2 = Dh_2 / h_2**4; ps = np.log2(Dh / Dh_2) if Dh != None else np.nan
print('I(h) = %.16f, err(h) = %.6e, p* = %4.2f, C = %.6e' % \
(Ih_2, Dh_2, ps, Ch_2))
if abs(Dh_2) < eps:
break
n *= 2; Ih = Ih_2; Dh = Dh_2
if n > 10000: print('Too large n'); break
return Ih_2
def f(x): return 1 / (1 + x**2)
a = 0; b = 0.5; exact = np.arctan(b)
runge_checks(f, a, b) - exact
I(h) = 0.4636479223346336, err(h) = 3.157185e-07, p* = nan, C = 2.069093e-02
I(h) = 0.4636476285453064, err(h) = 1.958596e-08, p* = 4.01, C = 2.053736e-02
I(h) = 0.4636476102217171, err(h) = 1.221573e-09, p* = 4.00, C = 2.049459e-02
I(h) = 0.4636476090771032, err(h) = 7.630759e-11, p* = 4.00, C = 2.048366e-02
I(h) = 0.4636476090055746, err(h) = 4.768578e-12, p* = 4.00, C = 2.048089e-02
I(h) = 0.4636476090011042, err(h) = 2.980246e-13, p* = 4.00, C = 2.048009e-02
2.9809488211185453e-13
Экстраполяция Ричардсона и алгоритм Ронберга#
Вспомним равенство, которое ранее получили:
Переставляя слагаемые, понимаем, что не изменяя сетку и не делая никаких новых вычислений функции, мы получили метод с бОльшим порядком сходимости! Такой метод уточнения точности называется экстраполяцией Ричардсона.
В частности, если мы подставим в выражение выше квадратуру для формулы трапеций, мы получим формулу Симпсона.
Продолжая эту процедуру далее и далее, получим алгоритм Ронберга уточнения вычисления интеграла.