00001
00002
00003
00004 package edu.tum.cs.util.math;
00005
00006
00011 public class MathUtils {
00012
00027 public static abstract class ContinuedFraction {
00028
00030 private static final double DEFAULT_EPSILON = 10e-9;
00031
00035 protected ContinuedFraction() {
00036 super();
00037 }
00038
00046 protected abstract double getA(int n, double x);
00047
00055 protected abstract double getB(int n, double x);
00056
00063 public double evaluate(double x) {
00064 return evaluate(x, DEFAULT_EPSILON, Integer.MAX_VALUE);
00065 }
00066
00074 public double evaluate(double x, double epsilon) {
00075 return evaluate(x, epsilon, Integer.MAX_VALUE);
00076 }
00077
00085 public double evaluate(double x, int maxIterations) {
00086 return evaluate(x, DEFAULT_EPSILON, maxIterations);
00087 }
00088
00106 public double evaluate(double x, double epsilon, int maxIterations)
00107 {
00108 double[][] f = new double[2][2];
00109 double[][] a = new double[2][2];
00110 double[][] an = new double[2][2];
00111
00112 a[0][0] = getA(0, x);
00113 a[0][1] = 1.0;
00114 a[1][0] = 1.0;
00115 a[1][1] = 0.0;
00116
00117 return evaluate(1, x, a, an, f, epsilon, maxIterations);
00118 }
00119
00134 private double evaluate(
00135 int n,
00136 double x,
00137 double[][] a,
00138 double[][] an,
00139 double[][] f,
00140 double epsilon,
00141 int maxIterations)
00142 {
00143 double ret;
00144
00145
00146 an[0][0] = getA(n, x);
00147 an[0][1] = 1.0;
00148 an[1][0] = getB(n, x);
00149 an[1][1] = 0.0;
00150
00151
00152 f[0][0] = (a[0][0] * an[0][0]) + (a[0][1] * an[1][0]);
00153 f[0][1] = (a[0][0] * an[0][1]) + (a[0][1] * an[1][1]);
00154 f[1][0] = (a[1][0] * an[0][0]) + (a[1][1] * an[1][0]);
00155 f[1][1] = (a[1][0] * an[0][1]) + (a[1][1] * an[1][1]);
00156
00157
00158 if (Math.abs((f[0][0] * f[1][1]) - (f[1][0] * f[0][1])) <
00159 Math.abs(epsilon * f[1][0] * f[1][1]))
00160 {
00161 ret = f[0][0] / f[1][0];
00162 } else {
00163 if (n >= maxIterations) {
00164 throw new RuntimeException(
00165 "Continued fraction convergents failed to converge.");
00166 }
00167
00168 ret = evaluate(n + 1, x, f
00169 , an
00170 , a
00171 , epsilon, maxIterations);
00172 }
00173
00174 return ret;
00175 }
00176 }
00177
00179 private static final double DEFAULT_EPSILON = 10e-9;
00180
00182 private static double[] lanczos =
00183 {
00184 0.99999999999999709182,
00185 57.156235665862923517,
00186 -59.597960355475491248,
00187 14.136097974741747174,
00188 -0.49191381609762019978,
00189 .33994649984811888699e-4,
00190 .46523628927048575665e-4,
00191 -.98374475304879564677e-4,
00192 .15808870322491248884e-3,
00193 -.21026444172410488319e-3,
00194 .21743961811521264320e-3,
00195 -.16431810653676389022e-3,
00196 .84418223983852743293e-4,
00197 -.26190838401581408670e-4,
00198 .36899182659531622704e-5,
00199 };
00200
00201
00219 public static double logGamma(double x) {
00220 double ret;
00221
00222 if (Double.isNaN(x) || (x <= 0.0)) {
00223 ret = Double.NaN;
00224 } else {
00225 double g = 607.0 / 128.0;
00226
00227 double sum = 0.0;
00228 for (int i = 1; i < lanczos.length; ++i) {
00229 sum = sum + (lanczos[i] / (x + i));
00230 }
00231 sum = sum + lanczos[0];
00232
00233 double tmp = x + g + .5;
00234 ret = ((x + .5) * Math.log(tmp)) - tmp +
00235 (.5 * Math.log(2.0 * Math.PI)) + Math.log(sum) - Math.log(x);
00236 }
00237
00238 return ret;
00239 }
00240
00249 public static double regularizedGammaP(double a, double x) {
00250 return regularizedGammaP(a, x, DEFAULT_EPSILON, Integer.MAX_VALUE);
00251 }
00252
00253
00280 public static double regularizedGammaP(double a, double x, double epsilon, int maxIterations) {
00281 double ret;
00282
00283 if (Double.isNaN(a) || Double.isNaN(x) || (a <= 0.0) || (x < 0.0)) {
00284 ret = Double.NaN;
00285 } else if (x == 0.0) {
00286 ret = 0.0;
00287 } else if (a > 1.0 && x > a) {
00288
00289
00290 ret = 1.0 - regularizedGammaQ(a, x, epsilon, maxIterations);
00291 } else {
00292
00293 double n = 0.0;
00294 double an = 1.0 / a;
00295 double sum = an;
00296 while (Math.abs(an) > epsilon && n < maxIterations) {
00297
00298 n = n + 1.0;
00299 an = an * (x / (a + n));
00300
00301
00302 sum = sum + an;
00303 }
00304 if (n >= maxIterations) {
00305 throw new RuntimeException(
00306 "maximum number of iterations reached");
00307 } else {
00308 ret = Math.exp(-x + (a * Math.log(x)) - logGamma(a)) * sum;
00309 }
00310 }
00311
00312 return ret;
00313 }
00314
00323 public static double regularizedGammaQ(double a, double x) {
00324 return regularizedGammaQ(a, x, DEFAULT_EPSILON, Integer.MAX_VALUE);
00325 }
00326
00349 public static double regularizedGammaQ(final double a,
00350 double x,
00351 double epsilon,
00352 int maxIterations)
00353 {
00354 double ret;
00355
00356 if (Double.isNaN(a) || Double.isNaN(x) || (a <= 0.0) || (x < 0.0)) {
00357 ret = Double.NaN;
00358 } else if (x == 0.0) {
00359 ret = 1.0;
00360 } else if (x < a || a <= 1.0) {
00361
00362
00363 ret = 1.0 - regularizedGammaP(a, x, epsilon, maxIterations);
00364 } else {
00365
00366 ContinuedFraction cf = new ContinuedFraction() {
00367 protected double getA(int n, double x) {
00368 return ((2.0 * n) + 1.0) - a + x;
00369 }
00370
00371 protected double getB(int n, double x) {
00372 return n * (a - n);
00373 }
00374 };
00375
00376 ret = 1.0 / cf.evaluate(x, epsilon, maxIterations);
00377 ret = Math.exp(-x + (a * Math.log(x)) - logGamma(a)) * ret;
00378 }
00379
00380 return ret;
00381 }
00382
00397 public static double erf(double x) {
00398 if (Double.isInfinite(x)) {
00399 if (x>0)
00400 return 1;
00401 else
00402 return -1;
00403 }
00404 if (Double.isNaN(x))
00405 return Double.NaN;
00406 double ret = regularizedGammaP(0.5, x * x, 1.0e-15, 10000);
00407 if (x < 0) {
00408 ret = -ret;
00409 }
00410 return ret;
00411 }
00412 }