Isis 3 Programmer Reference
GaussianDistribution.cpp
1 
6 /* SPDX-License-Identifier: CC0-1.0 */
7 #include "GaussianDistribution.h"
8 #include "Message.h"
9 #include <string>
10 #include <iostream>
11 
12 using namespace std;
13 namespace Isis {
20  GaussianDistribution::GaussianDistribution(const double mean, const double standardDeviation) {
21  p_mean = mean;
22  p_stdev = standardDeviation;
23  }
24 
25 
34  double GaussianDistribution::Probability(const double value) {
35  // P(x) = 1/(sqrt(2*pi)*sigma)*e^(-1/2*((x-mu)/sigma)^2)
36  return std::exp(-0.5 * std::pow((value - p_mean) / p_stdev, 2)) / (std::sqrt(2 * PI) * p_stdev);
37  }
38 
39 
48  double GaussianDistribution::CumulativeDistribution(const double value) {
49  // returns for the two extremes
50  if(value == DBL_MIN) {
51  return 0.0;
52  }
53  else if(value == DBL_MAX) {
54  return 1.0;
55  }
56 
57  // normalize the value and calculate the area under the
58  // pdf's curve.
59  double x = (value - p_mean) / p_stdev;
60  double sum = 0.0,
61  pre = -1.0;
62  double fact = 1.0; // fact = n!
63  // use taylor series to compute the area to machine precesion
64  // i.e. if an iteration has no impact on the sum, none of the following
65  // ones will since they are monotonically decreasing
66  for(int n = 0; pre != sum; n++) {
67  pre = sum;
68  // the nth term is x^(2n+1)/( (2n+1)*n!*(-2)^n )
69  sum += std::pow(x, 2 * n + 1) / (fact * (2 * n + 1) * std::pow(-2.0, n));
70  fact *= n + 1;
71  }
72 
73  // return the percentage (100% based)
74  return 50.0 + 100.0 / std::sqrt(2.0 * PI) * sum;
75  }
76 
90  double GaussianDistribution::InverseCumulativeDistribution(const double percent) {
91  // the cutoff values used in the ICDF algorithm
92  static double lowCutoff = 2.425;
93  static double highCutoff = 97.575;
94 
95  if((percent < 0.0) || (percent > 100.0)) {
96  string m = "Argument percent outside of the range 0 to 100 in";
97  m += " [GaussianDistribution::InverseCumulativeDistribution]";
98  throw IException(IException::Programmer, m, _FILEINFO_);
99  }
100 
101  // for information on the following algorithm, go to the website
102  // specified above
103 
104  double x;
105 
106  if(percent == 0.0) {
107  return DBL_MIN;
108  }
109  else if(percent == 100.0) {
110  return DBL_MAX;
111  }
112 
113  if(percent < lowCutoff) {
114  double q = std::sqrt(-2.0 * std::log(percent / 100.0));
115  x = C(q) / D(q);
116  }
117  else if(percent < highCutoff) {
118  double q = percent / 100.0 - 0.5,
119  r = q * q;
120  x = A(r) * q / B(r);
121  }
122  else {
123  double q = std::sqrt(-2.0 * std::log(1.0 - percent / 100.0));
124  x = -1.0 * C(q) / D(q);
125  }
126 
127  // refine the calculated value using one iteration of Halley's method
128  // for full machine precision
129  double e = CumulativeDistribution(p_stdev * x + p_mean) - percent; // the error amount
130  double u = e * std::sqrt(2.0 * PI) * std::exp(-0.5 * x * x);
131  x = x - u / (1 + 0.5 * x * u);
132 
133  return p_stdev * x + p_mean;
134  }
135 
136  // The following methods are used to compute the ICDF
137  // see the InverseCumulativeDistribution method for
138  // more information
139 
140  double GaussianDistribution::A(const double x) {
141  static const double a[6] = { -39.69683028665376,
142  220.9460984245205,
143  -275.9285104469687,
144  138.3577518672690,
145  -30.66479806614716,
146  2.506628277459239
147  };
148 
149  return((((a[0] * x + a[1]) * x + a[2]) * x + a[3]) * x + a[4]) * x + a[5];
150  }
151 
152  double GaussianDistribution::B(const double x) {
153  static const double b[6] = { -54.47609879822406,
154  161.5858368580409,
155  -155.6989798598866,
156  66.80131188771972,
157  -13.28068155288572,
158  1.0
159  };
160 
161  return((((b[0] * x + b[1]) * x + b[2]) * x + b[3]) * x + b[4]) * x + b[5];
162  }
163 
164  double GaussianDistribution::C(const double x) {
165  static const double c[6] = { -0.007784894002430293,
166  -0.3223964580411365,
167  -2.400758277161838,
168  -2.549732539343734,
169  4.374664141464968,
170  2.938163982698783
171  };
172 
173  return((((c[0] * x + c[1]) * x + c[2]) * x + c[3]) * x + c[4]) * x + c[5];
174  }
175 
176  double GaussianDistribution::D(const double x) {
177  static const double d[5] = { 0.007784695709041462,
178  0.3224671290700398,
179  2.445134137142996,
180  3.754408661907416,
181  1.0
182  };
183 
184  return(((d[0] * x + d[1]) * x + d[2]) * x + d[3]) * x + d[4];
185  }
186 }
Isis::PI
const double PI
The mathematical constant PI.
Definition: Constants.h:40
Isis::IException
Isis exception class.
Definition: IException.h:91
std
Namespace for the standard library.
Isis
This is free and unencumbered software released into the public domain.
Definition: Apollo.h:16