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 }