Romberg積分入門

Introduction

第1回では円周率の多角形近似,第2回では数値微分を扱いました.どちらでも重要だったのは,近似値そのものではなく,その背後にある誤差展開でした.第3回では,定積分を題材にします.台形公式の誤差展開を利用して,低次の誤差項を順に消す方法が Romberg 積分です.

1.今回の目的

今回の目的は,次の定積分 \[ I=\int_a^b f(x)\,dx \] を高精度に近似することです.もっとも基本的な数値積分公式の一つに,台形公式があります.台形公式は単純で実装しやすい一方で,そのままでは2次精度です.しかし,台形公式の誤差には \[ h^2,\ h^4,\ h^6,\ldots \] という偶数冪の展開が現れます.そのため,Richardson extrapolation と非常に相性がよいです.重要なのは,台形公式を単に細かくするだけでなく,Euler--Maclaurin 展開に基づいて \(h=0\) へ外挿する点です.

2.合成台形公式

区間 \([a,b]\) を \(N\) 等分します. \[ h=\frac{b-a}{N},\quad x_j=a+jh \quad (j=0,1,\ldots,N) \] とおきます.このとき,合成台形公式は \[ T(h) = h\left\{ \frac12 f(a) +\sum_{j=1}^{N-1} f(x_j) +\frac12 f(b) \right\} \] で定義されます.これは,各小区間 \([x_j,x_{j+1}]\) 上でグラフを直線で近似し,その下の台形の面積を足し合わせる方法です.したがって,考え方そのものは非常に素朴です.しかし,この素朴な公式が外挿法と組み合わさることで,高精度な方法になります.

台形公式の第一の役割は,計算可能な近似値 \[ T(h) \] を作ることです.ここで \(h\) はメッシュ幅です.\(h\to 0\) とすれば \[ T(h)\to I \] となります.外挿法では,この \(T(h)\) を \(h=0\) へ外挿します.

3.台形公式の誤差展開

外挿法を使うには,近似値 \(T(h)\) がどのような誤差展開をもつかを知る必要があります.ここで現れるのが Euler--Maclaurin 公式です.\(f\) が十分滑らかならば,台形公式には次の形の展開があります. \[ T(h) = \int_a^b f(x)\,dx + \sum_{k=1}^m h^{2k} \frac{B_{2k}}{(2k)!} \left\{ f^{(2k-1)}(b)-f^{(2k-1)}(a) \right\} + O(h^{2m+2}). \] ここで \(B_{2k}\) は Bernoulli 数です.特に最初の項だけを見ると, \[ T(h) = I + \frac{h^2}{12} \left\{ f'(b)-f'(a) \right\} +O(h^4) \] です. したがって,台形公式の誤差は基本的に \(O(h^2)\) です.

ここで大切なのは,誤差が \[ h,\ h^3,\ h^5,\ldots \] ではなく, \[ h^2,\ h^4,\ h^6,\ldots \] の偶数冪で現れることです.したがって,台形公式に外挿法を使うときは,\(h\) の多項式ではなく \(h^2\) の多項式として扱います.

4.1段の外挿

誤差展開の最初の部分だけを取り出して, \[ T(h)=I+c_2h^2+c_4h^4+O(h^6) \] と書きます.同じ公式を \(h/2\) で計算すると, \[ T(h/2) = I+c_2\frac{h^2}4+c_4\frac{h^4}{16}+O(h^6) \] です.この二つを \[ R_1(h):=\frac{4T(h/2)-T(h)}3 \] と組み合わせると,\(c_2h^2\) の項が消えます.実際, \[ R_1(h)=I+O(h^4) \] です.つまり,台形公式を二つのメッシュ幅で計算するだけで,2次精度の近似から4次精度の近似を作れます.これは第1回の円周率近似や,第2回の中心差分で見た外挿と同じ考え方です.

5.Romberg 表

1段の外挿で \(O(h^2)\) の誤差を消せるなら,さらに次の \(O(h^4)\) の誤差も消したくなります.そのために,台形公式を \[ h,\quad \frac h2,\quad \frac h4,\quad \frac h8,\ldots \] で順に計算し,外挿を表の形に整理します.これが Romberg 表です.

