001package com.hfg.math;
002
003
004//------------------------------------------------------------------------------
005/**
006 For calculations related to standard normal distributions.
007
008 @author J. Alex Taylor, hairyfatguy.com
009 */
010//------------------------------------------------------------------------------
011// com.hfg XML/HTML Coding Library
012//
013// This library is free software; you can redistribute it and/or
014// modify it under the terms of the GNU Lesser General Public
015// License as published by the Free Software Foundation; either
016// version 2.1 of the License, or (at your option) any later version.
017//
018// This library is distributed in the hope that it will be useful,
019// but WITHOUT ANY WARRANTY; without even the implied warranty of
020// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
021// Lesser General Public License for more details.
022//
023// You should have received a copy of the GNU Lesser General Public
024// License along with this library; if not, write to the Free Software
025// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026//
027// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
028// jataylor@hairyfatguy.com
029//------------------------------------------------------------------------------
030
031public class StandardNormalDistribution
032{
033   private Double mMean;
034   private Double mSampleStandardDeviation;
035
036   // Coefficients in rational approximations for the inverse normal cumulative distribution function
037   private static final double[] sA_coeff = new double[] {
038         -3.969683028665376e+01,  2.209460984245205e+02,
039         -2.759285104469687e+02,  1.383577518672690e+02,
040         -3.066479806614716e+01,  2.506628277459239e+00 };
041
042
043   private static final double[] sB_coeff = new double[] {
044         -5.447609879822406e+01,  1.615858368580409e+02,
045         -1.556989798598866e+02,  6.680131188771972e+01,
046         -1.328068155288572e+01 };
047
048
049   private static final double[] sC_coeff = new double[] {
050         -7.784894002430293e-03, -3.223964580411365e-01,
051         -2.400758277161838e+00, -2.549732539343734e+00,
052         4.374664141464968e+00,  2.938163982698783e+00 };
053
054   private static final double[] sD_coeff = new double[] {
055         7.784695709041462e-03, 3.224671290700398e-01,
056         2.445134137142996e+00,  3.754408661907416e+00 };
057
058   //--------------------------------------------------------------------------
059   public StandardNormalDistribution()
060   {
061
062   }
063
064   //--------------------------------------------------------------------------
065   public StandardNormalDistribution setMean(Double inValue)
066   {
067      mMean = inValue;
068      return this;
069   }
070
071   //--------------------------------------------------------------------------
072   public Double getMean()
073   {
074      return mMean;
075   }
076
077
078   //--------------------------------------------------------------------------
079   public StandardNormalDistribution setSampleStandardDeviation(Double inValue)
080   {
081      mSampleStandardDeviation = inValue;
082      return this;
083   }
084
085   //--------------------------------------------------------------------------
086   public Double getSampleStandardDeviation()
087   {
088      return mSampleStandardDeviation;
089   }
090
091
092   //--------------------------------------------------------------------------
093   public double getZScore(double inValue)
094   {
095      // Z = (X - μ) / σ
096      return (inValue - mMean) / mSampleStandardDeviation;
097   }
098
099   //--------------------------------------------------------------------------
100   public double getProbability(double inValue)
101   {
102      double zScore = getZScore(inValue);
103
104      double probability = 0;
105
106      if (zScore < -7)
107      {
108         probability = 0.0;
109      }
110      else if (zScore > 7)
111      {
112         probability = 1.0;
113      }
114      else
115      {
116         boolean invert = (zScore > 0.0);
117
118         zScore = Math.abs(zScore);
119         double b = 0.0;
120         double s = Math.sqrt(2) / 3 * zScore;
121         double HH = .5;
122         for (int i = 0; i < 12; i++)
123         {
124            double a = Math.exp(-HH * HH / 9) * Math.sin(HH * s) / HH;
125            b = b + a;
126            HH = HH + 1.0;
127         }
128
129         probability = .5 - b / Math.PI;
130
131         if (invert)
132         {
133            probability = 1.0 - probability;
134         }
135      }
136
137      return probability;
138   }
139
140   //--------------------------------------------------------------------------
141   /**
142    Given a probability P, it returns an approximation to the X satisfying P = Pr{Z &lt;= X} where Z is a
143    random variable from the standard normal distribution. Uses the lower tail quantile for standard normal distribution function from
144    <a href='http://home.online.no/~pjacklam/notes/invnorm/'>http://home.online.no/~pjacklam/notes/invnorm/</a>.
145    */
146   public double getValueThresholdForProbability(float inProbability)
147   {
148      double z = 0;
149
150      // Define break-points.
151      double pLow  = 0.02425;
152      double pHigh = 1 - pLow;
153
154      if ( inProbability < pLow )
155      {
156         // Rational approximation for lower region:
157         double q  = Math.sqrt(-2*Math.log(inProbability));
158         z = (((((sC_coeff[0]*q+sC_coeff[1])*q+sC_coeff[2])*q+sC_coeff[3])*q+sC_coeff[4])*q+sC_coeff[5]) /
159               ((((sD_coeff[0]*q+sD_coeff[1])*q+sD_coeff[2])*q+sD_coeff[3])*q+1);
160      }
161      else if ( pHigh < inProbability )
162      {
163         // Rational approximation for upper region:
164         double q  = Math.sqrt(-2*Math.log(1 - inProbability));
165         z = -(((((sC_coeff[0]*q+sC_coeff[1])*q+sC_coeff[2])*q+sC_coeff[3])*q+sC_coeff[4])*q+sC_coeff[5]) /
166               ((((sD_coeff[0]*q+sD_coeff[1])*q+sD_coeff[2])*q+sD_coeff[3])*q+1);
167      }
168      else
169      {
170         // Rational approximation for central region:
171         double q = inProbability - 0.5;
172         double r = q * q;
173         z = (((((sA_coeff[0] * r + sA_coeff[1]) * r + sA_coeff[2]) * r + sA_coeff[3]) * r + sA_coeff[4]) * r + sA_coeff[5]) * q /
174               (((((sB_coeff[0] * r + sB_coeff[1]) * r + sB_coeff[2]) * r + sB_coeff[3]) * r + sB_coeff[4]) * r + 1);
175      }
176
177      // X = μ + Zσ
178      return getMean() + (z * getSampleStandardDeviation());
179   }
180}