1 package nl.tudelft.simulation.jstats.math;
2
3 import org.djutils.exceptions.Throw;
4
5
6
7
8
9
10
11
12
13
14
15 public final class ProbMath
16 {
17
18 public static final double[] FACTORIAL_DOUBLE;
19
20
21 public static final long[] FACTORIAL_LONG;
22
23
24 public static final double[] POW2_DOUBLE;
25
26
27 public static final long[] POW2_LONG;
28
29
30 static
31 {
32 FACTORIAL_DOUBLE = new double[171];
33 FACTORIAL_DOUBLE[0] = 1.0;
34 double d = 1.0;
35 for (int i = 1; i <= 170; i++)
36 {
37 d = d * i;
38 FACTORIAL_DOUBLE[i] = d;
39 }
40
41 FACTORIAL_LONG = new long[21];
42 FACTORIAL_LONG[0] = 1L;
43 long n = 1L;
44 for (int i = 1; i <= 20; i++)
45 {
46 n = n * i;
47 FACTORIAL_LONG[i] = n;
48 }
49
50 POW2_DOUBLE = new double[1024];
51 POW2_DOUBLE[0] = 1.0;
52 for (int i = 1; i < 1024; i++)
53 {
54 POW2_DOUBLE[i] = 2.0 * POW2_DOUBLE[i - 1];
55 }
56
57 POW2_LONG = new long[63];
58 POW2_LONG[0] = 1L;
59 for (int i = 1; i < 63; i++)
60 {
61 POW2_LONG[i] = 2L * POW2_LONG[i - 1];
62 }
63 }
64
65
66
67
68 private ProbMath()
69 {
70
71 }
72
73
74
75
76
77
78 public static double factorial(final int n)
79 {
80 Throw.when(n < 0, IllegalArgumentException.class, "n! with n<0 is invalid");
81 Throw.when(n > 170, IllegalArgumentException.class, "n! with n>170 is larger than Double.MAX_VALUE");
82 return FACTORIAL_DOUBLE[n];
83 }
84
85
86
87
88
89
90 public static double factorial(final long n)
91 {
92 Throw.when(n < 0, IllegalArgumentException.class, "n! with n<0 is invalid");
93 Throw.when(n > 170, IllegalArgumentException.class, "n! with n>170 is larger than Double.MAX_VALUE");
94 return FACTORIAL_DOUBLE[(int) n];
95 }
96
97
98
99
100
101
102 public static long fac(final int n)
103 {
104 Throw.when(n < 0, IllegalArgumentException.class, "n! with n<0 is invalid");
105 Throw.when(n > 20, IllegalArgumentException.class, "n! with n>20 is more than 64 bits long");
106 return FACTORIAL_LONG[n];
107 }
108
109
110
111
112
113
114 public static long fac(final long n)
115 {
116 Throw.when(n < 0, IllegalArgumentException.class, "n! with n<0 is invalid");
117 Throw.when(n > 20, IllegalArgumentException.class, "n! with n>20 is more than 64 bits long");
118 return FACTORIAL_LONG[(int) n];
119 }
120
121
122
123
124
125
126
127 public static double permutations(final int n, final int k)
128 {
129 if (k > n)
130 {
131 throw new IllegalArgumentException("permutations of (n,k) with k>n");
132 }
133 return factorial(n) / factorial(n - k);
134 }
135
136
137
138
139
140
141
142 public static double permutations(final long n, final long k)
143 {
144 if (k > n)
145 {
146 throw new IllegalArgumentException("permutations of (n,k) with k>n");
147 }
148 return factorial(n) / factorial(n - k);
149 }
150
151
152
153
154
155
156
157 public static long perm(final int n, final int k)
158 {
159 if (k > n)
160 {
161 throw new IllegalArgumentException("permutations of (n,k) with k>n");
162 }
163 return fac(n) / fac(n - k);
164 }
165
166
167
168
169
170
171
172 public static long perm(final long n, final long k)
173 {
174 if (k > n)
175 {
176 throw new IllegalArgumentException("permutations of (n,k) with k>n");
177 }
178 return fac(n) / fac(n - k);
179 }
180
181
182
183
184
185
186
187 public static double combinations(final int n, final int k)
188 {
189 if (k > n)
190 {
191 throw new IllegalArgumentException("combinations of (n,k) with k>n");
192 }
193 return factorial(n) / (factorial(k) * factorial(n - k));
194 }
195
196
197
198
199
200
201
202 public static double combinations(final long n, final long k)
203 {
204 if (k > n)
205 {
206 throw new IllegalArgumentException("combinations of (n,k) with k>n");
207 }
208 return factorial(n) / (factorial(k) * factorial(n - k));
209 }
210
211
212
213
214
215
216
217 public static long comb(final int n, final int k)
218 {
219 if (k > n)
220 {
221 throw new IllegalArgumentException("combinations of (n,k) with k>n");
222 }
223 return fac(n) / (fac(k) * fac(n - k));
224 }
225
226
227
228
229
230
231
232 public static long comb(final long n, final long k)
233 {
234 if (k > n)
235 {
236 throw new IllegalArgumentException("combinations of (n,k) with k>n");
237 }
238 return fac(n) / (fac(k) * fac(n - k));
239 }
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257 public static double erf(final double z)
258 {
259 double zpos = Math.abs(z);
260 if (zpos < 0.5)
261 {
262 return erfSmall(z);
263 }
264 if (zpos > 3.8)
265 {
266 return erfBig(z);
267 }
268 return erfTaylor(z);
269 }
270
271
272
273
274
275
276
277
278
279
280 private static double erfSmall(final double z)
281 {
282 double zpos = Math.abs(z);
283
284 double sum = zpos
285 + 2.0 * Math.pow(zpos, 3) / 3.0
286 + 4.0 * Math.pow(zpos, 5) / 15.0
287 + 8.0 * Math.pow(zpos, 7) / 105.0
288 + 16.0 * Math.pow(zpos, 9) / 945.0
289 + 32.0 * Math.pow(zpos, 11) / 10395.0
290 + 64.0 * Math.pow(zpos, 13) / 135135.0
291 + 128.0 * Math.pow(zpos, 15) / 2027025.0
292 + 256.0 * Math.pow(zpos, 17) / 34459425.0
293 + 512.0 * Math.pow(zpos, 19) / 654729075.0
294 + 1024.0 * Math.pow(zpos, 21) / 13749310575.0;
295
296 return Math.signum(z) * sum * 2.0 * Math.exp(-zpos * zpos) / Math.sqrt(Math.PI);
297 }
298
299
300
301
302
303
304
305
306
307
308
309 private static double erfBig(final double z)
310 {
311 double zpos = Math.abs(z);
312
313 double sum = 1.0 / zpos
314 - (1.0 / 2.0) * Math.pow(zpos, -3)
315 + (3.0 / 4.0) * Math.pow(zpos, -5)
316 - (15.0 / 8.0) * Math.pow(zpos, -7)
317 + (105.0 / 16.0) * Math.pow(zpos, -9)
318 - (945.0 / 32.0) * Math.pow(zpos, -11)
319 + (10395.0 / 64.0) * Math.pow(zpos, -13)
320 - (135135.0 / 128.0) * Math.pow(zpos, -15)
321 + (2027025.0 / 256.0) * Math.pow(zpos, -17);
322
323 return Math.signum(z) * (1.0 - sum * Math.exp(-zpos * zpos) / Math.sqrt(Math.PI));
324 }
325
326
327
328
329
330
331
332
333
334
335
336 private static double erfTaylor(final double z)
337 {
338 double zpos = Math.abs(z);
339 double d = zpos;
340 double zpow = zpos;
341 for (int i = 1; i < 64; i++)
342 {
343
344 zpow *= zpos * zpos;
345 double term = zpow / ((2.0 * i + 1.0) * ProbMath.factorial(i));
346 d += term * ((i & 1) == 0 ? 1 : -1);
347 if (term < 1E-16)
348 {
349 break;
350 }
351 }
352 return Math.signum(z) * d * 2.0 / Math.sqrt(Math.PI);
353 }
354
355
356
357
358
359
360
361 public static double erfInv(final double y)
362 {
363 double ax, t, ret;
364 ax = Math.abs(y);
365
366
367
368
369
370 if (ax <= 0.75)
371 {
372
373 double[] p = new double[] {-13.0959967422, 26.785225760, -9.289057635};
374 double[] q = new double[] {-12.0749426297, 30.960614529, -17.149977991, 1.00000000};
375
376 t = ax * ax - 0.75 * 0.75;
377 ret = ax * (p[0] + t * (p[1] + t * p[2])) / (q[0] + t * (q[1] + t * (q[2] + t * q[3])));
378
379 }
380 else if (ax >= 0.75 && ax <= 0.9375)
381 {
382 double[] p = new double[] {-.12402565221, 1.0688059574, -1.9594556078, .4230581357};
383 double[] q = new double[] {-.08827697997, .8900743359, -2.1757031196, 1.0000000000};
384
385
386
387
388
389 t = ax * ax - 0.9375 * 0.9375;
390 ret = ax * (p[0] + t * (p[1] + t * (p[2] + t * p[3]))) / (q[0] + t * (q[1] + t * (q[2] + t * q[3])));
391
392 }
393 else if (ax >= 0.9375 && ax <= (1.0 - 1.0e-9))
394 {
395 double[] p =
396 new double[] {.1550470003116, 1.382719649631, .690969348887, -1.128081391617, .680544246825, -.16444156791};
397 double[] q = new double[] {.155024849822, 1.385228141995, 1.000000000000};
398
399
400
401
402
403 t = 1.0 / Math.sqrt(-Math.log(1.0 - ax));
404 ret = (p[0] / t + p[1] + t * (p[2] + t * (p[3] + t * (p[4] + t * p[5])))) / (q[0] + t * (q[1] + t * (q[2])));
405 }
406 else
407 {
408 ret = Double.POSITIVE_INFINITY;
409 }
410
411 return Math.signum(y) * ret;
412 }
413
414
415 private static final double[] GAMMALN_COF = {76.18009172947146, -86.50532032941677, 24.01409824083091, -1.231739572450155,
416 0.1208650973866179e-2, -0.5395239384953e-5};
417
418
419
420
421
422
423
424 public static double gammaln(final double xx)
425 {
426 Throw.when(xx < 0, IllegalArgumentException.class, "gamma function not defined for real values < 0");
427 double x, y, tmp, ser;
428 x = xx;
429 y = x;
430 tmp = x + 5.5;
431 tmp -= (x + 0.5) * Math.log(tmp);
432 ser = 1.000000000190015;
433 for (int j = 0; j <= 5; j++)
434 {
435 ser += GAMMALN_COF[j] / ++y;
436 }
437 return -tmp + Math.log(2.5066282746310005 * ser / x);
438 }
439
440
441
442
443
444
445
446 public static double gamma(final double x)
447 {
448 return Math.exp(gammaln(x));
449 }
450
451
452
453
454
455
456
457
458 public static double beta(final double z, final double w)
459 {
460 Throw.when(z < 0 || w < 0, IllegalArgumentException.class, "beta function not defined for negative arguments");
461 return Math.exp(gammaln(z) + gammaln(w) - gammaln(z + w));
462 }
463
464 }