読者です 読者をやめる 読者になる 読者になる

北海道苫小牧市出身の壮年PGが書くブログ

永遠のプログラマを夢見る、苫小牧市出身のおじさんのちらしの裏

問題4.19の解答(台形公式、シンプソンの公式)

math python sympy

概説微分積分の問題4.19の解答。

台形公式は定積分を台形で近似するもの。台形の公式を使って面積を出して整理すればすぐ出てくる。

def trapezoidal(f, range_, n):
    a, b = range_
    result = f(a) + f(b)
    for i in xrange(1, n):
        result += f(a + (b - a) * i / n) * 2
    return result * (b - a) / 2 / n

シンプソンの公式は定積分を放物線で近似するもの。これも愚直に計算して整理すれば出てくる。

def simpsons(f, range_, n):
    a, b = range_
    result = f(a) + f(b)
    for i in xrange(1, n):
        result += f(a + (b - a) * (2 * i) / 2 / n) * 2
    for i in xrange(0, n):
        result += f(a + (b - a) * (2 * i + 1) / 2 / n) * 4
    return result * (b - a) / 6 / n

実際に利用して誤差を見てみる。sympy を使って実際の値を求め、近似値と比較する。

import math
import sympy as sym
from sympy.utilities import lambdify
from sympy.abc import x

# compute the real answer
f = 1 / (x**2 + 1)
F = sym.integrate(f, x)
answer = F.subs(x, 1) - F.subs(x, 0)
print "f: %s, F: %s, answer=%s" % (f, F, answer)

# compute approximations
integral_range = (0., 1.)
n = 2
f_lambda = lambdify(x, f)
evaled_answer = answer.evalf()
trapezoidal_result = trapezoidal(f_lambda, integral_range, n * 2)
trapezoidal_error = abs(trapezoidal_result - evaled_answer)
print "trapezoidal: %f, error=%f" % (trapezoidal_result, trapezoidal_error)

simpsons_result = simpsons(f_lambda, integral_range, n)
simpsons_error = abs(simpsons_result - evaled_answer)
print "simpsons: %f, error=%f" % (simpsons_result, simpsons_error)
f: 1/(x**2 + 1), F: atan(x), answer=pi/4
trapezoidal: 0.782794, error=0.002604
simpsons: 0.785392, error=0.000006

この例のように滑らかな曲線だと、シンプソンの公式の方が有利に働くことが多いが、必ずしもこうなるとは限らない。特に滑らかではない曲線の場合は、台形公式の方が有利らしい。


これらの近似の誤差を評価するには、台形公式では二階微分の絶対値の上限、シンプソンの公式では四階微分の絶対値の上限が必要となる。