All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.enterprisemath.math.probability.NormalDistribution Maven / Gradle / Ivy

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();
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy