1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.hipparchus.special;
23
24 import org.hipparchus.exception.LocalizedCoreFormats;
25 import org.hipparchus.exception.MathIllegalArgumentException;
26 import org.hipparchus.util.ContinuedFraction;
27 import org.hipparchus.util.FastMath;
28 import org.hipparchus.util.MathUtils;
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 public class Beta {
60
61 private static final double DEFAULT_EPSILON = 1E-14;
62
63
64 private static final double HALF_LOG_TWO_PI = .9189385332046727;
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86 private static final double[] DELTA = {
87 .833333333333333333333333333333E-01,
88 -.277777777777777777777777752282E-04,
89 .793650793650793650791732130419E-07,
90 -.595238095238095232389839236182E-09,
91 .841750841750832853294451671990E-11,
92 -.191752691751854612334149171243E-12,
93 .641025640510325475730918472625E-14,
94 -.295506514125338232839867823991E-15,
95 .179643716359402238723287696452E-16,
96 -.139228964661627791231203060395E-17,
97 .133802855014020915603275339093E-18,
98 -.154246009867966094273710216533E-19,
99 .197701992980957427278370133333E-20,
100 -.234065664793997056856992426667E-21,
101 .171348014966398575409015466667E-22
102 };
103
104
105
106
107 private Beta() {}
108
109
110
111
112
113
114
115
116
117
118
119
120
121 public static double regularizedBeta(double x, double a, double b) {
122 return regularizedBeta(x, a, b, DEFAULT_EPSILON, Integer.MAX_VALUE);
123 }
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140 public static double regularizedBeta(double x,
141 double a, double b,
142 double epsilon) {
143 return regularizedBeta(x, a, b, epsilon, Integer.MAX_VALUE);
144 }
145
146
147
148
149
150
151
152
153
154
155
156
157 public static double regularizedBeta(double x,
158 double a, double b,
159 int maxIterations) {
160 return regularizedBeta(x, a, b, DEFAULT_EPSILON, maxIterations);
161 }
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187 public static double regularizedBeta(double x,
188 final double a, final double b,
189 double epsilon, int maxIterations) {
190 double ret;
191
192 if (Double.isNaN(x) ||
193 Double.isNaN(a) ||
194 Double.isNaN(b) ||
195 x < 0 ||
196 x > 1 ||
197 a <= 0 ||
198 b <= 0) {
199 ret = Double.NaN;
200 } else if (x > (a + 1) / (2 + b + a) &&
201 1 - x <= (b + 1) / (2 + b + a)) {
202 ret = 1 - regularizedBeta(1 - x, b, a, epsilon, maxIterations);
203 } else {
204 ContinuedFraction fraction = new ContinuedFraction() {
205
206
207 @Override
208 protected double getB(int n, double x) {
209 double ret;
210 double m;
211 if (n % 2 == 0) {
212 m = n / 2.0;
213 ret = (m * (b - m) * x) /
214 ((a + (2 * m) - 1) * (a + (2 * m)));
215 } else {
216 m = (n - 1.0) / 2.0;
217 ret = -((a + m) * (a + b + m) * x) /
218 ((a + (2 * m)) * (a + (2 * m) + 1.0));
219 }
220 return ret;
221 }
222
223
224 @Override
225 protected double getA(int n, double x) {
226 return 1.0;
227 }
228 };
229 ret = FastMath.exp((a * FastMath.log(x)) + (b * FastMath.log1p(-x)) -
230 FastMath.log(a) - logBeta(a, b)) *
231 1.0 / fraction.evaluate(x, epsilon, maxIterations);
232 }
233
234 return ret;
235 }
236
237
238
239
240
241
242
243
244
245
246
247
248
249 private static double logGammaSum(final double a, final double b)
250 throws MathIllegalArgumentException {
251
252 MathUtils.checkRangeInclusive(a, 1, 2);
253 MathUtils.checkRangeInclusive(b, 1, 2);
254
255 final double x = (a - 1.0) + (b - 1.0);
256 if (x <= 0.5) {
257 return Gamma.logGamma1p(1.0 + x);
258 } else if (x <= 1.5) {
259 return Gamma.logGamma1p(x) + FastMath.log1p(x);
260 } else {
261 return Gamma.logGamma1p(x - 1.0) + FastMath.log(x * (1.0 + x));
262 }
263 }
264
265
266
267
268
269
270
271
272
273
274
275
276
277 private static double logGammaMinusLogGammaSum(final double a,
278 final double b)
279 throws MathIllegalArgumentException {
280
281 if (a < 0.0) {
282 throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL,
283 a, 0.0);
284 }
285 if (b < 10.0) {
286 throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL,
287 b, 10.0);
288 }
289
290
291
292
293 final double d;
294 final double w;
295 if (a <= b) {
296 d = b + (a - 0.5);
297 w = deltaMinusDeltaSum(a, b);
298 } else {
299 d = a + (b - 0.5);
300 w = deltaMinusDeltaSum(b, a);
301 }
302
303 final double u = d * FastMath.log1p(a / b);
304 final double v = a * (FastMath.log(b) - 1.0);
305
306 return u <= v ? (w - u) - v : (w - v) - u;
307 }
308
309
310
311
312
313
314
315
316
317
318
319 private static double deltaMinusDeltaSum(final double a,
320 final double b)
321 throws MathIllegalArgumentException {
322
323 MathUtils.checkRangeInclusive(a, 0, b);
324 if (b < 10) {
325 throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL,
326 b, 10);
327 }
328
329 final double h = a / b;
330 final double p = h / (1.0 + h);
331 final double q = 1.0 / (1.0 + h);
332 final double q2 = q * q;
333
334
335
336 final double[] s = new double[DELTA.length];
337 s[0] = 1.0;
338 for (int i = 1; i < s.length; i++) {
339 s[i] = 1.0 + (q + q2 * s[i - 1]);
340 }
341
342
343
344 final double sqrtT = 10.0 / b;
345 final double t = sqrtT * sqrtT;
346 double w = DELTA[DELTA.length - 1] * s[s.length - 1];
347 for (int i = DELTA.length - 2; i >= 0; i--) {
348 w = t * w + DELTA[i] * s[i];
349 }
350 return w * p / b;
351 }
352
353
354
355
356
357
358
359
360
361
362
363
364
365 private static double sumDeltaMinusDeltaSum(final double p,
366 final double q) {
367
368 if (p < 10.0) {
369 throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL,
370 p, 10.0);
371 }
372 if (q < 10.0) {
373 throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL,
374 q, 10.0);
375 }
376
377 final double a = FastMath.min(p, q);
378 final double b = FastMath.max(p, q);
379 final double sqrtT = 10.0 / a;
380 final double t = sqrtT * sqrtT;
381 double z = DELTA[DELTA.length - 1];
382 for (int i = DELTA.length - 2; i >= 0; i--) {
383 z = t * z + DELTA[i];
384 }
385 return z / a + deltaMinusDeltaSum(a, b);
386 }
387
388
389
390
391
392
393
394
395
396
397
398 public static double logBeta(final double p, final double q) {
399 if (Double.isNaN(p) || Double.isNaN(q) || (p <= 0.0) || (q <= 0.0)) {
400 return Double.NaN;
401 }
402
403 final double a = FastMath.min(p, q);
404 final double b = FastMath.max(p, q);
405 if (a >= 10.0) {
406 final double w = sumDeltaMinusDeltaSum(a, b);
407 final double h = a / b;
408 final double c = h / (1.0 + h);
409 final double u = -(a - 0.5) * FastMath.log(c);
410 final double v = b * FastMath.log1p(h);
411 if (u <= v) {
412 return (((-0.5 * FastMath.log(b) + HALF_LOG_TWO_PI) + w) - u) - v;
413 } else {
414 return (((-0.5 * FastMath.log(b) + HALF_LOG_TWO_PI) + w) - v) - u;
415 }
416 } else if (a > 2.0) {
417 if (b > 1000.0) {
418 final int n = (int) FastMath.floor(a - 1.0);
419 double prod = 1.0;
420 double ared = a;
421 for (int i = 0; i < n; i++) {
422 ared -= 1.0;
423 prod *= ared / (1.0 + ared / b);
424 }
425 return (FastMath.log(prod) - n * FastMath.log(b)) +
426 (Gamma.logGamma(ared) +
427 logGammaMinusLogGammaSum(ared, b));
428 } else {
429 double prod1 = 1.0;
430 double ared = a;
431 while (ared > 2.0) {
432 ared -= 1.0;
433 final double h = ared / b;
434 prod1 *= h / (1.0 + h);
435 }
436 if (b < 10.0) {
437 double prod2 = 1.0;
438 double bred = b;
439 while (bred > 2.0) {
440 bred -= 1.0;
441 prod2 *= bred / (ared + bred);
442 }
443 return FastMath.log(prod1) +
444 FastMath.log(prod2) +
445 (Gamma.logGamma(ared) +
446 (Gamma.logGamma(bred) -
447 logGammaSum(ared, bred)));
448 } else {
449 return FastMath.log(prod1) +
450 Gamma.logGamma(ared) +
451 logGammaMinusLogGammaSum(ared, b);
452 }
453 }
454 } else if (a >= 1.0) {
455 if (b > 2.0) {
456 if (b < 10.0) {
457 double prod = 1.0;
458 double bred = b;
459 while (bred > 2.0) {
460 bred -= 1.0;
461 prod *= bred / (a + bred);
462 }
463 return FastMath.log(prod) +
464 (Gamma.logGamma(a) +
465 (Gamma.logGamma(bred) -
466 logGammaSum(a, bred)));
467 } else {
468 return Gamma.logGamma(a) +
469 logGammaMinusLogGammaSum(a, b);
470 }
471 } else {
472 return Gamma.logGamma(a) +
473 Gamma.logGamma(b) -
474 logGammaSum(a, b);
475 }
476 } else {
477 if (b >= 10.0) {
478 return Gamma.logGamma(a) +
479 logGammaMinusLogGammaSum(a, b);
480 } else {
481
482
483
484
485 return FastMath.log(Gamma.gamma(a) * Gamma.gamma(b) /
486 Gamma.gamma(a + b));
487 }
488 }
489 }
490 }