com.enterprisemath.math.probability.NormalDistribution Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of em-math Show documentation
Show all versions of em-math Show documentation
Advanced mathematical algorithms.
The newest version!
package com.enterprisemath.math.probability;
import java.security.SecureRandom;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import com.enterprisemath.math.algebra.Interval;
import com.enterprisemath.utils.ValidationUtils;
/**
* Normal 1 dimensional probability distribution.
*
* @author radek.hecl
*
*/
public class NormalDistribution implements ProbabilityDistribution {
/**
* Builder object.
*/
public static class Builder {
/**
* Mi value of the distribution.
*/
private double mi;
/**
* Sigma value of the distribution.
*/
private double sigma;
/**
* Sets the value mi.
*
* @param mi mi value
* @return this instance
*/
public Builder setMi(double mi) {
this.mi = mi;
return this;
}
/**
* Sets the value sigma.
*
* @param sigma sigma value
* @return this instance
*/
public Builder setSigma(double sigma) {
this.sigma = sigma;
return this;
}
/**
* Creates the result object.
*
* @return created object
*/
public NormalDistribution build() {
return new NormalDistribution(this);
}
}
/**
* Object for generating random values.
*/
private static final SecureRandom random = new SecureRandom();
/**
* Mi parameter of the distribution.
*/
private double mi;
/**
* Sigma parameter of the distribution.
*/
private double sigma;
/**
* This is cached constant calculated to speed up. Value is 1 / (sigma * sqrt(2pi)).
*/
private double fraction;
/**
* This is cached constant calculated to speed up. Value is ln (1 / (sigma * sqrt(2pi))).
*/
private double lnFraction;
/**
* Creates new instance.
*
* @param builder builder object
*/
public NormalDistribution(Builder builder) {
mi = builder.mi;
sigma = builder.sigma;
guardInvariants();
fraction = 1 / (sigma * Math.sqrt(2 * Math.PI));
lnFraction = -Math.log(sigma) - Math.log(Math.sqrt(2 * Math.PI));
ValidationUtils.guardBoundedDouble(fraction, "1 / (sigma * Math.sqrt(2 * Math.PI)) went out of range: sigma = " + sigma);
ValidationUtils.guardBoundedDouble(fraction, "-Math.log(sigma) - Math.log(Math.sqrt(2 * Math.PI)) went out of range: sigma = " + sigma);
}
/**
* Guards this object to be consistent. Throws exception if this is not the case.
*/
private void guardInvariants() {
ValidationUtils.guardPositiveDouble(sigma, "sigma must be positive");
ValidationUtils.guardBoundedDouble(mi, "mi must be bounded");
ValidationUtils.guardBoundedDouble(sigma, "sigma must be bounded");
}
@Override
public double getValue(Double x) {
return fraction * Math.exp(-0.5 * sqr((x - mi) / sigma));
}
@Override
public double getLnValue(Double x) {
return lnFraction - (0.5 * sqr((x - mi) / sigma));
}
@Override
public Double generateRandom() {
return random.nextGaussian() * sigma + mi;
}
/**
* Returns the value of the parameter mi.
*
* @return value of the parameter mi
*/
public double getMi() {
return mi;
}
/**
* Returns the value of the parameter sigma.
*
* @return value of the parameter sigma
*/
public double getSigma() {
return sigma;
}
/**
* Returns interval with at least specified probability that random elements
* generated by this distribution will be inside.
*
* @param min minimum probability, must be in interval [0, 1)
* @return interval with at least specified probability that random elements generated by this distribution will be inside
*/
public Interval getProbableInterval(double min) {
ValidationUtils.guardNotNegativeDouble(min, "min cannot be negative");
ValidationUtils.guardGreaterDouble(1, min, "min must be less than 1");
if (min < 0.6) {
return Interval.create(mi - sigma, mi + sigma);
}
else if (min < 0.9) {
return Interval.create(mi - 2 * sigma, mi + 2 * sigma);
}
else if (min < 0.95) {
return Interval.create(mi - 3 * sigma, mi + 3 * sigma);
}
else if (min < 0.98) {
return Interval.create(mi - 4 * sigma, mi + 4 * sigma);
}
else {
return Interval.create(mi - 10 * sigma, mi + 10 * sigma);
}
}
/**
* Returns the x * x.
*
* @param x value x
* @return x * x
*/
private double sqr(double x) {
return x * x;
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
@Override
public boolean equals(Object obj) {
return EqualsBuilder.reflectionEquals(this, obj);
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
/**
* Creates normal distribution.
*
* @param mi mi value
* @param sigma sigma value
* @return created distribution
*/
public static NormalDistribution create(double mi, double sigma) {
return new NormalDistribution.Builder().
setMi(mi).
setSigma(sigma).
build();
}
}