random_gaussian.hpp

Back to Gaussian-distributed random numbers

pastel/sys/random/

#ifndef PASTELSYS_RANDOM_GAUSSIAN_HPP
#define PASTELSYS_RANDOM_GAUSSIAN_HPP

#include "pastel/sys/random/random_gaussian.h"
#include "pastel/sys/random/random_uniform.h"
#include "pastel/sys/math/constants.h"

#include <boost/math/distributions/normal.hpp>

namespace Pastel
{

    template <typename Real>
    Real randomGaussian()
    {
        /*
       The probability density distribution of the
       gaussian distribution is

       p(x) = (1 / sqrt(d^2 2 pi)) e^(-(x - m)^2 / (2d^2))
       
       where d is the standard deviation
       and m is the mean.

       Without loss of generality, we take
       d = 1 and m = 0, and let the user transform the
       variable to the desired parameters.
       Then the pdf is given by:

       p(x) = (1 / sqrt(2 pi)) e^(-x^2 / 2)

       See the 'Box-Muller transform' for the
       generation algorithm.
       */

        Real u = 0;
        Real v = 0;
        Real s = 0;

        do
        {
            u = 2 * random<Real>() - 1;
            v = random<Real>();
            s = u * u + v * v;
        }
        while(s <= 0 || s >= 1);

        return u * std::sqrt(-2 * std::log(s) / s);
    }

    template <typename Real>
    Real randomGaussian(
        const NoDeduction<Real>& deviation)
    {
        PENSURE_OP(deviation, >=, 0);

        return Pastel::randomGaussian<Real>() * deviation;
    }

    template <typename Real, integer N>
    Vector<Real, N> randomGaussianVector()
    {
        PASTEL_STATIC_ASSERT(N != Dynamic);
        return Pastel::randomGaussianVector<Real, N>(N);
    }

    template <typename Real, integer N>
    Vector<Real, N> randomGaussianVector(integer dimension)
    {
        PENSURE_OP(dimension, >=, 0);
        Vector<Real, N> direction(ofDimension(dimension));

        for (integer i = 0;i < dimension;++i)
        {
            direction[i] = randomGaussian<Real>();          
        }

        return direction;
    }

    template <typename Real>
    Real gaussianPdf(
        const NoDeduction<Real>& x)
    {
        return inverse(std::sqrt(2 * constantPi<Real>())) * 
            std::exp(-square(x) / 2);
    }

    template <typename Real>
    Real gaussianPdf(
        const NoDeduction<Real>& x,
        const NoDeduction<Real>& deviation)
    {
        PENSURE_OP(deviation, >=, 0);

        return inverse(deviation * std::sqrt(2 * constantPi<Real>())) * 
            std::exp(-square(x / deviation) / 2);
    }

    template <typename Real>
    Real approximateGaussianCdf(
        const NoDeduction<Real>& x,
        const NoDeduction<Real>& deviation)
    {
        return Pastel::approximateGaussianCdf<Real>(
            x / deviation);
    }

    template <typename Real>
    Real approximateGaussianCdf(
        const NoDeduction<Real>& x)
    {
        // "Handbook and Mathematical Functions",
        // Abramowitz and Stegun, 1964.
        //
        // Formula 26.2.17.
        //
        // |Error| <= 7.5 * 10^-8

        Real b0 = 0.2316419;
        Real b1 = 0.319381530;
        Real b2 = -0.356563782;
        Real b3 = 1.781477937;
        Real b4 = -1.821255978;
        Real b5 = 1.330274429;
        Real xAbs = std::abs(x);

        const Real t = inverse(1 + b0 * xAbs);

        //Real result = gaussianPdf<Real>(xAbs) * (
        // t * (b1 + t * (b2 + t * (b3 + t * (b4 + t * b5)))));

        Real t2 = square(t);

        const Real t3 = t2 * t;
        Real t4 = square(t2);

        const Real t5 = t4 * t;

        Real result = gaussianPdf<Real>(xAbs) * (
            t * b1 + t2 * b2 + t3 * b3 + t4 * b4 + t5 * b5);

        if (x > 0)
        {
            result = (Real)1 - result;
        }

        return result;
    }

    template <typename Real>
    Real gaussianCdf(
        const NoDeduction<Real>& x)
    {
        boost::math::normal_distribution<Real>
            normal;

        return boost::math::cdf(normal, x);
    }

    template <typename Real>
    Real gaussianCdf(
        const NoDeduction<Real>& x,
        const NoDeduction<Real>& deviation)
    {
        return Pastel::gaussianCdf<Real>(
            x / deviation);
    }

}

#endif