GO FOR IT(2)実数の階乗

昨日に引き続き、GO FOR ITをやってみます。

問題

ある検索サイトに5!と入力するとその計算結果である120が表示されます。
その検索サイトに2.5!と入力するとなんと3.32335097と表示されます。
さらにその検索サイトに(-1.9)!と入力すると-10.5705641と表示されます。
きっとそれらの仕組みはとても難しくて企業秘密に違いないので是非ともこれらを実行するプログラムを作ってほしい。
ただし、君のPCは古いのでネットワークや便利で高度な数学関数は入っていません。
入っている数学関数はsin,cos,tan,log,pow,floorなどの初歩的な関数のみです、残念ながら。

i)入力された整数a(0<=a<=10)の階乗を求めるプログラムを作ってください。
ii)入力された実数a(0<=a<=10)の階乗を求めるプログラムを作ってください。
iii)入力された実数a(-1.9<=a<=-1.1)の階乗を求めるプログラムを作ってください。

i)は何度もやってるので省略します。
ii)は0以上である実数の階乗を求めるプログラムをつくれと言っています。

実数の階乗なんて初めて聞いたので色々と調べてみるとどうやらガンマ関数を使えばいいことがわかります。
ガンマ関数 - Wikipedia

しかし、問題の制約として簡単な関数のみ使用可能というのがあるので、積分計算をすることはできません。
ならば、近似するしかないなーということで、ガンマ関数についてもっとよく調べてみると、スターリングの近似というのがあるではありませんか。
スターリングの近似 - Wikipedia

近似の仕方がたくさん載っていますが、その中の「計算機向けの変形」という項目が使えそう。
特にGerg Nemes が2007年に提案した近似式が、簡単な関数のみで実装できそうです。
式はこちら。

あんなに難しそうな式をこんな単純にできるなんて数学出来る人はすごいですね。

0以上の実数の階乗を求めるプログラム

<?php
function compute($z){
	$z = $z + 1;
	$a = sqrt(2*pi()/$z);
	$b = 1/exp(1);
	$c = $z + 1/(12*$z-1/(10*$z));
	$d = pow($b*$c,$z);
	return $a * $d;
}

引数を2.5としたときの実行結果

3.3233471127805

答えが「3.32335097」なので精度は十分高いといってよいのではないでしょうか。

問題なのはiii)

問題なのは正ではなく負の階乗を求めるときです。
先にあげた近似式では、平方根の中にzがあるので、zが負になると根号の中身が負になってしまい、平方根の定義を満たせなくなってしまうのです。
平方根の中身が正になるように-1をかけてあげても正しい結果が得られませんでした。
一体どうすれば…?

追記

スターリングの近似は精度があまりよくないらしいです。。。

参考
SONY GOF FOR IT ~2問目: 実数の階乗~

追記2

ようへいくんのアイディアのおかげで出来ました。負の場合はΓ(z)=Γ(z+1)/zの性質を使って非負にしてあげればよかったんですね。

※なぜかzが1未満のときにスターリングの近似を行うと正しい値を返してくれなかったので、ここでは負の場合ではなく、1未満の場合にΓ(z)=Γ(z+1)/zを使って1以上になるまで変形するようにました。

iii)のプログラム

<?php
function gammaFunc($z){
	$a = sqrt(2*pi() / $z);
	$b = 1 / exp(1);
	$c = $z + 1 / (12*$z-1 / (10*$z));
	$d = pow($b*$c,$z);
	return $a * $d;
}

function compute($z){
	$result = 1;
	$z = $z + 1;
	while($z < 1){
		$z++;
		$result = $result * 1 / ($z-1);
	}
	return $result * gammaFunc($z);
}
echo compute(-1.9);

実行結果

−10.567934926951

正解は-10.5705641なので、やはり精度はあまりよくないみたいです。