From f4094dd470e0bd669012f528b9bbba527c58415f Mon Sep 17 00:00:00 2001 From: muhammedkucukaslan Date: Mon, 16 Dec 2024 18:46:57 +0300 Subject: [PATCH] feat(maths): add bisection method --- maths/bisection.ts | 61 ++++++++++++++++++++++++++++++++++++ maths/test/bisection.test.ts | 32 +++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 maths/bisection.ts create mode 100644 maths/test/bisection.test.ts diff --git a/maths/bisection.ts b/maths/bisection.ts new file mode 100644 index 00000000..7e2c1d3c --- /dev/null +++ b/maths/bisection.ts @@ -0,0 +1,61 @@ +/** + * + * @description Find the root of a function within a given interval using the bisection method. + * @param f The function for which the root is being sought + * @param a The start of the interval + * @param b The end of the interval + * @param tol The tolerance value, determining the precision of the root + * @param maxIter The maximum number of iterations + * @returns The approximate root of the function, or null if no root is found + * @throws Error if f(a) and f(b) do not have opposite signs + * @throws Error if f is not a function + * @see https://en.wikipedia.org/wiki/Bisection_method + * @example bisection(x => x * x - 2, 1, 2) returns Math.sqrt(2) + * @example bisection(x => x * x * x - 4, 1, 2) returns Math.cbrt(4) + */ +export function bisection( + f: (x: number) => number, + a: number, + b: number, + tol = 1e-7, + maxIter = 100 +): number | null { + + if (typeof f !== 'function') { + throw new Error("The first argument must be a function.") + } + + const fa = f(a) + const fb = f(b) + + if (fa * fb >= 0) { + throw new Error("The function must have opposite signs at the endpoints a and b.") + } + + let iter = 0 + let left = a + let right = b + + while (iter < maxIter) { + const mid = (left + right) / 2 + const fMid = f(mid) + + if (Math.abs(fMid) < tol) { + return mid + } + + if (Math.abs(right - left) < tol) { + return mid + } + + if (fa * fMid < 0) { + right = mid + } else { + left = mid + } + + iter++ + } + + return null +} diff --git a/maths/test/bisection.test.ts b/maths/test/bisection.test.ts new file mode 100644 index 00000000..bf25e02e --- /dev/null +++ b/maths/test/bisection.test.ts @@ -0,0 +1,32 @@ +import { bisection } from '../bisection'; + +const f = (x: number): number => x * x - 2; +const g = (x: number): number => x * x * x - 4; + +describe('Bisection Method with multiple functions', () => { + + test.each([ + [1, 2, Math.sqrt(2)], + [0, 2, Math.sqrt(2)], + ])('should find the root of f(x) = x^2 - 2 in range (%i, %i)', (a, b, expected) => { + const root = bisection(f, a, b, 1e-7, 100); + expect(root).toBeCloseTo(expected, 6); + }); + + + test.each([ + [1, 2, Math.cbrt(4)], + [1, 3, Math.cbrt(4)], + ])('should find the root of g(x) = x^3 - 4 in range (%i, %i)', (a, b, expected) => { + const root = bisection(g, a, b, 1e-7, 100); + expect(root).toBeCloseTo(expected, 6); + }); + + + test('should throw error for invalid range', () => { + expect(() => bisection(f, -1, 1, 1e-7, 100)).toThrow( + 'The function must have opposite signs at the endpoints a and b.' + ); + }); + +}); \ No newline at end of file