/*
* Apache License
* Version 2.0, January 2004
* http://www.apache.org/licenses/
*
* Copyright 2013 Aurelian Tutuianu
* Copyright 2014 Aurelian Tutuianu
* Copyright 2015 Aurelian Tutuianu
* Copyright 2016 Aurelian Tutuianu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package rapaio.core.distributions;
import static rapaio.sys.WS.formatFlex;
/**
* @author <a href="mailto:padreatiyahoo.com">Aurelian Tutuianu</a>
*/
public class Normal implements Distribution {
private static final long serialVersionUID = 3618971055326379083L;
private final double mu;
private final double sd;
private final double var;
@Override
public String name() {
return "Normal(mu=" + formatFlex(mu) + ", sd=" + formatFlex(sd) + ")";
}
@Override
public boolean discrete() {
return false;
}
public Normal() {
this(0, 1);
}
public Normal(double mu, double sd) {
this.mu = mu;
this.sd = sd;
this.var = sd * sd;
}
@Override
public double pdf(double x) {
return 1 / Math.sqrt(2 * Math.PI * var) * Math.exp(-Math.pow(x - mu, 2) / (2 * var));
}
@Override
public double cdf(double x) {
return cdf(x, mu, sd);
}
private double cdf(double x, double mu, double sd) {
if (Double.isNaN(x) || Double.isInfinite(x)) {
throw new IllegalArgumentException("X is not a real number");
}
return cdfMarsaglia((x - mu) / sd);
}
@Override
public double quantile(double p) {
if (p < 0 || p > 1) {
throw new IllegalArgumentException("Inverse of a probability requires a probablity in the range [0,1], not " + p);
}
if (p == 0) {
return Double.NEGATIVE_INFINITY;
}
if (p == 1) {
return Double.POSITIVE_INFINITY;
}
//http://home.online.no/~pjacklam/notes/invnorm/
double a[] = {
-3.969683028665376e+01, 2.209460984245205e+02,
-2.759285104469687e+02, 1.383577518672690e+02,
-3.066479806614716e+01, 2.506628277459239e+00
};
double b[] = {
-5.447609879822406e+01, 1.615858368580409e+02,
-1.556989798598866e+02, 6.680131188771972e+01, -1.328068155288572e+01
};
double c[] = {
-7.784894002430293e-03, -3.223964580411365e-01,
-2.400758277161838e+00, -2.549732539343734e+00,
4.374664141464968e+00, 2.938163982698783e+00
};
double d[] = {
7.784695709041462e-03, 3.224671290700398e-01,
2.445134137142996e+00, 3.754408661907416e+00
};
double p_low = 0.02425;
double p_high = 1 - p_low;
double result;
if (0 < p && p < p_low) {
double q = Math.sqrt(-2 * Math.log(p));
result = (((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5])
/ ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);
} else if (p_low <= p && p <= p_high) {
double q = p - 0.5;
double r = q * q;
result = (((((a[0] * r + a[1]) * r + a[2]) * r + a[3]) * r + a[4]) * r + a[5]) * q
/ (((((b[0] * r + b[1]) * r + b[2]) * r + b[3]) * r + b[4]) * r + 1);
} else//upper region
{
double q = Math.sqrt(-2 * Math.log(1 - p));
result = -(((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5])
/ ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);
}
//Refining step
double e = cdf(result, 0, 1) - p;
double u = e * Math.sqrt(2 * Math.PI) * Math.exp(result * result / 2);
result = result - u / (1 + result * u / 2);
return result * Math.sqrt(var) + mu;
}
private static double cdfMarsaglia(double x) {
/*
* Journal of Statistical Software (July 2004, Volume 11, Issue 5),
* George Marsaglia Algorithum to compute the cdf of the gaussian
* densities for some z score
*/
double s = x, t = 0, b = x, q = x * x, i = 1;
while (s != t) {
s = (t = s) + (b *= q / (i += 2));
}
if (s == Double.NEGATIVE_INFINITY) {
return 0;
}
if (s == Double.POSITIVE_INFINITY) {
return 1;
}
return 0.5 + s * Math.exp(-.5 * q - 0.91893853320467274178);
}
@Override
public double min() {
return Double.NEGATIVE_INFINITY;
}
@Override
public double max() {
return Double.POSITIVE_INFINITY;
}
@Override
public double mean() {
return mu;
}
@Override
public double mode() {
return mean();
}
@Override
public double var() {
return var;
}
@Override
public double skewness() {
return 0;
}
@Override
public double kurtosis() {
return 0;
}
@Override
public double entropy() {
return Math.log(2 * Math.PI * Math.E * var);
}
}