distriprob
Version:
A library for calculating the PDF, CDFs, and quantile function values of common probability distributions
161 lines (160 loc) • 5.13 kB
JavaScript
;
function rootFind(fctn, derivativeFctn, value, initialRootEstimate, max, min) {
var root = newton(fctn, derivativeFctn, value, initialRootEstimate, max, min);
if (root === "non-convergent") {
root = bisection(fctn, value, max, min);
}
return root;
}
exports.rootFind = rootFind;
function newton(fctn, derivativeFctn, value, initialRootEstimate, max, min) {
const MAX_ITERATIONS = 1000;
const RELATIVE_ERROR_TOLERANCE = 1e-17;
let iterations = 0;
let oldX = initialRootEstimate;
let x;
let approximateRelativeError;
let fOfX;
let fPrimeOfX;
while (true) {
fOfX = fctn(oldX);
fPrimeOfX = derivativeFctn(oldX);
x = oldX - ((fOfX - value) / fPrimeOfX);
if (min !== null && x < min) {
x = 1e-14 + min;
}
else if (max !== null && x > max) {
x = max - 1e-14;
}
if (x === Infinity || x === -Infinity || fPrimeOfX === 0) {
return "non-convergent";
}
approximateRelativeError = Math.abs(x - oldX) / Math.abs(value);
if (approximateRelativeError < RELATIVE_ERROR_TOLERANCE
|| iterations > MAX_ITERATIONS) {
break;
}
oldX = x;
iterations++;
}
return x;
}
exports.newton = newton;
function bisection(fctn, value, max, min) {
const MAX_ITERATIONS = 1000;
const RELATIVE_ERROR_TOLERANCE = 0.00000000000001;
let upper_root_bound;
let upper_root_bound_function_value;
let lower_root_bound;
let lower_root_bound_function_value;
let approximateRelativeError;
let iterations = 0;
let x;
let oldX;
let fOfOldX;
if (max === null) {
upper_root_bound = 1;
do {
upper_root_bound = 2 * Math.abs(upper_root_bound);
upper_root_bound_function_value = fctn(upper_root_bound) - value;
} while (upper_root_bound_function_value < 0);
}
else {
upper_root_bound = max;
upper_root_bound_function_value = fctn(max) - value;
}
if (min === null) {
lower_root_bound = -1;
do {
lower_root_bound = -2 * Math.abs(lower_root_bound);
lower_root_bound_function_value = fctn(lower_root_bound) - value;
} while (lower_root_bound_function_value > 0);
}
else {
lower_root_bound = min;
lower_root_bound_function_value = fctn(min) - value;
}
oldX = (upper_root_bound + lower_root_bound) / 2;
do {
fOfOldX = fctn(oldX) - value;
if (fOfOldX > 0) {
upper_root_bound = oldX;
upper_root_bound_function_value = fOfOldX;
}
else {
lower_root_bound = oldX;
lower_root_bound_function_value = fOfOldX;
}
x = (upper_root_bound + lower_root_bound) / 2;
approximateRelativeError = Math.abs(x - oldX) / Math.abs(value);
oldX = x;
iterations++;
} while (approximateRelativeError >= RELATIVE_ERROR_TOLERANCE
&& iterations < MAX_ITERATIONS);
return x;
}
exports.bisection = bisection;
function discreteQuantileFind(simplifiedCDF, p, max, min, initialEstimate = 0, lowerTail = true) {
if (max === null) {
// find a value of max where cdf(max) >= p for lowerTail === true and cdf(max) <= p
// for lowerTail === false
let offset = 0.5;
let cdfVal;
do {
offset *= 2;
max = initialEstimate + offset;
cdfVal = simplifiedCDF(max);
if ((cdfVal < p && lowerTail) || (cdfVal > p && !lowerTail)) {
if (min === null || min < max) {
min = max;
}
}
else {
break;
}
} while (true);
}
if (min === null) {
// find a value of min where cdf(min) < p for lowerTail === true and cdf(min) > p
// for lowerTail === true
let offset = 0.5;
let cdfVal;
do {
offset *= 2;
min = initialEstimate - offset;
cdfVal = simplifiedCDF(min);
} while ((cdfVal >= p && lowerTail) || (cdfVal <= p && !lowerTail));
}
// check that min doesn't satisfy requirements
let cdfMin = simplifiedCDF(min);
if ((cdfMin >= p && lowerTail) || (cdfMin <= p && !lowerTail)) {
return min;
}
let center;
let centerFloor;
let centerCeil;
let maxMinAve;
while (max - min > 1) {
maxMinAve = (max + min) / 2;
centerFloor = Math.floor(maxMinAve);
centerCeil = Math.ceil(maxMinAve);
if (centerFloor === min) {
if (centerCeil === max) {
break;
}
center = centerCeil;
}
else {
center = centerFloor;
}
let cdfValCenter = simplifiedCDF(center);
if ((cdfValCenter >= p && lowerTail) || (cdfValCenter <= p && !lowerTail)) {
max = center;
}
else {
min = center;
}
}
return max;
}
exports.discreteQuantileFind = discreteQuantileFind;