|
STK++ 1.0
|
00001 /*--------------------------------------------------------------------*/ 00002 /* Copyright (C) 2004-2007 Serge Iovleff 00003 00004 This program is free software; you can redistribute it and/or modify 00005 it under the terms of the GNU Lesser General Public License as 00006 published by the Free Software Foundation; either version 2 of the 00007 License, or (at your option) any later version. 00008 00009 This program is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 GNU Lesser General Public License for more details. 00013 00014 You should have received a copy of the GNU Lesser General Public 00015 License along with this program; if not, write to the 00016 Free Software Foundation, Inc., 00017 59 Temple Place, 00018 Suite 330, 00019 Boston, MA 02111-1307 00020 USA 00021 00022 Contact : Serge.Iovleff@stkpp.org 00023 */ 00024 00025 /* 00026 * Project: Analysis 00027 * Purpose: Implementation of functions around the gamma function ratio 00028 * Author: Serge Iovleff, serge.iovleff@stkpp.org 00029 **/ 00030 00036 #include "../include/STK_Funct_gammaRatio.h" 00037 00038 #include "../include/STK_ISerie.h" 00039 #include "../include/STK_Algo.h" 00040 #include "../include/STK_Funct_gamma.h" 00041 #include "../include/STK_Funct_util.h" 00042 #include "../include/STK_Funct_raw.h" 00043 00044 namespace STK 00045 { 00046 00047 namespace Funct 00048 { 00064 static Real apois(Real const& a, Real const& b) 00065 { 00066 // special value 00067 if (b==0.) return( 0. ); 00068 // stirling approximation and deviance 00069 return( sqrt(a)*exp(-gammaLnStirlingError(a)-dev0(a, b)) 00070 * (Const::_1_SQRT2PI_) 00071 ); 00072 } 00073 00080 class Seriedl : public ISerie<Seriedl> 00081 { 00082 private: 00083 const Real& a_; 00084 const Real& x_; 00085 mutable Real n_; 00086 mutable Real xn_factn_; 00087 mutable Real aplusn_; 00088 00089 public: 00090 inline Seriedl(Real const& a, Real const& x) 00091 : a_(a), x_(x) 00092 , n_(1), xn_factn_(x), aplusn_(a+1.) 00093 { ;} 00094 00095 inline Real first() const 00096 { return x_/aplusn_;} 00097 00098 inline Real next() const 00099 { 00100 // n and x^n/ n! 00101 xn_factn_ *= (x_/(++n_)); 00102 // x^n/((a+n)n!) 00103 return xn_factn_/(++aplusn_); 00104 } 00105 }; 00106 00124 static Real gammaRatio_dl( Real const& a 00125 , Real const& x 00126 , const bool &lower_tail 00127 ) 00128 { 00129 Seriedl f(a, x); 00130 if (lower_tail) 00131 return (1.- a*sumAlternateSerie(f))*poisson_pdf_raw(a, x)*exp(x); 00132 else 00133 return apois(a, x) * sumAlternateSerie(f) * exp(x) 00134 - expm1(a*log(x)-gammaLn(a+1.)); 00135 } 00136 00154 static Real gammaRatio_cf( Real const& a 00155 , Real const& x 00156 , const bool lower_tail 00157 ) 00158 { 00159 // initialize b_n 00160 Real bn = x + 1. -a; // b_1 00161 // initialize numerator 00162 Real Nold = 0, Ncur = 1.; // = a_1 = 1 00163 // initialize denominator 00164 Real Dold = 1, Dcur = bn; // = b_1 00165 00166 // normalize if necessary by 2^32 00167 if (abs(Dcur) > 4294967296.0) 00168 { 00169 Ncur /= Dcur; 00170 Dold /= Dcur; 00171 Dcur = 1.; 00172 } 00173 00174 // auxiliary variables 00175 Real aminusn = a; 00176 // result 00177 Real cf = Ncur/Dcur; 00178 // iterations 00179 for (Integer n=1; ; n++) 00180 { 00181 // compute a_n 00182 Real an = n*(--aminusn); 00183 // check trivial case 00184 // (note a_n =0 => b_n != 0 for x>0 and thus cf!=N_n/D_n exists) 00185 if (an == 0.) break; 00186 // update b_n 00187 bn += 2.; 00188 // compute numerator and denominator 00189 Real anew = bn * Ncur + an * Nold; 00190 Nold = Ncur; 00191 Ncur = anew; 00192 anew = bn * Dcur + an * Dold; 00193 Dold = Dcur; 00194 Dcur = anew; 00195 // normalize if necessary with 2^32 00196 if (abs(Dcur) > 4294967296.0) 00197 { 00198 Ncur /= Dcur; 00199 Nold /= Dcur; 00200 Dold /= Dcur; 00201 Dcur = 1.; 00202 } 00203 // normalize if necessary with 2^32 00204 if (abs(Ncur) > 4294967296.0) 00205 { 00206 Ncur /= 4294967296.0; 00207 Nold /= 4294967296.0; 00208 Dold /= 4294967296.0; 00209 Dcur /= 4294967296.0; 00210 } 00211 // test D_n not too small 00212 if (abs(Dcur) != 0) 00213 { 00214 Real cfold = cf; 00215 cf = Ncur/Dcur; 00216 // check cv: relative; absolute for small cf 00217 if (abs(cf - cfold) < abs(cf)*Arithmetic<Real>::epsilon()) 00218 break; 00219 } 00220 } 00221 // cv 00222 return (lower_tail) ? (1 - apois(a, x)*cf) 00223 : (apois(a, x)*cf); 00224 } 00225 00240 static Real gammaRatio_sr( Real const& a 00241 , Real const& x 00242 , const bool &lower_tail 00243 ) 00244 { 00245 Real y = a+1., term = x / (a+1.), sum = term; 00246 /* sum = 1+\sum_{n=1}^\infty x^n / (a+1)*(a+2)*...*(a+n)) 00247 */ 00248 do 00249 { 00250 y++; 00251 sum += (term *= x / y); 00252 } 00253 while (term > sum * Arithmetic<Real>::epsilon()); 00254 sum+=1; 00255 00256 if (lower_tail) 00257 return poisson_pdf_raw(a, x)*sum; 00258 else 00259 return 1.-poisson_pdf_raw(a, x)*sum; 00260 } 00261 00282 static Real gammaRatio_ae( Real const& a 00283 , Real const& x 00284 , const bool &lower_tail 00285 ) 00286 { 00287 Real term = 1, sum = 0, b=a-1; 00288 // sum = 1+\sum_{n=1}^\infty (a-1)*...*(a - n) / x^n 00289 while ((b > 1.) && (term > sum * Arithmetic<Real>::epsilon())) 00290 { 00291 sum += (term *= b / x); 00292 b--; 00293 } 00294 sum+=1.; 00295 // achieved cv 00296 if (b>1.) 00297 return lower_tail ? (1.-poisson_pdf_raw(a-1, x)*sum) 00298 : ( poisson_pdf_raw(a-1, x)*sum); 00299 // case (b>x) and (b <=1) 00300 if (b>x) 00301 return lower_tail ? (gammaRatio_sr(b, x, lower_tail)-poisson_pdf_raw(a-1, x)*sum) 00302 : (gammaRatio_sr(b, x, lower_tail)+poisson_pdf_raw(a-1, x)*sum); 00303 // case (b <x) and (b <=1) 00304 return lower_tail ? gammaRatio_cf(b, x, lower_tail)-poisson_pdf_raw(a-1, x)*sum 00305 : gammaRatio_cf(b, x, lower_tail)+poisson_pdf_raw(a-1, x)*sum; 00306 } 00307 00321 static inline Real poisson_ae( Real const& a1 00322 , Real const& apd 00323 , const bool &lower_tail = true 00324 ) 00325 { 00326 // odd coefficients 00327 static const Real coefs_i[8] = 00328 { 00329 0., /* placeholder used for 1-indexing */ 00330 2/3., 00331 -4/135., 00332 8/2835., 00333 16/8505., 00334 -8992/12629925., 00335 -334144/492567075., 00336 698752/1477701225. 00337 }; 00338 // stirling coefficients 00339 static const Real coefs_s[8] = 00340 { 00341 0., /* placeholder used for 1-indexing */ 00342 1/12., 00343 1/288., 00344 -139/51840., 00345 -571/2488320., 00346 163879/209018880., 00347 5246819/75246796800., 00348 -534703531/902961561600. 00349 }; 00350 // compute D = -x(log(1+d/x) - d/x) = xlog(x/(x+d))+(x+d)-x 00351 Real D = dev0(a1, apd); 00352 // compute sqrt(2D) 00353 Real sqrt2D = sqrt (2. * D); 00354 // compute D/x 00355 Real D_x = D/a1; 00356 00357 // treat negative difference 00358 if (apd - a1 < 0) sqrt2D = -sqrt2D; 00359 00360 // variables for the numerator 00361 Real num = 0; 00362 Real sum1, num1_term, sum2, num2_term; 00363 num1_term = sum1 = sqrt(a1); 00364 num2_term = sum2 = sqrt2D; 00365 00366 // variables for the denominator 00367 Real den = a1; 00368 Real den_term = 1; 00369 // computation 00370 for (Integer i = 1; i < 8; i++) 00371 { 00372 // compute the numerator 00373 num += num1_term * coefs_i[i]; 00374 num += num2_term * coefs_s[i]; 00375 // first sum 00376 sum1 *= (D_x / i) ; // sqrt(x) * D^n/(n! x^n) 00377 num1_term = num1_term/a1 + sum1; 00378 // second sum 00379 sum2 *= (D_x / (i + 0.5)); // sqrt(2D) * D^n/((1+0.5)*...(n+0.5)*x^n) 00380 num2_term = num2_term/a1 + sum2; 00381 // compute the denominator 00382 den += den_term * coefs_s[i]; 00383 den_term /= a1; 00384 } 00385 // result (P or Q ?) 00386 return lower_tail ? normal_cdf_raw( sqrt2D) 00387 - (num / den) * normal_pdf_raw(sqrt2D) 00388 : normal_cdf_raw(-sqrt2D) 00389 + (num / den) * normal_pdf_raw(sqrt2D); 00390 } 00391 00399 Real gammaRatioQ(Real const& a, Real const& x) 00400 { 00401 return gammaRatio(a, x, false); 00402 } 00403 00411 Real gammaRatioP(Real const& a, Real const& x) 00412 { 00413 return gammaRatio(a, x, true); 00414 } 00415 00424 Real gammaRatio(Real const& a, Real const& x, const bool &lower_tail) 00425 { 00426 // Check if a and x are available 00427 if (Arithmetic<Real>::isNA(a)||Arithmetic<Real>::isNA(x)) return a; 00428 // Negative parameter not allowed 00429 if (a<=0) 00430 throw domain_error("Funct::gammaRatio(a,x,lower_tail) " 00431 "Negative parameter a"); 00432 // trivial case 00433 if (x<=0) return lower_tail ? 0 : 1.; 00434 if (Arithmetic<Real>::isInfinite(x)) return lower_tail ? 1. : 0.; 00435 00436 // small values of x 00437 if (x <= 1.) return gammaRatio_dl(a, x, lower_tail); 00438 00439 // large a compared to x 00440 if (0.75*a> x) return gammaRatio_sr(a, x, lower_tail); 00441 00442 // large x compared to a 00443 if (0.75*x > a) 00444 { // if a>1 we can use the a.e. before computing 00445 if (a>1) return gammaRatio_ae(a, x, lower_tail); 00446 else return gammaRatio_cf(a, x, lower_tail); 00447 } 00448 00449 // (a<100) and (x ~ a) 00450 if (a<100.) 00451 { 00452 // a greater than x 00453 if (a>x) return gammaRatio_sr(a, x, lower_tail); 00454 else 00455 { // if a>1 we can use the a.e. before computing 00456 if (a>1) return gammaRatio_ae(a, x, lower_tail); 00457 else return gammaRatio_cf(a, x, lower_tail);} 00458 } 00459 // (x ~ a) and (a>=100) 00460 return poisson_ae(a-1, x, lower_tail); 00461 } 00462 } // namespace Funct 00463 00464 } // namespace STK