Pythonプログラミング(ステップ7・補足・ガンマ関数)

このページでは、統計分布などにしばしば登場するガンマ関数について説明する。

積分で定義されるガンマ関数

積分を使って「新しい」関数を定義する例はたくさんある。 例えば、正規分布12πet2/2 を元に、その累積分布関数 F(x)=12πxet2/2dt を定義できるが、F(x)をこれ以上簡単な数式で表すことはできない。

これは、積分範囲を「可動」にして、関数を生成する例であるが、 被積分関数の中に「可動」な変数を入れて、関数を生成することもできる。 その代表的な例がガンマ関数で、以下のように定義される: Γ(z)=0tz1etdt 被積分関数の中身のzが「可動」箇所で、tで積分しても、その部分の自由度が残るため、積分全体としてはzの関数となる。

階乗の一般化

では、ガンマ関数の値を計算してみよう。 z=1の場合は Γ(1)=0etdt=[et]0=1 z=1/2の場合は、t=y2と変数変換すると Γ(1/2)=0t1/2etdt=20ey2dy=π と求まる。 その他の一般の z に対しては値を求めることが難しい。

しかしながら、zが自然数の場合と、半整数の場合は、具体的な値を得ることができる。 まず、部分積分(etを積分、tzを微分)すると、 Γ(z+1)=0tzetdt=[tzet]0+0ztz1etdt=z0tz1etdt=zΓ(z) という関係式(漸化式)が得られる。それを繰り返し適用すると、nが自然数の場合 Γ(n+1)=nΓ(n1)=n(n1)Γ(n2)==n(n1)(n2)Γ(1)=n! となる。すなわち Γ(n+1)n! を与えることがわかった。 同様に、半整数に対しても Γ(2n+12)=2n122n32Γ(1/2) となるので、具体的な値を得ることができる。

このように、ガンマ関数は、階乗を実数、さらには複素数に拡張したものと考えることができる。 すると、自然数nについて f(n+1)=n! となるような関数は他にあっても構わないように思われる。 事実、例えば f(x)=Γ(x)+sin(πx) のようにすれば、いくらでも「階乗もどき」の関数が作れるはずだ。

しかしながら、そのような関数のうち、(z)>0の領域で logf(z) が下に凸であるような複素解析関数は唯一ガンマ関数であることが証明されている (Bohr–Mollerup Theorem)。

ガンマ関数の近似:スターリングの式

統計学や物理学などでは、大きなnに対する階乗(n!)の振る舞いを知る必要性が出てくる。 そこで、n!の良い見積を得るために、その積分表示であるΓ(n+1)を使ってみよう。 積分変数をt=zsと変換すると Γ(z+1)=0tzetdt=0(sz)zeszzds=zz+10szeszds=zz+10ez(log(s)s)ds

ここで、eの「上」の z(log(s)s) は上に凸な形をしており、微分を取ると、s=1で最大になることがわかる。 これを、s=1の周りでテイラー展開すると z(log(s)s)=zz2(s1)2+ である。そこで、 Γ(z+1)=zz+1ez0ez2(s1)2+ds と書き直して、zが正の大きな値の場合の積分の振る舞いを考えてみる。

zが十分大きければ、被積分関数はs=1の周りで鋭いピークを持つので、その値は以下の式でよく近似できるであろう: 0ez2(s1)2+dsez2(s1)2ds=2πz ピークの幅が鋭くなれば、「遠く」の様子は関係なくなるであろうから、積分範囲をまで広げても結果は変わらないはずである。 また、展開の高次の項も無視できるとした(本来ならば誤差の評価を行うべきであるが、ここでは割愛)。

zの値が大きくなると、指数関数の肩のz(log(s)s)は幅が狭まり鋭い頂点を持つようになるので、 頂点の辺りだけを放物線で表したとしても、よい近似が得られる。

ここまでをまとめると、スターリングの式として知られる n!=Γ(n+1)nn+1en2πn=nn+1/2en2π が得られた。

math.gamma( )

Pythonでは、ガンマ関数は数学ライブラリとして用意されており、積分を行わずとも、import math しておけば、math.gamma( )で呼び出すことができる。以下に、ガンマ関数の値とスターリングの近似値のそれぞれ対数を計算し、比較してプロットするコードの例を示す。

実行には、matplotlibとNumPyが必要。

# coding: utf-8

import matplotlib.pyplot as plt
import numpy as np
import math

xdata = np.arange(1,10,0.01)
n = xdata.shape[0]

log_gamma = np.empty(n)
log_stirling = np.empty(n)

for i in range(n):
    x = xdata[i]
    log_gamma[i] = math.log(math.gamma(x+1))
    log_stirling[i] = (x+1/2)*math.log(x) - x + 1/2*math.log(2*math.pi)

plt.title("GAMMA FUNCTION")
plt.plot(xdata,log_gamma, color=(1.0,0,0.0), linewidth=1.0)
plt.plot(xdata,log_stirling, color=(0.0,0,1.0), linewidth=1.0)
plt.xlim(0,10)
plt.xlabel('X')
plt.ylabel('LOG GAMMA(X)')
plt.grid(True)
plt.show()

赤がガンマ関数、青がスターリングの式。 スターリングの式は、xの値が比較的小さい領域でも、驚くべき正確さを発揮することがわかる。



ベータ関数

ガンマ関数の親戚で、統計学などでよく登場する二変数の関数にベータ関数がある。 ベータ関数はいろいろな定義(表現)が可能であるが、ひとまず、ガンマ関数との関係 B(x,y)=Γ(x)Γ(y)Γ(x+y) を押さえておけば、Pythonのmathライブラリを使って、

math.gamma(x)*math.gamma(y)/math.gamma(x+y)

で計算が可能である。