From e0ddce117099b22fc9fde13c3c7f95b2156324a2 Mon Sep 17 00:00:00 2001 From: Lumi Pakkanen Date: Wed, 28 Feb 2024 09:02:37 +0200 Subject: [PATCH] Replace fraction addition implementation with a 1% faster version --- package.json | 4 ++- src/__benchmarks__/fraction.mark.ts | 48 +++++++++++++++++++++++++++++ src/fraction.ts | 14 +++++---- 3 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 src/__benchmarks__/fraction.mark.ts diff --git a/package.json b/package.json index a0aaaec..27b8233 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,9 @@ "premonzo-benchmark": "tsc -p tsconfig-benchmark.json", "monzo-benchmark": "node benchmarks/__benchmarks__/monzo.mark.js", "preprimes-benchmark": "tsc -p tsconfig-benchmark.json", - "primes-benchmark": "node benchmarks/__benchmarks__/primes.mark.js" + "primes-benchmark": "node benchmarks/__benchmarks__/primes.mark.js", + "prefraction-benchmark": "tsc -p tsconfig-benchmark.json", + "fraction-benchmark": "node benchmarks/__benchmarks__/fraction.mark.js" }, "engines": { "node": ">=10.6.0" diff --git a/src/__benchmarks__/fraction.mark.ts b/src/__benchmarks__/fraction.mark.ts new file mode 100644 index 0000000..32f831f --- /dev/null +++ b/src/__benchmarks__/fraction.mark.ts @@ -0,0 +1,48 @@ +// @ts-ignore +import Benchmark = require('benchmark'); +import {Fraction, gcd, lcm} from '..'; + +function addWithLcm(self: Fraction, other: Fraction) { + const {s, n, d} = new Fraction(other); + const denominator = lcm(self.d, d); + return new Fraction( + self.s * self.n * (denominator / self.d) + s * n * (denominator / d), + denominator + ); +} + +function addWithGcd(self: Fraction, other: Fraction) { + const {s, n, d} = new Fraction(other); + const factor = gcd(self.d, d); + const df = d / factor; + return new Fraction( + self.s * self.n * df + s * n * (self.d / factor), + df * self.d + ); +} + +function randomFraction() { + return new Fraction( + Math.floor(Math.random() * 10000), + Math.floor(Math.random() * 10000) + 1 + ); +} + +const fractionAddSuite = new Benchmark.Suite(); +fractionAddSuite + .add('current implementation', () => { + randomFraction().add(randomFraction()); + }) + .add('old implementation (using lcm)', () => { + addWithLcm(randomFraction(), randomFraction()); + }) + .add('new implementation (using gcd)', () => { + addWithGcd(randomFraction(), randomFraction()); + }) + .on('cycle', (event: {target: any}) => { + console.log(String(event.target)); + }) + .on('complete', () => { + console.log('Fastest is ' + fractionAddSuite.filter('fastest').map('name')); + }) + .run({async: true}); diff --git a/src/fraction.ts b/src/fraction.ts index c4c88b6..b07b07e 100644 --- a/src/fraction.ts +++ b/src/fraction.ts @@ -481,10 +481,11 @@ export class Fraction { add(other: FractionValue) { const {s, n, d} = new Fraction(other); // Must pre-reduce to avoid blowing the limits - const denominator = lcm(this.d, d); + const factor = gcd(this.d, d); + const df = d / factor; return new Fraction( - this.s * this.n * (denominator / this.d) + s * n * (denominator / d), - denominator + this.s * this.n * df + s * n * (this.d / factor), + df * this.d ); } @@ -496,10 +497,11 @@ export class Fraction { sub(other: FractionValue) { const {s, n, d} = new Fraction(other); // Must pre-reduce to avoid blowing the limits - const denominator = lcm(this.d, d); + const factor = gcd(this.d, d); + const df = d / factor; return new Fraction( - this.s * this.n * (denominator / this.d) - s * n * (denominator / d), - denominator + this.s * this.n * df - s * n * (this.d / factor), + df * this.d ); }