( cdf: (k: number) => number, prob: number, mean: number, stddev: number, kMin: number, kMax: number, deadline?: number )
| 18 | * search seeded from the normal approximation. `kMax` may be `+∞` (Poisson). |
| 19 | */ |
| 20 | export function discreteQuantile( |
| 21 | cdf: (k: number) => number, |
| 22 | prob: number, |
| 23 | mean: number, |
| 24 | stddev: number, |
| 25 | kMin: number, |
| 26 | kMax: number, |
| 27 | deadline?: number |
| 28 | ): number { |
| 29 | if (prob <= 0) return kMin; |
| 30 | if (prob >= 1) return kMax; |
| 31 | |
| 32 | // A tiny tolerance makes the `CDF(k) ≥ p` boundary inclusive against |
| 33 | // round-off: when `p` is itself a CDF value (the `Quantile(CDF(k)) = k` |
| 34 | // identity) it may be recomputed a few ulps above the machine CDF used in |
| 35 | // this search, which would otherwise overshoot by one. |
| 36 | const target = prob - 1e-12; |
| 37 | |
| 38 | // Seed from the normal-approximation inverse-CDF, then correct. |
| 39 | let k = Math.round(mean + Math.SQRT2 * erfInv(2 * prob - 1) * stddev); |
| 40 | if (!Number.isFinite(k)) k = Math.round(mean); |
| 41 | k = Math.max(k, kMin); |
| 42 | if (Number.isFinite(kMax)) k = Math.min(k, kMax); |
| 43 | |
| 44 | let guard = 0; |
| 45 | // Step up while the CDF is still below the target probability. |
| 46 | while (k < kMax && cdf(k) < target) { |
| 47 | k++; |
| 48 | if ((++guard & 0x3ff) === 0) checkDeadline(deadline); |
| 49 | } |
| 50 | // Step down while the previous integer already reaches the target. |
| 51 | while (k > kMin && cdf(k - 1) >= target) { |
| 52 | k--; |
| 53 | if ((++guard & 0x3ff) === 0) checkDeadline(deadline); |
| 54 | } |
| 55 | return k; |
| 56 | } |
| 57 | |
| 58 | /** Quantile of Binomial(n, p) at probability `prob` (an integer in [0, n]). */ |
| 59 | export function binomialQuantile( |
no test coverage detected