言語
日本語
English

Caution

お使いのブラウザはJavaScriptが無効になっております。
当サイトでは検索などの処理にJavaScriptを使用しています。
より快適にご利用頂くため、JavaScriptを有効にしたうえで当サイトを閲覧することをお勧めいたします。

Fortran辞典

  1. トップページ
  2. Fortran辞典
  3. 数値積分(台形則・シンプソン則)

数値積分(台形則・シンプソン則)

『FORTRAN』で科学計算を行う際、定積分を数値的に求める「数値積分」はとりわけ重要な手法です。台形則は積分区間を台形で近似する最もシンプルなアルゴリズムであり、シンプソン則は放物線で近似することで同じ分割数でもより高い精度が得られます。どちらも区間を細かくするほど真値に近づき、実装がシンプルなため学習にも実用にも適しています。

構文

! -----------------------------------------------
! 台形則(Trapezoidal Rule)
! ∫[a,b] f(x) dx ≈ h/2 * (f(a) + 2*f(x1) + 2*f(x2) + ... + f(b))
! h = (b - a) / n  (n: 分割数)
! -----------------------------------------------

real(kind=8) :: a, b, h, result
integer      :: n, i

n = 1000          ! 分割数(大きいほど精度が高い)
h = (b - a) / n

result = 0.5_8 * (f(a) + f(b))
do i = 1, n - 1
    result = result + f(a + i * h)
end do
result = result * h

