USGS

Isis 3.0 Object Programmers' Reference

Home

GaussianDistribution.cpp
Go to the documentation of this file.
1 
23 #include "GaussianDistribution.h"
24 #include "Message.h"
25 #include <string>
26 #include <iostream>
27 
28 using namespace std;
29 namespace Isis {
36  GaussianDistribution::GaussianDistribution(const double mean, const double standardDeviation) {
37  p_mean = mean;
38  p_stdev = standardDeviation;
39  }
40 
49  double GaussianDistribution::Probability(const double value) {
50  // P(x) = 1/(sqrt(2*pi)*sigma)*e^(-1/2*((x-mu)/sigma)^2)
51  return std::exp(-0.5 * std::pow((value - p_mean) / p_stdev, 2)) / (std::sqrt(2 * PI) * p_stdev);
52  }
53 
62  double GaussianDistribution::CumulativeDistribution(const double value) {
63  // returns for the two extremes
64  if(value == DBL_MIN) {
65  return 0.0;
66  }
67  else if(value == DBL_MAX) {
68  return 1.0;
69  }
70 
71  // normalize the value and calculate the area under the
72  // pdf's curve.
73  double x = (value - p_mean) / p_stdev;
74  double sum = 0.0,
75  pre = -1.0;
76  double fact = 1.0; // fact = n!
77  // use taylor series to compute the area to machine precesion
78  // i.e. if an iteration has no impact on the sum, none of the following
79  // ones will since they are monotonically decreasing
80  for(int n = 0; pre != sum; n++) {
81  pre = sum;
82  // the nth term is x^(2n+1)/( (2n+1)*n!*(-2)^n )
83  sum += std::pow(x, 2 * n + 1) / (fact * (2 * n + 1) * std::pow(-2.0, n));
84  fact *= n + 1;
85  }
86 
87  // return the percentage (100% based)
88  return 50.0 + 100.0 / std::sqrt(2.0 * PI) * sum;
89  }
90 
104  double GaussianDistribution::InverseCumulativeDistribution(const double percent) {
105  // the cutoff values used in the ICDF algorithm
106  static double lowCutoff = 2.425;
107  static double highCutoff = 97.575;
108 
109  if((percent < 0.0) || (percent > 100.0)) {
110  string m = "Argument percent outside of the range 0 to 100 in";
111  m += " [GaussianDistribution::InverseCumulativeDistribution]";
112  throw IException(IException::Programmer, m, _FILEINFO_);
113  }
114 
115  // for information on the following algorithm, go to the website
116  // specified above
117 
118  double x;
119 
120  if(percent == 0.0) {
121  return DBL_MIN;
122  }
123  else if(percent == 100.0) {
124  return DBL_MAX;
125  }
126 
127  if(percent < lowCutoff) {
128  double q = std::sqrt(-2.0 * std::log(percent / 100.0));
129  x = C(q) / D(q);
130  }
131  else if(percent < highCutoff) {
132  double q = percent / 100.0 - 0.5,
133  r = q * q;
134  x = A(r) * q / B(r);
135  }
136  else {
137  double q = std::sqrt(-2.0 * std::log(1.0 - percent / 100.0));
138  x = -1.0 * C(q) / D(q);
139  }
140 
141  // refine the calculated value using one iteration of Halley's method
142  // for full machine precision
143  double e = CumulativeDistribution(p_stdev * x + p_mean) - percent; // the error amount
144  double u = e * std::sqrt(2.0 * PI) * std::exp(-0.5 * x * x);
145  x = x - u / (1 + 0.5 * x * u);
146 
147  return p_stdev * x + p_mean;
148  }
149 
150  // The following methods are used to compute the ICDF
151  // see the InverseCumulativeDistribution method for
152  // more information
153 
154  double GaussianDistribution::A(const double x) {
155  static const double a[6] = { -39.69683028665376,
156  220.9460984245205,
157  -275.9285104469687,
158  138.3577518672690,
159  -30.66479806614716,
160  2.506628277459239
161  };
162 
163  return((((a[0] * x + a[1]) * x + a[2]) * x + a[3]) * x + a[4]) * x + a[5];
164  }
165 
166  double GaussianDistribution::B(const double x) {
167  static const double b[6] = { -54.47609879822406,
168  161.5858368580409,
169  -155.6989798598866,
170  66.80131188771972,
171  -13.28068155288572,
172  1.0
173  };
174 
175  return((((b[0] * x + b[1]) * x + b[2]) * x + b[3]) * x + b[4]) * x + b[5];
176  }
177 
178  double GaussianDistribution::C(const double x) {
179  static const double c[6] = { -0.007784894002430293,
180  -0.3223964580411365,
181  -2.400758277161838,
182  -2.549732539343734,
183  4.374664141464968,
184  2.938163982698783
185  };
186 
187  return((((c[0] * x + c[1]) * x + c[2]) * x + c[3]) * x + c[4]) * x + c[5];
188  }
189 
190  double GaussianDistribution::D(const double x) {
191  static const double d[5] = { 0.007784695709041462,
192  0.3224671290700398,
193  2.445134137142996,
194  3.754408661907416,
195  1.0
196  };
197 
198  return(((d[0] * x + d[1]) * x + d[2]) * x + d[3]) * x + d[4];
199  }
200 }