\(R_{i,0}\) を台形公式の値とします.たとえば \(h_i=(b-a)/2^i\) として, \[ R_{i,0}=T(h_i) \] です.第1列目は,単なる台形公式の値です.次に,外挿した値を \[ R_{i,k} \] と書きます.更新式は \[ R_{i,k} = R_{i,k-1} + \frac{R_{i,k-1}-R_{i-1,k-1}}{4^k-1}, \quad k=1,\ldots,i. \] です.分母に \(4^k-1\) が出る理由は,台形公式の誤差展開が \(h^2\) の冪で進むからです.\(k\) 段目の外挿では,主に \(h^{2k}\) の項を消しています.\(h\) を半分にすると \[ h^{2k} \quad\text{は}\quad \left(\frac h2\right)^{2k} = \frac{h^{2k}}{4^k} \] になります.そのため,補正係数に \(4^k-1\) が現れます.Romberg 表は,次のような三角形の表になります.

level 台形公式 1段外挿 2段外挿 3段外挿
0 \(R_{0,0}\)
1 \(R_{1,0}\) \(R_{1,1}\)
2 \(R_{2,0}\) \(R_{2,1}\) \(R_{2,2}\)
3 \(R_{3,0}\) \(R_{3,1}\) \(R_{3,2}\) \(R_{3,3}\)

一般には,右下の対角成分 \[ R_{0,0},\ R_{1,1},\ R_{2,2},\ldots \] が高精度な近似値になります.

6.Pythonで確認する

ここでは \[ I=\int_0^1 e^x\,dx \] を Romberg 積分で計算します.この積分は厳密に \[ I=e-1 \] と分かるので,誤差を確認しやすい例です.次のコードは,合成台形公式を計算し,その値から Romberg 表を作ります.HTMLの中にコードをそのまま掲載しているので,この部分だけをコピーしてPython ファイルとして実行することもできます.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Romberg integration.

This code accompanies the article:
外挿法と誤差展開(第3回):台形公式から Romberg 積分へ

The goal is to show how the composite trapezoidal rule can be improved
by Richardson extrapolation.

We use the example

    I = ∫_0^1 exp(x) dx = e - 1.

The composite trapezoidal rule has an even-power error expansion:

    T(h) = I + c_2 h^2 + c_4 h^4 + c_6 h^6 + ...