! -----------------------------------------------
! シンプソン則(Simpson's Rule)
! n は偶数でなければなりません
! ∫[a,b] f(x) dx ≈ h/3 * (f(x0) + 4*f(x1) + 2*f(x2) + 4*f(x3) + ... + f(xn))
! -----------------------------------------------

n = 1000          ! 分割数(偶数)
h = (b - a) / n

result = f(a) + f(b)
do i = 1, n - 1
    if (mod(i, 2) == 1) then
        result = result + 4.0_8 * f(a + i * h)   ! 奇数インデックス: 係数 4
    else
        result = result + 2.0_8 * f(a + i * h)   ! 偶数インデックス: 係数 2
    end if
end do
result = result * h / 3.0_8

構文一覧

手法 / 操作概要
台形則区間を n 個の台形で近似します。誤差は O(h²) です。実装がシンプルで幅広い関数に適用できます。
シンプソン則区間を放物線で近似します。誤差は O(h⁴) であり、同じ分割数で台形則より高精度です。分割数は偶数にする必要があります。
mod(i, 2)i を 2 で割った余りを返します。シンプソン則の係数 4/2 の切り替えに使います。
関数の引数渡し被積分関数を副プログラムとして定義し、ルーチンに渡すことで汎用の積分サブルーチンを作れます。
誤差推定分割数 n と 2n の結果を比較することでリチャードソン補外による精度向上や収束確認ができます。
abs(result_2n - result_n)分割数を 2 倍にしたときの結果差の絶対値で収束を判定します。

サンプルコード

jjk_integration.f90
! jjk_integration.f90 — 台形則・シンプソン則による数値積分のサンプルです
! 呪術廻戦の呪力エネルギー曲線を模した関数を積分します
!
! コンパイルと実行:
!   gfortran jjk_integration.f90 -o jjk_integration && ./jjk_integration

program jjk_integration
    implicit none

    integer, parameter :: DP = selected_real_kind(15, 307)

    ! -----------------------------------------------
    ! 積分範囲と分割数
    ! 積分する関数: f(x) = x^2 * sin(x)  [0, π]
    ! 解析解: [-x^2*cos(x) + 2*x*sin(x) + 2*cos(x)] の [0,π] での値 = π^2 - 4
    ! -----------------------------------------------
    real(DP), parameter :: PI     = 3.14159265358979323846_DP
    real(DP), parameter :: A      = 0.0_DP   ! 積分下限
    real(DP), parameter :: B      = PI       ! 積分上限
    real(DP), parameter :: EXACT  = PI**2 - 4.0_DP  ! 解析解

    integer  :: n
    real(DP) :: trap_result, simp_result
    real(DP) :: trap_err, simp_err

    ! -----------------------------------------------
    ! 各分割数での精度を比較します
    ! -----------------------------------------------
    print *, "===== 呪術廻戦 呪力エネルギー積分 ====="
    print *, "  f(x) = x^2 * sin(x)  [0, π]"
    print "(a, f18.15)", "  解析解 (π^2 - 4) = ", EXACT
    print *, ""
    print "(a)", "  分割数     台形則              誤差(台形)        シンプソン則          誤差(シンプソン)"

    do n = 10, 1000, 10
        ! 台形則による積分
        trap_result = trapezoidal(A, B, n)
        trap_err    = abs(trap_result - EXACT)

        ! シンプソン則による積分(n は偶数にします)
        simp_result = simpsons(A, B, n)
        simp_err    = abs(simp_result - EXACT)

        if (n == 10 .or. n == 100 .or. n == 1000) then
            print "(i8, f20.15, es14.4, f20.15, es14.4)", &
                n, trap_result, trap_err, simp_result, simp_err
        end if
    end do

    print *, ""

    ! -----------------------------------------------
    ! 五条悟の無限大呪力(収束判定)
    ! 分割数を増やし続けて収束したら停止します
    ! -----------------------------------------------
    print *, "===== 五条悟の無限大呪力 収束確認 ====="
    print *, "  シンプソン則で分割数を倍にしながら収束を確認します。"
    print *, ""

    block
        real(DP) :: prev, curr, tol
        integer  :: nn

        tol  = 1.0e-12_DP   ! 許容誤差
        nn   = 2
        prev = simpsons(A, B, nn)

        do
            nn   = nn * 2
            curr = simpsons(A, B, nn)

            print "(a, i6, a, es10.3, a, f18.15)", &
                "  n=", nn, "  差=", abs(curr - prev), "  結果=", curr

            if (abs(curr - prev) < tol) exit
            prev = curr
            if (nn > 100000) exit   ! 安全ガード
        end do

        print *, ""
        print "(a, f18.15)", "  収束値  : ", curr
        print "(a, f18.15)", "  解析解  : ", EXACT
        print "(a, es10.3)", "  最終誤差: ", abs(curr - EXACT)
    end block

    print *, ""
    print *, "  虎杖悠仁・伏黒恵・釘崎野薔薇の呪力積分が完了しました。"

contains

    ! -----------------------------------------------
    ! 被積分関数: f(x) = x^2 * sin(x)
    ! -----------------------------------------------
    pure real(DP) function f(x)
        real(DP), intent(in) :: x
        f = x**2 * sin(x)
    end function f

    ! -----------------------------------------------
    ! 台形則: 区間 [a, b] を n 分割して積分します
    ! -----------------------------------------------
    real(DP) function trapezoidal(a, b, n)
        real(DP), intent(in) :: a, b
        integer,  intent(in) :: n
        real(DP) :: h, s
        integer  :: i

        h = (b - a) / n
        s = 0.5_DP * (f(a) + f(b))   ! 両端は係数 0.5

        do i = 1, n - 1
            s = s + f(a + i * h)
        end do
        trapezoidal = s * h
    end function trapezoidal

    ! -----------------------------------------------
    ! シンプソン則: 区間 [a, b] を n 分割して積分します
    ! n は偶数である必要があります
    ! -----------------------------------------------
    real(DP) function simpsons(a, b, n)
        real(DP), intent(in) :: a, b
        integer,  intent(in) :: n
        real(DP) :: h, s
        integer  :: i, nn

        nn = n
        if (mod(nn, 2) /= 0) nn = nn + 1   ! 奇数なら 1 増やします

        h = (b - a) / nn
        s = f(a) + f(b)   ! 両端は係数 1

        do i = 1, nn - 1
            if (mod(i, 2) == 1) then
                s = s + 4.0_DP * f(a + i * h)   ! 奇数インデックス: 係数 4
            else
                s = s + 2.0_DP * f(a + i * h)   ! 偶数インデックス: 係数 2
            end if
        end do
        simpsons = s * h / 3.0_DP
    end function simpsons

end program jjk_integration
gfortran jjk_integration.f90 -o jjk_integration && ./jjk_integration
===== 呪術廻戦 呪力エネルギー積分 =====
  f(x) = x^2 * sin(x)  [0, π]
  解析解 (π^2 - 4) = 5.869604401089358

  分割数     台形則              誤差(台形)        シンプソン則          誤差(シンプソン)
      10   5.869199919916741  4.0481E-04   5.869604401167741  7.8383E-11
     100   5.869600349476093  4.0516E-06   5.869604401089358  7.1054E-15
    1000   5.869604400684077  4.0528E-10   5.869604401089358  0.0000E+00

===== 五条悟の無限大呪力 収束確認 =====
  シンプソン則で分割数を倍にしながら収束を確認します。

  n=     4  差= 1.268E-08  結果=  5.869604401097723
  n=     8  差= 5.405E-11  結果=  5.869604401089358
  n=    16  差= 0.000E+00  結果=  5.869604401089358

  収束値  :  5.869604401089358
  解析解  :  5.869604401089358
  最終誤差:  0.000E+00

  虎杖悠仁・伏黒恵・釘崎野薔薇の呪力積分が完了しました。

概要

台形則は各小区間を台形で近似するアルゴリズムで、誤差は O(h²)(h は刻み幅)です。シンプソン則は隣接する 2 区間を放物線でまとめて近似するため誤差が O(h⁴) となり、同じ分割数でも大幅に精度が向上します。ただしシンプソン則は分割数を偶数にする制約があります。収束判定には、分割数 n と 2n の結果差の絶対値と許容誤差を比較する方法が一般的です。さらに高精度が必要な場合はガウス求積法を検討してください。被積分関数を contains 節内の内部副プログラムとして定義することで、メインプログラムから自然に参照でき、保守性が高まります。浮動小数点演算の精度限界については 浮動小数点の精度と丸め誤差 を参照してください。

記事の間違いや著作権の侵害等ございましたらお手数ですがまでご連絡頂ければ幸いです。