contrib/rpl/rrel/rrel_misc.cxx
Go to the documentation of this file.
00001 #include "rrel_misc.h"
00002 #include <vcl_cmath.h>
00003 #include <vnl/vnl_math.h>
00004 
00005 // Chebychev approximation to erfc --- the complement of the error
00006 // function.  Taken from Numerical Recipes in C.
00007 
00008 double
00009 rrel_misc_erfcc( double x )
00010 {
00011     double t,z,ans;
00012 
00013     z=vcl_fabs(x);
00014     t=1.0/(1.0+0.5*z);
00015     ans=t*vcl_exp(-z*z-1.26551223+t*(1.00002368+t*(0.37409196+t*(0.09678418+
00016            t*(-0.18628806+t*(0.27886807+t*(-1.13520398+t*(1.48851587+
00017                  t*(-0.82215223+t*0.17087277)))))))));
00018     return  x >= 0.0 ? ans : 2.0-ans;
00019 }
00020 
00021 //
00022 // Robert W. Cox from the Biophysics Research Institute at the Medical
00023 // College of Wisconsin provided the following routine for computing
00024 // the inverse CDF for a standardized Gaussian.  This function is
00025 // based off of a rational polynomial approximation to the inverse
00026 // Gaussian CDF which can be found in
00027 //
00028 // M. Abramowitz and I.A. Stegun. Handbook of Mathematical Functions
00029 // with Formulas, Graphs, and Mathematical Tables.  John Wiley & Sons.
00030 // New York. Equation 26.2.23. pg. 933. 1972.
00031 //
00032 // Since the initial approximation only provides an estimate within
00033 // 4.5 E-4 of the true value, 3 Newton-Raphson iterations are used
00034 // to refine the approximation.
00035 //
00036 // Let
00037 // $Q(x) = (1/\sqrt{2 pi}) \Int_{x}^{\infty} e^{-t^2/2} dt
00038 //       = 0.5 * erfc(x/\sqrt{2})$
00039 //
00040 // Given p, this function computes x such that $Q(x) = p$, for $0 < p < 1$
00041 //
00042 // Note that the Gaussian CDF is defined as
00043 // $P(x) = (1/\sqrt{2 pi}) \Int_{-\infty}^{x} e^{-t^2/2} dt
00044 //       = 1 - Q(x)$
00045 //
00046 // This function has been modified to compute the inverse of $P(x)$ instead
00047 // of $Q(x)$.
00048 //
00049 double
00050 rrel_misc_gaussian_cdf_inv( double p )
00051 {
00052    double dp , dx , dt , ddq , dq ;
00053    int    newt ;
00054 
00055    dp = (p <= 0.5) ? p : 1.0-p;   // make between 0 and 0.5
00056 
00057    // if original value is invalid, return +infinity or -infinity
00058    // changed from original code to reflect the fact that the
00059    // the inverse of P(x) not Q(x) is desired.
00060    //
00061    // Original line: used for inverse of Q(x)
00062    // if ( dp <= 0.0 ){ dx = 13.0 ;  return (p <= 0.5) ? (dx) : (-dx); }
00063 
00064    // replaced with this if construct for the inverse of P(x)
00065    if (p <= 0.0)
00066      return -vnl_huge_val(double(0));
00067    else if (p >= 1.0)
00068      return vnl_huge_val(double(0));
00069 
00070 
00071    //  Step 1:  use 26.2.23 from Abramowitz and Stegun
00072 
00073    dt = vcl_sqrt( -2.0 * vcl_log(dp) ) ;
00074    dx = dt
00075      - ((.010328e+0*dt + .802853e+0)*dt + 2.515517e+0)
00076      /(((.001308e+0*dt + .189269e+0)*dt + 1.432788e+0)*dt + 1.e+0) ;
00077 
00078    //  Step 2:  do 3 Newton steps to improve this
00079 
00080    for ( newt=0 ; newt < 3 ; newt++ ){
00081      dq  = 0.5e+0 * rrel_misc_erfcc( dx / 1.414213562373095e+0 ) - dp ;
00082      ddq = vcl_exp( -0.5e+0 * dx * dx ) / 2.506628274631000e+0 ;
00083      dx  = dx + dq / ddq ;
00084    }
00085 
00086    // original line when computing the inverse of Q(x)
00087    // return (p <= 0.5) ? dx : (-dx)   // return with correct sign
00088    //
00089    // Note that P(-x) = Q(x), so whatever x was calculated for Q(x) = p,
00090    // we simply need to return the negative of x to get P(xp) = p.
00091    //
00092    return (p <= 0.5) ? (-dx) : dx; // return with correct sign
00093 }