UNPKG

brent-zero-generator

Version:

Brent's root finding algorithm, as a generator function.

136 lines (113 loc) 3.98 kB
/** * zero_generator2() seeks a root in an interval [a, b]. * * zero_generator() is based on Brent's function `zero()`. * Generator function `zero_generator` returns a zero `x` in * the interval [a,b], to within a tolerance `6 macheps |x| + 2 t`, * where `macheps` is the relative machine precision, and `t` is a * positive tolerance. The function assumes that `f(a)` and `f(b)` have * different signs. * * @param {number} a * @param {number} b * @param {number} [macheps=Number.EPSILON] - The macheps (Default: Number.EPSILON) * @param {number} [t=0.0] * Used to determine how close the bounds bracketing root finding * must converge before quitting. (Default: 0.0) * @returns {Generator<number,number[],number>} */ function* zero_generator2(a, b, macheps=Number.EPSILON, t=0.0) { let fa = yield a; let fb = yield b; let [c, fc] = [a, fa]; let e = b - a; let d = e; while (true) { if (Math.abs(fc) < Math.abs(fb)) { [a, fa] = [b, fb]; [b, fb] = [c, fc]; [c, fc] = [a, fa]; } const tol = 2 * macheps * Math.abs(b) + t; const m = 0.5 * (c - b); if (Math.abs(m) <= tol || fb == 0.0) return [b, fb, c, fc]; // See if bisection is forced if (Math.abs(e) < tol || Math.abs(fa) <= Math.abs(fb)) { d = e = m; } else { const s = fb / fa; let p, q; if (a == c) { // Linear interpolation p = 2 * m * s; q = 1 - s; } else { // Inverse quadratic interpolation q = fa / fc; const r = fb / fc; p = s * (2 * m * q * (q - r) - (b - a) * (r - 1)); q = (q - 1) * (r - 1) * (s - 1); } if (p > 0) q = -q; else p = -p; if (2 * p < 3 * m * q - Math.abs(tol * q) && p < Math.abs(0.5 * e * q)) { e = d; d = p / q; } else { d = e = m; } } [a, fa] = [b, fb]; b += (Math.abs(d) > tol ? d : (m > 0 ? tol : -tol)); fb = yield b; if ( (fb > 0) == (fc > 0) ) { [c, fc] = [a, fa]; d = e = b - a; } } } /* zero_generator2 */ /** * zero_generator() simplifies the return value from * zero_generator2(). Its main purpose is not to break * the API from version 1.0. * * @param {number} a * @param {number} b * @param {number} [macheps=Number.EPSILON] - The macheps (Default: Number.EPSILON) * @param {number} [t=0.0] * Used to determine how close the bounds bracketing root finding * must converge before quitting. (Default: 0.0) * @returns {Generator<number,number,number>} */ function* zero_generator(a, b, macheps=Number.EPSILON, t=0.0) { const gen = zero_generator2(a, b, macheps, t); const res = yield* gen; return res[0]; } /** * @param {(x:number)=>number} f - The function for which roots are desired. * @param {number} a - The lower value used to bracket root finding into the function. * @param {number} b - The upper value used to bracket root finding into the function. * @param {number=} macheps - The macheps (default: Number.EPSILON) * @param {number} t - Used to determine how close the bounds bracketing * root finding must converge before quitting. (default: 0.0) * @returns Returns a input value resulting in a suitable root if successful. Returns false on failure. */ function zero(f, a, b, macheps=Number.EPSILON, t=0.0) { const gen = zero_generator(a, b, macheps, t); let result = gen.next(); while ( ! result.done ) { const x = result.value; const y = f(x); result = gen.next(y) } return result.value; } module.exports = { zero_generator2, zero_generator, zero, };