222 lines
5.5 KiB
JavaScript
222 lines
5.5 KiB
JavaScript
// Egyptian Fractions implementation in JavaScript
|
|
// Using BigInt for arbitrary precision integers
|
|
|
|
function gcd(a, b) {
|
|
if (b === 0n) {
|
|
return a;
|
|
}
|
|
return gcd(b, a % b);
|
|
}
|
|
|
|
class Frac {
|
|
constructor(n, d) {
|
|
// Handle different input types (number or BigInt)
|
|
const num = typeof n === 'number' ? BigInt(n) : n;
|
|
const denom = typeof d === 'number' ? BigInt(d) : d;
|
|
|
|
if (denom === 0n) {
|
|
throw new Error("Parameter d may not be zero.");
|
|
}
|
|
|
|
let nn = num;
|
|
let dd = denom;
|
|
|
|
if (nn === 0n) {
|
|
dd = 1n;
|
|
} else if (dd < 0n) {
|
|
nn = -nn;
|
|
dd = -dd;
|
|
}
|
|
|
|
const g = gcd(nn < 0n ? -nn : nn, dd).valueOf();
|
|
|
|
if (g > 0n) {
|
|
nn = nn / g;
|
|
dd = dd / g;
|
|
}
|
|
|
|
this.num = nn;
|
|
this.denom = dd;
|
|
}
|
|
|
|
plus(rhs) {
|
|
return new Frac(
|
|
this.num * rhs.denom + this.denom * rhs.num,
|
|
this.denom * rhs.denom
|
|
);
|
|
}
|
|
|
|
unaryMinus() {
|
|
return new Frac(-this.num, this.denom);
|
|
}
|
|
|
|
minus(rhs) {
|
|
return this.plus(rhs.unaryMinus());
|
|
}
|
|
|
|
compareTo(rhs) {
|
|
// We'll compare by cross multiplication to avoid floating point issues
|
|
const leftSide = this.num * rhs.denom;
|
|
const rightSide = rhs.num * this.denom;
|
|
|
|
if (leftSide < rightSide) return -1;
|
|
if (leftSide > rightSide) return 1;
|
|
return 0;
|
|
}
|
|
|
|
equals(obj) {
|
|
if (!(obj instanceof Frac)) {
|
|
return false;
|
|
}
|
|
return this.compareTo(obj) === 0;
|
|
}
|
|
|
|
toString() {
|
|
if (this.denom === 1n) {
|
|
return this.num.toString();
|
|
}
|
|
return `${this.num}/${this.denom}`;
|
|
}
|
|
|
|
toNumber() {
|
|
// Convert fraction to a regular number (potentially with loss of precision)
|
|
return Number(this.num) / Number(this.denom);
|
|
}
|
|
|
|
toEgyptian() {
|
|
if (this.num === 0n) {
|
|
return [this];
|
|
}
|
|
|
|
const fracs = [];
|
|
|
|
if ((this.num < 0n ? -this.num : this.num) >= (this.denom < 0n ? -this.denom : this.denom)) {
|
|
// Handle improper fractions
|
|
const div = new Frac(this.num / this.denom, 1n);
|
|
const rem = this.minus(div);
|
|
fracs.push(div);
|
|
this._toEgyptian(rem.num, rem.denom, fracs);
|
|
} else {
|
|
this._toEgyptian(this.num, this.denom, fracs);
|
|
}
|
|
|
|
return fracs;
|
|
}
|
|
|
|
_toEgyptian(n, d, fracs) {
|
|
if (n === 0n) {
|
|
return;
|
|
}
|
|
|
|
// Calculate ceiling of d/n
|
|
let div = d / n;
|
|
if (d % n !== 0n) {
|
|
div = div + 1n;
|
|
}
|
|
|
|
fracs.push(new Frac(1n, div));
|
|
|
|
// Calculate next fraction's numerator and denominator
|
|
let n3 = -(d % n);
|
|
if (n3 < 0n) {
|
|
n3 = n3 + n;
|
|
}
|
|
|
|
const d3 = d * div;
|
|
const f = new Frac(n3, d3);
|
|
|
|
if (f.num === 1n) {
|
|
fracs.push(f);
|
|
return;
|
|
}
|
|
|
|
this._toEgyptian(f.num, f.denom, fracs);
|
|
}
|
|
}
|
|
|
|
// Main function
|
|
function main() {
|
|
const fracs = [
|
|
new Frac(43, 48),
|
|
new Frac(5, 121),
|
|
new Frac(2014, 59)
|
|
];
|
|
|
|
for (const frac of fracs) {
|
|
const list = frac.toEgyptian();
|
|
const first = list[0];
|
|
|
|
if (first.denom === 1n) {
|
|
process.stdout.write(`${frac} -> [${first}] + `);
|
|
} else {
|
|
process.stdout.write(`${frac} -> ${first}`);
|
|
}
|
|
|
|
for (let i = 1; i < list.length; ++i) {
|
|
process.stdout.write(` + ${list[i]}`);
|
|
}
|
|
|
|
console.log();
|
|
}
|
|
|
|
for (const r of [98, 998]) {
|
|
if (r === 98) {
|
|
console.log("\nFor proper fractions with 1 or 2 digits:");
|
|
} else {
|
|
console.log("\nFor proper fractions with 1, 2 or 3 digits:");
|
|
}
|
|
|
|
let maxSize = 0;
|
|
let maxSizeFracs = [];
|
|
let maxDen = 0n;
|
|
let maxDenFracs = [];
|
|
|
|
// Create sieve array
|
|
const sieve = Array(r + 1).fill().map(() => Array(r + 2).fill(false));
|
|
|
|
for (let i = 1; i < r; ++i) {
|
|
for (let j = i + 1; j < r + 1; ++j) {
|
|
if (sieve[i][j]) continue;
|
|
|
|
const f = new Frac(i, j);
|
|
const list = f.toEgyptian();
|
|
const listSize = list.length;
|
|
|
|
if (listSize > maxSize) {
|
|
maxSize = listSize;
|
|
maxSizeFracs = [f];
|
|
} else if (listSize === maxSize) {
|
|
maxSizeFracs.push(f);
|
|
}
|
|
|
|
const listDen = list[list.length - 1].denom;
|
|
|
|
if (listDen > maxDen) {
|
|
maxDen = listDen;
|
|
maxDenFracs = [f];
|
|
} else if (listDen === maxDen) {
|
|
maxDenFracs.push(f);
|
|
}
|
|
|
|
if (i < r / 2) {
|
|
let k = 2;
|
|
while (true) {
|
|
if (j * k > r + 1) break;
|
|
sieve[i * k][j * k] = true;
|
|
k++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log(` largest number of items = ${maxSize}`);
|
|
console.log(`fraction(s) with this number : ${maxSizeFracs}`);
|
|
|
|
const md = maxDen.toString();
|
|
console.log(` largest denominator = ${md.length} digits, ${md.substring(0, 20)}...${md.substring(md.length - 20)}`);
|
|
console.log(`fraction(s) with this denominator : ${maxDenFracs}`);
|
|
}
|
|
}
|
|
|
|
// Run the main function
|
|
main();
|