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 <= 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}