2020/08/26 C言語辞典の不完全ガンマ関数を修正したプログラムに第二種不完全ガンマ関数を直接計算するルーチンも修正追加しました。
不完全ガンマ関数
不完全ガンマ関数は、0~X と X~∞の不定積分です。
第1種不完全ガンマ関数は0~Xの不定積分で、第2種不完全ガンマ関数はX~∞の不定積分となります。
不完全ガンマ関数は、それ自身がそのまま使用されることは無く、分散や確率の計算に使用されます。
詳細は、ウィキペディア 不完全ガンマ関数 を参照してください。
プログラム
次のプログラムはMathematics Source Library C & ASM の
incomplete_gamma_function.c をDelphiに変換したものです。
使用されていないルーチンや無駄なルーチンもあるので、他のプログラムに組み込む場合は、後述のプログラムの方が、小さく簡単です。
実行画面は、同じです。
//---------------------------------------------------------------- // http://www.mymathlib.com/functions/ // Mathematics Source Library C & ASM // gamma function convert C -> delphi //---------------------------------------------------------------- unit main; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.Buttons, system.UITypes, VclTee.TeeGDIPlus, VCLTee.Series, VCLTee.TeEngine, VCLTee.TeeProcs, VCLTee.Chart; type TForm1 = class(TForm) BitBtn1: TBitBtn; LabeledEdit1: TLabeledEdit; LabeledEdit2: TLabeledEdit; Memo1: TMemo; Chart1: TChart; Series1: TLineSeries; Series2: TLineSeries; Series3: TPointSeries; Series4: TPointSeries; procedure FormCreate(Sender: TObject); procedure BitBtn1Click(Sender: TObject); private { Private 宣言 } function Gamma_Function(x: double): double; function xGamma_Function(x: extended): extended; function xGamma(x: extended): extended; function Duplication_Formula(two_x: extended): extended; function Gamma_Function_Max_Arg: double; function xGamma_Function_Max_Arg: extended; function Ln_Gamma_Function(x: double): double; function xLn_Gamma_Function(x: extended): extended; function xLnGamma_Asymptotic_Expansion(x: extended): extended; function Factorial(n: integer): double; function xFactorial(n :integer): extended; function Factorial_Max_Arg: integer; function Entire_Incomplete_Gamma_Function(x, nu: double): double; function xEntire_Incomplete_Gamma_Function(x, nu: extended): extended; function xSmall_x(x, nu: extended): extended; function xMedium_x(x, nu: extended): extended; function xLarge_x(x, nu: extended): extended; function Incomplete_Gamma_Function(x, nu: double): double; function xIncomplete_Gamma_Function(x, nu: extended): extended; public { Public 宣言 } end; var Form1: TForm1; implementation {$R *.dfm} uses math; const LONG_MAX = 2147483647; e = 2.71828182845904523536028747; exp_g_o_sqrt_2pi = 6.23316569877722552586386e+3; g = 9.65657815377331589457187; max_double_arg: double = 171.6243769; max_long_double_arg: extended = 1755.5; a: array[0..8] of extended = ( +1.14400529453851095667309e+4, -3.23988020152318335053598e+4, +3.50514523505571666566083e+4, -1.81641309541260702610647e+4, +4.63232990536666818409138e+3, -5.36976777703356780555748e+2, +2.28754473395181007645155e+1, -2.17925748738865115560082e-1, +1.08314836272589368860689e-4 ); //============================================================================= // // この関数はLanczosの式を使用して実際のGamma(x)を計算します。 // xの範囲は -(max_double_arg-1) < x < max_double_arg。 // ガンマ関数は複素平面で有理型であり、 // 正でない整数の値、xaの正の整数または半分の正の整数をテストすると、 // 約1.9e-16の最大絶対相対誤差です。 // x > max_double_argの場合、xGamma_Function(x)を使用する必要があります。 // または lnGamma(x) を計算します。 // x < 0の場合、ln(Gamma(x))は複素数になる可能性があることに注意してください。 // 複素数の計算はプログラムに組んでありません // 戻り値 // xが正で、max_double_argより小さい場合、Gamma(x)は // 返され、x> max_double_argの場合、maxdoubleが返されます。 // xが非正の整数、つまりxが極の場合、maxdoubleが返されます。 // (Gamma(x)は極の両側で符号を変更することに注意してください)。 // xが非正の非整数の場合 Gamma(x)> maxdouble場合、maxdoubleが返され、 // Gamma(x)<-maxdoubleの場合、-maxdoubleが返されます。 // //============================================================================= //================ ガンマ関数 =================== // Γ(x) function TForm1.Gamma_Function(x: double): double; var g: extended; maxx: double; begin maxx := max_double_arg; if x >= maxx then begin result := maxdouble; exit; end; g := xGamma_Function(x); if abs(g) < maxdouble then result := g else if g < 0 then result := -maxdouble else result := maxdouble; end; //============================================================================= // // この関数はLanczosの式を使用して実際のGamma(x)を計算します。 // xの範囲は -(max_long_double_arg-1) < x < max_long_double_arg。 // ガンマ関数は複素平面で有理型であり、正でない整数の値 // xaの正の整数または半分の正の整数をテストすると、 // 最大絶対相対誤差は約3.5e-16です。 // // x > max_long_double_argの場合、lnGamma(x)を使用する必要があります。 // x < 0 の場合、ln(Gamma(x)) は複素数になる可能性があることに注意してください。 // // 戻り値 // xが正で、max_long_double_argより小さい場合、Gamma(x) // が返され、x > max_long_double_argの場合、maxextendedが返されます。 // xが正でない整数の場合、maxextendedが // 返されます(Gamma(x)は値の両側で符号を変更することに注意してください)。 // xが非正の非整数の場合でx>-(max_long_double_arg + 1)の場合 // Gamma(x)が返されます。それ以外の場合は0.0が返されます // //============================================================================= function TForm1.xGamma_Function(x: extended): extended; var sin_x: extended; rg: extended; ix: int64; begin if x > 0 then if x <= max_long_double_arg then begin result := xGamma(x); exit; end else begin result := maxextended; exit; end; if x > -LONG_MAX then begin ix := round(x); if x = ix then begin result := maxextended; exit; end; end; sin_x := sin(pi * x); if sin_x = 0 then begin result := maxextended; exit; end; if x < - max_long_double_arg - 1 then begin result := 0; exit; end; rg := xGamma(1 - x) * sin_x / pi; if rg <> 0 then result := 1 / rg else result := maxextended; end; //============================================================================= // // この関数はLanczosの式を使用して実際のGamma(x)を計算します。 // x、ここで0<x<=900。900<x<1755.5の場合、倍数公式を使用します。 // 関数power()。結果の相対誤差は約10^-16です。x = 0付近を除きます。 // x>1755.5の場合、lnGamma(x)を計算する必要があります。 // xが正で1755.5未満の場合、Gamma(x)が返され、 // x>1755.5の場合、maxextendedが返されます。 // //============================================================================= function TForm1.xGamma(x: extended): extended; var xx: extended; temp: extended; n, i: integer; xx2 : extended; begin if x < 1 then xx := x + 1 else xx := x; n := sizeof(a) div sizeof(extended); if x > max_long_double_arg then begin result := maxextended; exit; end; if x > 900 then begin result := Duplication_Formula(x); exit; end; temp := 0; for i := n - 1 downto 0 do temp := temp + a[i] / (xx + i); temp := temp + 1; xx2 := (xx - 0.5) / 2; temp := temp * (power((g + xx - 0.5) / e, xx2) / exp_g_o_sqrt_2pi ); temp := temp * power((g + xx - 0.5) / e, xx2); // X64 オーバーフロー対策 if x < 1 then result := temp / x else result := temp; end; //============================================================================= // // この関数は倍数公式を使用してGamma(two_x)を返します。 // //============================================================================= function TForm1.Duplication_Formula(two_x: extended): extended; var x: extended; g: extended; n: integer; begin x := 0.5 * two_x; n := round(two_x) - 1; g := power(2.0, two_x - 1.0 - n); g := g * power(2, n); g := g / sqrt(pi); g := g * xGamma_Function(x); g := g * xGamma_Function(x + 0.5); result := g; end; //============================================================================= // // この関数は、Gamma_Functionの最大引数を返します。 // Gamma_Functionは引数が1より大きい場合、< maxdoubleという数値が返されます。 // //============================================================================= function TForm1.Gamma_Function_Max_Arg: double; begin result := max_double_arg; end; //============================================================================= // // この関数は、xGamma_Functionの最大引数を返します。 // xGamma_Functionは引数が1より大きい場合、< maxextendedという数値が返されます。 // //============================================================================= function TForm1.xGamma_Function_Max_Arg: extended; begin result := max_long_double_arg; end; //***************************************************************************** //============================================================================= // // 正の実数xに対するGamma(x)の自然対数を計算します。 // //============================================================================= //============================================================================= // Ln_Gamma_Function(x) および xLn_Gamma_Function(x) // // Gamma_Function_Max_Arg = 171とすると、 // 0 < x <= 171の場合、ln(gamma(x))は、Gamma_Function(x)からの結果。 // x> 171の場合、ln(gamma(x)は漸近展開を使用して計算されます。 // ln(gamma(x)〜ln(2sqrt(2pi))-x + (x-1/2)ln x + // B[2j] / [2j *(2j-1)* x^(2j-1)]の合計、 // 合計 jは1から3で、B[2j]は2j番目のベルヌーイ数です。 // //============================================================================= function TForm1.Ln_Gamma_Function(x: double): double; begin // 正の引数の 0 <x <= Gamma_Function_Max_Arg // の場合、Log(Gamma(x)を返します。 if x <= Gamma_Function_Max_Arg then result := ln(Gamma_Function(x)) //そうでない場合は、ln Gamma(x) の漸近展開から結果を返します。 else result := xLnGamma_Asymptotic_Expansion(x); end; //============================================================================= // // 正の実数のGamma(x)の自然対数を計算します // // Gamma_Function_Max_Arg = 171とすると、 // 0 <x <= 171の場合、ln(gamma(x))は // Gamma_Function(x)からの結果の値。 // x> 171の場合、ln(gamma(x)) は漸近展開を使用して計算されます。 // ln(gamma(x))〜ln(2sqrt(2pi))-x +(x-1/2)ln x + // B[2j] / [2j *(2j-1)* x^(2j-1)]の合計、 // 合計 jは1から3で、B[2j]は2j番目のベルヌーイ数です。 // //============================================================================= function TForm1.xLn_Gamma_Function(x: extended): extended; begin // 正の引数の場合、0 <x <= Gamma_Function_Max_Arg() // の場合、LogGamma(x)を返します。 if x <= xGamma_Function_Max_Arg then result := ln(xGamma_Function(x)) // そうでない場合は、ln Gamma(x) の漸近展開から結果を返します。 else result := xLnGamma_Asymptotic_Expansion(x); end; //============================================================================= // // この関数は、漸近を評価することでlog(gamma(x)を推定します // // ln(Gamma(x))〜ln(2sqrt(2pi))-x +(x-1/2)ln(x) + // B[2j] / [2j *(2j-1)* x^(2j-1)]の合計、 // 合計 jは1から3で、B[2j]は2j番目のベルヌーイ数です。 // //============================================================================= const log_sqrt_2pi = 9.18938533204672741780329736e-1; //ベルヌーイ数B(2)、B(4)、B(6)、...、B(20) B : array[0..9] of extended = ( 1.0 / (6 * 2 * 1), -1.0 / (30 * 4 * 3), 1.0 / (42 * 6 * 5), -1.0 / (30 * 8 * 7), 5.0 / (66 * 10 * 9), -691.0 / (2730 * 12 * 11), 7.0 / (6 * 14 * 13), -3617.0 / (510 * 16 * 15), 43867.0 / (798 * 18 * 17), -174611.0 / (330 * 20 * 19)); m: integer = sizeof(B) div sizeof(extended); function TForm1.xLnGamma_Asymptotic_Expansion(x: extended): extended; //const // m = 3; // 3で十分な精度が得られます。 var i : integer; term: array[0..9] of extended; sum : extended; xx : extended; xj : extended; lngamma : extended; begin sum := 0; xx := x * x; xj := x; lngamma := log_sqrt_2pi - xj + (xj - 0.5) * ln(xj); for i := 0 to m - 1 do begin term[i] := B[i] / xj; xj := xj * xx; end; for i := m - 1 downto 0 do sum := sum + term[i]; result := lngamma + sum; end; //============================================================================== //////////////////////////////////////////////////////////////////////////////// // 階乗 ルーチン: // // xFactorial // // Factorial_Max_Arg // //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // 関数Factorial(n)およびxFactorial(n)はn!を返します。 // // 0 <= n <= Factorial_Max_Arg()の場合。n> Factorial_Max_Argの場合、 // // 次にDBL_MAXが返され、n <0の場合は0が返されます。 // // 関数Factorial_Max_Argは最大引数を返します // // 関数FactorialおよびxFactorial。 // //////////////////////////////////////////////////////////////////////////////// const factorials: array [0..170] of extended = ( 1.000000000000000000000e+0, // 0! 1.000000000000000000000e+0, // 1! 2.000000000000000000000e+0, // 2! 6.000000000000000000000e+0, // 3! 2.400000000000000000000e+1, // 4! 1.200000000000000000000e+2, // 5! 7.200000000000000000000e+2, // 6! 5.040000000000000000000e+3, // 7! 4.032000000000000000000e+4, // 8! 3.628800000000000000000e+5, // 9! 3.628800000000000000000e+6, // 10! 3.991680000000000000000e+7, // 11! 4.790016000000000000000e+8, // 12! 6.227020800000000000000e+9, // 13! 8.717829120000000000000e+10, // 14! 1.307674368000000000000e+12, // 15! 2.092278988800000000000e+13, // 16! 3.556874280960000000000e+14, // 17! 6.402373705728000000000e+15, // 18! 1.216451004088320000000e+17, // 19! 2.432902008176640000000e+18, // 20! 5.109094217170944000000e+19, // 21! 1.124000727777607680000e+21, // 22! 2.585201673888497664000e+22, // 23! 6.204484017332394393600e+23, // 24! 1.551121004333098598400e+25, // 25! 4.032914611266056355840e+26, // 26! 1.088886945041835216077e+28, // 27! 3.048883446117138605015e+29, // 28! 8.841761993739701954544e+30, // 29! 2.652528598121910586363e+32, // 30! 8.222838654177922817726e+33, // 31! 2.631308369336935301672e+35, // 32! 8.683317618811886495518e+36, // 33! 2.952327990396041408476e+38, // 34! 1.033314796638614492967e+40, // 35! 3.719933267899012174680e+41, // 36! 1.376375309122634504632e+43, // 37! 5.230226174666011117600e+44, // 38! 2.039788208119744335864e+46, // 39! 8.159152832478977343456e+47, // 40! 3.345252661316380710817e+49, // 41! 1.405006117752879898543e+51, // 42! 6.041526306337383563736e+52, // 43! 2.658271574788448768044e+54, // 44! 1.196222208654801945620e+56, // 45! 5.502622159812088949850e+57, // 46! 2.586232415111681806430e+59, // 47! 1.241391559253607267086e+61, // 48! 6.082818640342675608723e+62, // 49! 3.041409320171337804361e+64, // 50! 1.551118753287382280224e+66, // 51! 8.065817517094387857166e+67, // 52! 4.274883284060025564298e+69, // 53! 2.308436973392413804721e+71, // 54! 1.269640335365827592597e+73, // 55! 7.109985878048634518540e+74, // 56! 4.052691950487721675568e+76, // 57! 2.350561331282878571829e+78, // 58! 1.386831185456898357379e+80, // 59! 8.320987112741390144276e+81, // 60! 5.075802138772247988009e+83, // 61! 3.146997326038793752565e+85, // 62! 1.982608315404440064116e+87, // 63! 1.268869321858841641034e+89, // 64! 8.247650592082470666723e+90, // 65! 5.443449390774430640037e+92, // 66! 3.647111091818868528825e+94, // 67! 2.480035542436830599601e+96, // 68! 1.711224524281413113725e+98, // 69! 1.197857166996989179607e+100, // 70! 8.504785885678623175212e+101, // 71! 6.123445837688608686152e+103, // 72! 4.470115461512684340891e+105, // 73! 3.307885441519386412260e+107, // 74! 2.480914081139539809195e+109, // 75! 1.885494701666050254988e+111, // 76! 1.451830920282858696341e+113, // 77! 1.132428117820629783146e+115, // 78! 8.946182130782975286851e+116, // 79! 7.156945704626380229481e+118, // 80! 5.797126020747367985880e+120, // 81! 4.753643337012841748421e+122, // 82! 3.945523969720658651190e+124, // 83! 3.314240134565353266999e+126, // 84! 2.817104114380550276949e+128, // 85! 2.422709538367273238177e+130, // 86! 2.107757298379527717214e+132, // 87! 1.854826422573984391148e+134, // 88! 1.650795516090846108122e+136, // 89! 1.485715964481761497310e+138, // 90! 1.352001527678402962552e+140, // 91! 1.243841405464130725548e+142, // 92! 1.156772507081641574759e+144, // 93! 1.087366156656743080274e+146, // 94! 1.032997848823905926260e+148, // 95! 9.916779348709496892096e+149, // 96! 9.619275968248211985333e+151, // 97! 9.426890448883247745626e+153, // 98! 9.332621544394415268170e+155, // 99! 9.332621544394415268170e+157, // 100! 9.425947759838359420852e+159, // 101! 9.614466715035126609269e+161, // 102! 9.902900716486180407547e+163, // 103! 1.029901674514562762385e+166, // 104! 1.081396758240290900504e+168, // 105! 1.146280563734708354534e+170, // 106! 1.226520203196137939352e+172, // 107! 1.324641819451828974500e+174, // 108! 1.443859583202493582205e+176, // 109! 1.588245541522742940425e+178, // 110! 1.762952551090244663872e+180, // 111! 1.974506857221074023537e+182, // 112! 2.231192748659813646597e+184, // 113! 2.543559733472187557120e+186, // 114! 2.925093693493015690688e+188, // 115! 3.393108684451898201198e+190, // 116! 3.969937160808720895402e+192, // 117! 4.684525849754290656574e+194, // 118! 5.574585761207605881323e+196, // 119! 6.689502913449127057588e+198, // 120! 8.094298525273443739682e+200, // 121! 9.875044200833601362412e+202, // 122! 1.214630436702532967577e+205, // 123! 1.506141741511140879795e+207, // 124! 1.882677176888926099744e+209, // 125! 2.372173242880046885677e+211, // 126! 3.012660018457659544810e+213, // 127! 3.856204823625804217357e+215, // 128! 4.974504222477287440390e+217, // 129! 6.466855489220473672507e+219, // 130! 8.471580690878820510985e+221, // 131! 1.118248651196004307450e+224, // 132! 1.487270706090685728908e+226, // 133! 1.992942746161518876737e+228, // 134! 2.690472707318050483595e+230, // 135! 3.659042881952548657690e+232, // 136! 5.012888748274991661035e+234, // 137! 6.917786472619488492228e+236, // 138! 9.615723196941089004197e+238, // 139! 1.346201247571752460588e+241, // 140! 1.898143759076170969429e+243, // 141! 2.695364137888162776589e+245, // 142! 3.854370717180072770522e+247, // 143! 5.550293832739304789551e+249, // 144! 8.047926057471991944849e+251, // 145! 1.174997204390910823948e+254, // 146! 1.727245890454638911203e+256, // 147! 2.556323917872865588581e+258, // 148! 3.808922637630569726986e+260, // 149! 5.713383956445854590479e+262, // 150! 8.627209774233240431623e+264, // 151! 1.311335885683452545607e+267, // 152! 2.006343905095682394778e+269, // 153! 3.089769613847350887959e+271, // 154! 4.789142901463393876336e+273, // 155! 7.471062926282894447084e+275, // 156! 1.172956879426414428192e+278, // 157! 1.853271869493734796544e+280, // 158! 2.946702272495038326504e+282, // 159! 4.714723635992061322407e+284, // 160! 7.590705053947218729075e+286, // 161! 1.229694218739449434110e+289, // 162! 2.004401576545302577600e+291, // 163! 3.287218585534296227263e+293, // 164! 5.423910666131588774984e+295, // 165! 9.003691705778437366474e+297, // 166! 1.503616514864999040201e+300, // 167! 2.526075744973198387538e+302, // 168! 4.269068009004705274939e+304, // 169! 7.257415615307998967397e+306 // 170! ); NN: integer = sizeof(factorials) div sizeof(extended); ////////////////////////////////////////////////////////////////////////////////// // function Factorial(n: integer): // // この関数はn!を計算します0 <= n <= Factorial_Max_Arg()の場合。 // // n> Factorial_Max_Arg()の場合、DBL_MAXが返され、n <0の場合、0が返されます。 // // // //引数: // // n 階乗関数の引数。 // // // //戻り値: // // nが負の場合、0が返されます。n> Factorial_Max_Argの場合、DBL_MAXが返されます。// // 0 <= n <= Factorial_Max_Arg()の場合、 n!返されます。 // // // ////////////////////////////////////////////////////////////////////////////////// function TForm1.Factorial(n: integer): double; begin // For a negative argument (n < 0) return 0.0 // if n < 0 then begin result := 0.0; exit; end; // For a large postive argument (n >= N) return DBL_MAX // if n >= NN then begin result := Maxdouble; exit; end; // Otherwise return n!. result := factorials[n]; end; ///////////////////////////////////////////////////////////////////////////////// // function xFactorial(n : integer): extended; // // // // この関数はn!を計算します 0 <= n <= Factorial_Max_Arg()の場合 // // n> Factorial_Max_Arg()の場合、DBL_MAXが返され、n <0の場合、0が返されます。// // // // 戻り値: // // nが負の場合、0が返されます。n> Factorial_Max_Arg()の場合、 // // 次にDBL_MAXが返されます。0 <= n <= Factorial_Max_Arg()の場合、 // // n!返されます。 // ///////////////////////////////////////////////////////////////////////////////// function TForm1.xFactorial(n: integer): extended; begin // For a negative argument (n < 0) return 0.0 if n < 0 then begin result := 0.0; exit; end; // For a large postive argument (n >= N) return DBL_MAX if n >= NN then begin result := MaxDouble; exit; end; // Otherwise return n!. result := factorials[n]; end; //////////////////////////////////////////////////////////////////////////////// // function Factorial_Max_Arg: integer // // // // この関数は、Factial関数の最大引数を返します // //////////////////////////////////////////////////////////////////////////////// function TForm1.Factorial_Max_Arg: integer; begin result := NN - 1; end; //////////////////////////////////////////////////////////////////////////////// // Entire_Incomplete_Gamma_Function // // xEntire_Incomplete_Gamma_Function // // 不完全なガンマ関数全体。正規化とも呼ばれます // // 不完全なガンマ関数、0からxまでの積分として定義 // // 被積分関数t ^(nu-1)exp(-t)/ gamma(nu)dt。 // // パラメータnuは 形状パラメータと呼ばれることもあります。 // ////////////////////////////////////////////////// ///////////////////////////// ////////////////////////////////////////////////// ///////////////////////////// // function Entire_Incomplete_Gamma_Function(x,nu: double): double // // 引数 // // x : double 被積分関数で与えられる上限 // // nu: double不完全なガンマ関数全体の形状パラメーター。 // ////////////////////////////////////////////////// ///////////////////////////// function TForm1.Entire_Incomplete_Gamma_Function(x, nu: double): double; begin result := xEntire_Incomplete_Gamma_Function(x, nu); end; ////////////////////////////////////////////////// ////////////////////////////// // function xEntire_Incomplete_Gamma_Function(x、nu: extended): extended // // 不完全なガンマ関数全体。正規化とも呼ばれます // // 不完全なガンマ関数、0からxのxまでの積分として定義 // // 被積分関数 t ^(nu-1)exp(-t)/ gamma(nu)dt // // パラメータnuは 形状パラメータと呼ばれることもあります。 // // // // 引数: // // x: extended 上記の被積分関数を使用した積分の上限。 // // nu: extended 不完全なガンマ全体の形状パラメータ 。 // ////////////////////////////////////////////////// ////////////////////////////// function TForm1.xEntire_Incomplete_Gamma_Function(x, nu: extended): extended; begin if x = 0.0 then begin result := 0.0; exit; end; if abs(x) <= 1.0 then begin result := xSmall_x(x, nu); exit; end; if abs(x) < (nu + 1.0) then begin result := xMedium_x(x, nu); exit; end; result := xLarge_x(x, nu); end; ///////////////////////////////////////////////////////////////////////////////// // function xSmall_x(x、nu: extended): extended; // // // // この関数は、不完全なガンマ関数全体を近似します // // x、ここで -1 <= x <=1 // // 引数: // // x extended 被積分関数が記述された積分の上限 // // Entire_Incomplete_Gamma_Functionの下のセクション。 // // long double nu不完全なガンマ全体の形状パラメータ // // 戻り値: // // 不完全なガンマ関数全体: // // I(0、x)t ^(nu-1)Exp(-t)dt / Gamma(nu)。 // ///////////////////////////////////////////////////////////////////////////////// const Nterms= 20; function TForm1.xSmall_x(x, nu: extended): extended; var terms : array [0..Nterms - 1] of extended; x_term : extended; x_power : extended; sum : extended; i : integer; begin x_term := -x; x_power := 1.0; for i := 0 to Nterms - 1 do begin terms[i] := (x_power / xFactorial(i)) / (i + nu); x_power := x_power * x_term; end; sum := terms[Nterms-1]; for i := Nterms-2 downto 0 do sum := sum + terms[i]; if nu <= Gamma_Function_Max_Arg then result := power(x,nu) * sum / xGamma_Function(nu) else result := exp(nu * ln(x) + ln(sum) - xLn_Gamma_Function(nu)); end; //////////////////////////////////////////////////////////////////////////////// // function xMedium_x(x、nu: extended):extended // // // //この関数は、不完全なガンマ関数全体を近似します // // x、ここで 1 <x <nu +1 // // // // nu + 1 < xの場合、xLarge_x(x、nu)を使用する必要があります。 // // // // 引数: // // x : extended 被積分関数が記述された積分の上限 // // Entire_Incomplete_Gamma_Functionの下のセクション。 // // nu: extended 不完全なガンマ全体の形状パラメータ // // 戻り値: // // 不完全なガンマ関数全体: // // I(0、x)t ^(nu-1)exp(-t)dt / gamma(nu) // //////////////////////////////////////////////////////////////////////////////// var DBL_EPSILON : double; function TForm1.xMedium_x(x, nu: extended): extended; var coef : extended; term : extended; corrected_term : extended; temp_sum : extended; correction : extended; sum1 : extended; sum2 : extended; epsilon : extended; i : integer; begin term := 1.0 / nu; corrected_term := term; temp_sum := term; correction := -temp_sum + corrected_term; sum1 := temp_sum; epsilon := 0.0; if nu > Gamma_Function_Max_Arg then begin coef := exp(nu * ln(x) - x - xLn_Gamma_Function(nu)); if coef > 0.0 then epsilon := DBL_EPSILON / coef; end else begin coef := power(x, nu) * exp(-x) / xGamma_Function(nu); epsilon := DBL_EPSILON / coef; end; if epsilon <= 0.0 then epsilon := DBL_EPSILON; i := 1; while term > epsilon * sum1 do begin term := term * x / (nu + i); corrected_term := term + correction; temp_sum := sum1 + corrected_term; correction := (sum1 - temp_sum) + corrected_term; sum1 := temp_sum; inc(i); end; sum2 := sum1; sum1 := sum1 * coef; correction := correction + sum2 - sum1 / coef; term := term * x / (nu + i); sum2 := term + correction; inc(i); while term + correction > epsilon * sum2 do begin term := term * x / (nu + i); corrected_term := term + correction; temp_sum := sum2 + corrected_term; correction := (sum2 - temp_sum) + corrected_term; sum2 := temp_sum; inc(i); end; sum2 := sum2 + correction; sum2 := sum2 * coef; result := sum1 + sum2; end; ///////////////////////////////////////////////////////////////////////////////// // function xLarge_x(x、nu: extended) // // // // この関数は、不完全なガンマ関数全体を//近似します // // x、ここで nu + 1 <= x // // // // 0 <= x <nu + 1の場合、xSmall_x(x、nu)を使用する必要があります // // // // 引数: // // x: extended被積分関数が記述された積分の上限 // // Entire_Incomplete_Gamma_Functionの下のセクション // // nu: extended 不完全なガンマ全体の形状パラメータ // // 戻り値: // // xが正で171より小さい場合、Gamma(x)が返され // // x> 171の場合、MAXDoubleが返されます。 // ///////////////////////////////////////////////////////////////////////////////// function TForm1.xLarge_x(x, nu: extended): extended; var temp : extended; sum : extended; coef : extended; i : integer; n : integer; begin temp := 1.0 / nu; sum := temp; n := trunc(x - nu - 1.0) + 1; for i := 1 to n - 1 do begin temp := temp * x / (nu + i); sum := sum + temp; end; if nu <= Gamma_Function_Max_Arg then begin coef := power(x, nu) * exp(-x) / xGamma_Function(nu); result := xMedium_x(x, nu + n) + coef * sum; end else result := exp(ln(sum) + nu * ln(x) - x - xLn_Gamma_Function(nu)) + xMedium_x(x, nu + n); end; ///////////////////////////////////////////////////////////////////////////////// //ファイル:incomplete_gamma_function // // // // Incomplete_Gamma_Function // // xIncomplete_Gamma_Function // // 不完全なガンマ関数は0からxまでの積分として定義されます // // 被積分関数t ^(nu-1)exp(-t)dtのパラメータnuは // // 時々 形状パラメータと呼ばれます。 // ///////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////// // function Incomplete_Gamma_Function(x、nu: double):double; // // 引数: // // x: double 与えられた被積分関数を使った積分の上限 // // nu: double 不完全なガンマ関数の形状パラメーター // // // // 戻り値: // // 不完全なガンマ関数 Integral [0、x] t ^(nu-1)exp(-t)dt // ///////////////////////////////////////////////////////////////////////////////// function TForm1.Incomplete_Gamma_Function(x, nu: double): double; begin result := xIncomplete_Gamma_Function(x, nu); end; //////////////////////////////////////////////////////////////////////////////// // function xIncomplete_Gamma_Function(x、nu: extended): extended; // // // // 不完全なガンマ関数は0からxまでの積分として定義されます // // 被積分関数t ^(nu-1)exp(-t)dtのパラメータnuは時々 // // 形状パラメータと呼ばれます。 // // // // 引数: // // x : extended 上記の被積分関数を使用した積分の上限 // // nu : extended 不完全なガンマ関数の形状パラメーター // // // // 戻り値: // // 不完全なガンマ関数 Integral [0、x] t ^(nu-1)exp(-t)dt。 // //////////////////////////////////////////////////////////////////////////////// function TForm1.xIncomplete_Gamma_Function(x, nu: extended): extended; begin if x = 0.0 then begin result := 0.0; exit; end; if nu <= Gamma_Function_Max_Arg then result := xEntire_Incomplete_Gamma_Function(x, nu) * xGamma_Function(nu) else result := exp(ln(xEntire_Incomplete_Gamma_Function(x,nu)) + xLn_Gamma_Function(nu)); end; ////////////////////////////////////////////////////////////////////// // // // DBL_EPSILONの設定 // // // // 1に加算して1以上になる一番小さい値を計算します // ////////////////////////////////////////////////////////////////////// procedure TForm1.FormCreate(Sender: TObject); var i : integer; y, z : double; begin i := 0; z := 1; DBL_EPSILON := 1; repeat DBL_EPSILON := DBL_EPSILON / 2; y := z + DBL_EPSILON; inc(i); until (y = 1) or (i > 70); DBL_EPSILON := DBL_EPSILON * 2; end; //============================== // 入力値計算とグラフ作図 //============================== procedure TForm1.BitBtn1Click(Sender: TObject); var ch : integer; x, nu: double; GI, G : double; MIN, MAX: double; dx : double; i : integer; begin val(LabeledEdit1.Text, x, Ch); if ch <> 0 then begin MessageDlg('xの値に間違いがあります。', mtInformation, [mbOk], 0, mbOk); exit; end; if x < 0 then begin MessageDlg('負数のxは計算出来ません', mtInformation, [mbOk], 0, mbOk); exit; end; val(LabeledEdit2.Text, nu, Ch); if ch <> 0 then begin MessageDlg('aの値に間違いがあります。', mtInformation, [mbOk], 0, mbOk); exit; end; if nu <= 0 then begin MessageDlg('aはゼロより大きくして下さい計算出来ません。', mtInformation, [mbOk], 0, mbOk); exit; end; GI := Incomplete_Gamma_Function(x, nu); G := Gamma_Function(nu); Memo1.Clear; Memo1.Lines.Add('γ(' + floatTostr(nu) + ',' + floatTostr(x) + ') = '); Memo1.Lines.Add(' ' + floatTostr(GI)); Memo1.Lines.Add('Γ(' + floatTostr(nu) + ',' + floatTostr(x) + ') = '); Memo1.Lines.Add(' ' + floatTostr(G - GI)); Series1.Clear; Series2.Clear; Series3.Clear; Series4.Clear; Series3.AddXY(x, GI); Series4.AddXY(x, G - GI); MIN := x / 20; MAX := MIN + x + 5; dx := (MAX - MIN) / 100; for i := 0 to 100 do begin x := dx * i + min; GI := Incomplete_Gamma_Function(x, nu); Series1.AddXY(x, GI); Series2.AddXY(x, G - GI); end; end; end.
次のプログラムは、C言語辞典にある不完全ガンマ関数を修正したものです。
C言語辞典では、第1種不完全ガンマ関数の使用例となっていますので、第1種不完全ガンマ関数部分を取り出し、
第2種はガンマ関数から第1種を差し引いて計算する方法と、 x の値が 1 + a より大きい場合は、 第2種不完全ガンマ関数を直接計算する方法を選択出来るようにしています。
unit Main; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, VclTee.TeeGDIPlus, VCLTee.Series, VCLTee.TeEngine, Vcl.ExtCtrls, VCLTee.TeeProcs, VCLTee.Chart, Vcl.Buttons, system.UITypes; type TForm1 = class(TForm) Memo1: TMemo; LabeledEdit1: TLabeledEdit; BitBtn1: TBitBtn; LabeledEdit2: TLabeledEdit; Chart1: TChart; Series1: TLineSeries; Series2: TLineSeries; Series3: TPointSeries; Series4: TPointSeries; procedure BitBtn1Click(Sender: TObject); private { Private 宣言 } public { Public 宣言 } end; var Form1: TForm1; implementation {$R *.dfm} const //ベルヌーイ数B(2)、B(4)、B(6)、...、B(16) B : array[1..8] of double = ( 1.0 / 6, // B(2) -1.0 / 30, 1.0 / 42, -1.0 / 30, 5.0 / 66, -691.0 / 2730, 7.0 / 6, -3617.0 / 510); // B(16) m: integer = sizeof(B) div sizeof(double); function Gamma(x: double): double; // ガンマ関数 var i : integer; sum : double; xx, v : double; xj : double; lng : double; ln_sqrt_2pi: double; begin ln_sqrt_2pi := ln(sqrt(2 * pi)); // = ln(2 * pi) / 2; sum := 0; v := 1; while x < m do begin v := v * x; x := x + 1; end; xx := x * x; xj := x; lng := ln_sqrt_2pi - x + (x - 0.5) * ln(x) - ln(v); for i := 1 to m do begin sum := sum + B[i] / (i * 2 * (2 * i - 1)) / xj; xj := xj * xx; end; result := exp(sum + lng); end; // γ(a, x) function incomplete_gamma(a, x: double): double; // 第1種不完全ガンマ関数 var k : integer; term, previous : double; begin result := 0; if x = 0 then exit; term := exp(a * ln(x) - x) / a; result := term; for k := 1 to 1000 do begin term := term * x / (a + k); previous := result; result := result + term; if result = previous then begin exit; end; end; Form1.Memo1.Lines.add ('p_gamma(): 収束しません'); end; // Γ(a, x) function incomplete_gamma2(a, x: double): double; // 第2種不完全ガンマ関数 var k : integer; w, temp, previous : double; la, lb : double; begin if x < 1 + a then begin result := Gamma(a) - incomplete_gamma1(a, x); exit; end; la := 1; // Laguerreの多項式 lb := 1 + x - a; w := exp(a * ln(x) - x); result := w / lb; for k := 2 to 1000 do begin temp := ((k - 1 - a) * (lb - la) + (k + x) * lb) / k; la := lb; lb := temp; w := w * (k - 1 - a) / k; temp := w / (la * lb); previous := result; result := result + temp; if result = previous then exit; end; Form1.Memo1.Lines.add ('q_gamma: 収束しません'); end; procedure TForm1.BitBtn1Click(Sender: TObject); var ch : integer; x, a: double; GI, G : double; MIN, MAX: double; dx : double; i : integer; begin val(LabeledEdit1.Text, x, Ch); if ch <> 0 then begin MessageDlg('xの値に間違いがあります。', mtInformation, [mbOk], 0, mbOk); exit; end; if x < 0 then begin MessageDlg('負数のxは計算出来ません', mtInformation, [mbOk], 0, mbOk); exit; end; val(LabeledEdit2.Text, a, Ch); if ch <> 0 then begin MessageDlg('aの値に間違いがあります。', mtInformation, [mbOk], 0, mbOk); exit; end; if a <= 0 then begin MessageDlg('aはゼロより大きくして下さい計算出来ません。', mtInformation, [mbOk], 0, mbOk); exit; end; GI := incomplete_gamma(a, x); if checkbox1.Checked = false then G := gamma(a) - GI else G := incomplete_gamma2(a, x); Memo1.Clear; Memo1.Lines.Add('γ(' + floatTostr(a) + ',' + floatTostr(x) + ') = '); Memo1.Lines.Add(' ' + floatTostr(GI)); Memo1.Lines.Add('Γ(' + floatTostr(a) + ',' + floatTostr(x) + ') = '); Memo1.Lines.Add(' ' + floatTostr(G)); Series1.Clear; Series2.Clear; Series3.Clear; Series4.Clear; Series3.AddXY(x, GI); Series4.AddXY(x, G); MIN := x / 20; MAX := MIN + x + 5; dx := (MAX - MIN) / 100; for i := 0 to 100 do begin x := dx * i + MIN; GI := incomplete_gamma(a, x); Series1.AddXY(x, GI); if checkbox1.Checked = false then G := gamma(a) - GI else G := incomplete_gamma2(a, x); Series2.AddXY(x, G); end; end; end.