Romberg integration removes these error terms successively.
"""

import math


def composite_trapezoidal(f, a, b, n):
    """
    Composite trapezoidal rule.

    Let h=(b-a)/n and x_j=a+jh. Then

        T_n = h { 1/2 f(a) + sum_{j=1}^{n-1} f(x_j) + 1/2 f(b) }.
    """
    h = (b - a) / n

    total = 0.5 * f(a) + 0.5 * f(b)

    for j in range(1, n):
        x_j = a + j * h
        total += f(x_j)

    return h * total


def romberg_table(f, a, b, max_level):
    """
    Build a Romberg table.

    We use n=1,2,4,8,... subintervals.
    Thus h is halved at each new row.

    The first column is the composite trapezoidal rule:
        R[i][0] = T(h_i).

    The remaining columns are computed by Richardson extrapolation:
        R[i][k] = R[i][k-1]
                  + (R[i][k-1] - R[i-1][k-1]) / (4^k - 1).

    The factor 4^k appears because the trapezoidal rule has an expansion
    in powers of h^2.
    """
    table = []

    for i in range(max_level + 1):
        n = 2 ** i

        row = [composite_trapezoidal(f, a, b, n)]

        for k in range(1, i + 1):
            correction = (row[k - 1] - table[i - 1][k - 1]) / (4 ** k - 1)
            row.append(row[k - 1] + correction)

        table.append(row)

    return table


def print_romberg_table(table, exact):
    """
    Print the Romberg table and the errors of the diagonal entries.
    """
    print("=" * 88)
    print("Romberg integration for I = integral_0^1 exp(x) dx")
    print("Exact value: e - 1")
    print("=" * 88)

    max_level = len(table) - 1

    header = " i   N       T(h)=R[i,0]"
    for k in range(1, max_level + 1):
        header += f"        R[i,{k}]"
    print(header)
    print("-" * 88)

    for i, row in enumerate(table):
        n = 2 ** i
        line = f"{i:2d} {n:4d} "
        for value in row:
            line += f" {value:15.12f}"
        print(line)

    print()
    print("Errors of the diagonal entries R[i,i]")
    print("-" * 88)
    print(f"{'i':>2} {'N':>4} {'R[i,i]':>18} {'error':>14} {'rate':>8}")

    previous_error = None
    previous_h = None

    for i, row in enumerate(table):
        n = 2 ** i
        h = 1.0 / n
        diagonal = row[-1]
        error = abs(diagonal - exact)

        if previous_error is None:
            rate_text = "   ---"
        else:
            rate = math.log(previous_error / error) / math.log(previous_h / h)
            rate_text = f"{rate:8.3f}"

        print(f"{i:2d} {n:4d} {diagonal:18.14f} {error:14.6e} {rate_text}")

        previous_error = error
        previous_h = h


def main():
    f = math.exp
    a = 0.0
    b = 1.0
    exact = math.e - 1.0

    table = romberg_table(f, a, b, max_level=5)

    print_romberg_table(table, exact)


if __name__ == "__main__":
    main()

実行結果の例

========================================================================================
Romberg integration for I = integral_0^1 exp(x) dx
Exact value: e - 1
========================================================================================
 i   N       T(h)=R[i,0]        R[i,1]        R[i,2]        R[i,3]        R[i,4]        R[i,5]
----------------------------------------------------------------------------------------
 0    1   1.859140914230
 1    2   1.753931092465  1.718861151877
 2    4   1.727221904558  1.718318841922  1.718282687925
 3    8   1.720518592164  1.718284154700  1.718281842218  1.718281828795
 4   16   1.718841128580  1.718281974052  1.718281828675  1.718281828460  1.718281828459
 5   32   1.718421660316  1.718281837562  1.718281828462  1.718281828459  1.718281828459  1.718281828459

Errors of the diagonal entries R[i,i]
----------------------------------------------------------------------------------------
 i    N             R[i,i]          error     rate
 0    1   1.85914091422952   1.408591e-01    ---
 1    2   1.71886115187659   5.793234e-04    7.926
 2    4   1.71828268792476   8.594657e-07    9.397
 3    8   1.71828182879453   3.354852e-10   11.323
 4   16   1.71828182845908   3.375078e-14   13.279
 5   32   1.71828182845904   6.661338e-16    5.663

最初の列 \(R[i,0]\) は,単なる台形公式です.行が下がるにつれて分割数 \(N\) が増え,\(h\) が半分になっています.一方,右側の列は外挿によって作られた値です.右下の対角成分 \(R[i,i]\) を見ると,急速に厳密値 \(e-1\) に近づくことが分かります.

7.何が起きているのか

Romberg 積分で起きていることは,次の一言で説明できます.

台形公式の誤差展開 \[ T(h)=I+c_2h^2+c_4h^4+c_6h^6+\cdots \] を利用して,\(c_2h^2,c_4h^4,c_6h^6,\ldots\) を順に消している.

したがって,Romberg 積分は単なる「台形公式の改良版」ではありません. むしろ,

  • 低次の近似公式を使う,
  • 誤差展開を調べる,
  • 主要誤差を線形結合で消す,
  • 高次精度の近似を作る,

という外挿法の典型例です.

8.注意:滑らかさが重要

Romberg 積分がうまく働くためには,\(f\) が十分滑らかで,Euler--Maclaurin 型の誤差展開が有効であることが重要です.不連続な関数,端点で特異性をもつ関数,急激に振動する関数では,表の右下に進めば必ず高精度になるとは限りません.これは有限要素法でも同じです.外挿法は魔法ではなく,誤差展開があって初めて強力になります.

9.今回のまとめ

  • 合成台形公式は,定積分を計算する基本的な公式です.
  • 台形公式の誤差は,Euler--Maclaurin 展開により偶数冪で表されます.
  • そのため,台形公式には \(h^2\) に関する Richardson extrapolation が自然に使えます.
  • Romberg 積分は,台形公式と外挿法を組み合わせた高精度積分法です.
  • ただし,十分な滑らかさと誤差展開の存在が重要です.

第4回では,この考え方を常微分方程式に移します.非stiffな問題に対する Gragg 型の外挿法が扱われています.数値積分より少し難しくなりますが,「低次の方法を基礎にして,誤差展開を利用して精度を上げる」という主役は同じです.

参考文献

  • Rolf Rannacher, Special Topics in Numerics III: Extrapolation and Defect Correction, Lecture Notes, Heidelberg University, 2017.

ページの先頭へ