数値的に広義積分を計算するための変数置換のコツ
📂数値解析数値的に広義積分を計算するための変数置換のコツ
定理
0<a<b<∞ としよう。
- [1]: 0<p<1 ならば ∫0bxpf(x)dx=∫01−p1b1−pf([(1−p)m]1−p1)dm
- [2]: 1<p ならば ∫a∞xpf(x)dx=∫0p−11a1−pf([(p−1)m]1−p1)dm
説明
不適切積分はいつも頭痛の種だけど、解くのを諦めるわけにはいかない。積分が通常難しい理由は、どんなトリックを使っても、それらのトリックが結局新しい問題を作り出すからだ。幸い数値積分では、関数を正確に持っていれば、詳細なプロセスがなくてもとにかく計算を進めることができる。代表的な方法は代入で、関数が特異であっても、xp のような項を分母から引くことができれば、簡単に解決できる。
例
回避積分
例として、次の不適切積分を考えてみよう。
∫01xe−xdx≈1.493648265625
m(x):=∫0xt1dt=2x とすると xdm=dx となるので
∫01xe−xdx===∫m=02xe−xxdm∫02e−xdm∫02e−m2/4dm
このようなトリックを使って閉区間に対する積分を作れば、積分技術を適用できる。実際に台形則を使って数値を計算してみると、次のような結果が得られる。

速度の向上
一方で、より興味深い例として、次の場合を見てみよう。
∫01e−xxdx≈0.378944691641
最初に思い浮かぶのは「特に代入が必要ない」という点だろう。e−xx は[0,1] で特異性を持たないので、そのまま 台形則 を適用できる。しかし、x=0 で微分不可能であるため、エラーのバウンドがなくなり、収束速度が遅くなる。同様に m(x):=∫0xt1dt=2x とすると xdm=dx となるので
∫01e−xxdx===∫m=02e−xxxdm∫02e−xxdm∫02e−m2/44m2dm
見ての通り、代入積分を行うと、m=0 の特異性がなくなる。同じように台形則 を使って数値を計算してみると、次のような結果が得られる。左がそのまま計算したもので、右が代入を行って計算したものだ。
代入なしでも収束はするが、代入を通じた計算結果が怖いほど真値に収束するのに対して、代入なしで計算した値は収束速度が遅いことが確認できる。数値解析の特徴として速度を重視するため、このようなロスはできるだけ減らすのが良い。いつか選ばなくてはならないときが来たら、コードの簡潔さ、美しさよりも、数学者の細かくて丁寧なトリックを選んだ方が良いだろう。
証明
戦略:実際の計算はコンピュータに任せるため、閉区間での積分に変換するだけで良い。方法は本質的に同じだ。
[1]
m:=∫0xtp1dt=1−p1x1−p のようにすると dxdm=x−p⟺xpdm=dx となるので
∫0bxpf(x)dx==∫m=01−p1b1−pxpf(x)xpdm∫01−p1b1−pf([(1−p)m]1−p1)dm
■
[2]
m:=∫x∞tp1dt=−1−p1x1−p のようにすると dxdm=−x−p⟺−xpdm=dx となるので
∫a∞xpf(x)dx==∫m=−1−p1a1−p0xpf(x)(−xp)dm∫0p−11a1−pf([(p−1)m]1−p1)dm
■
実装
次は、例を数値的に解くためのPythonのコードだ。
import numpy as np
def f1(x) :
return np.exp(-x)
def f2(x) :
return np.exp(-x)*np.sqrt(x)
def f3(x) :
return np.exp(-x)*x
def I(f,a,b,n) :
h=(b-a)/n
x=np.linspace(a,b,n+1)
x=f(x)
x[0]=x[0]/2
x[-1]=x[-1]/2
return(h*sum(x))
def CVI(f,a,b,n) :
b=2*np.sqrt(b)
h=(b-a)/n
x=np.linspace(a,b,n+1)
x=f((x**2)/4)
x[0]=x[0]/2
x[-1]=x[-1]/2
return(h*sum(x))
TV1=1.49364826562485405079
TV2=0.37894469164098470380
print("")
print("True Value 1 : %2.12f" % TV1)
print("| n | In | I-In |")
print("="*32)
for n in range(3,10) :
print("| %3d | %8.6f | %9.2e |" % (
2**n,
CVI(f1,0,1,2**n),
TV1-CVI(f1,0,1,2**n)))
print("")
print("True Value 2 : %2.12f" % TV2)
print("| n | In | I-In |")
print("="*32)
for n in range(3,10) :
print("| %3d | %8.6f | %9.2e |" % (
2**n,
I(f2,0,1,2**n),
TV2-I(f2,0,1,2**n)))
print("")
print("True Value 2 : %2.12f" % TV2)
print("| n | In | I-In |")
print("="*32)
for n in range(3,10) :
print("| %3d | %8.6f | %9.2e |" % (
2**n,
CVI(f3,0,1,2**n),
TV2-CVI(f3,0,1,2**n)))
print("")