Skip to content

Commit fcf75eb

Browse files
feat: add Egyptian fraction algorithm
1 parent 1d88e87 commit fcf75eb

2 files changed

Lines changed: 90 additions & 0 deletions

File tree

‎Maths/EgyptianFraction.js‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* @function egyptianFraction
3+
* @description Represents a fraction as a sum of distinct unit fractions
4+
* (fractions with numerator 1) using the greedy algorithm.
5+
* For example, 2/3 = 1/2 + 1/6.
6+
* @param {number} numerator - The numerator of the fraction
7+
* @param {number} denominator - The denominator of the fraction
8+
* @returns {number[]} - Array of denominators of unit fractions
9+
* @see https://en.wikipedia.org/wiki/Egyptian_fraction
10+
* @example egyptianFraction(2, 3) => [2, 6]
11+
* @example egyptianFraction(3, 4) => [2, 4]
12+
*/
13+
const egyptianFraction = (numerator, denominator) => {
14+
if (
15+
!Number.isInteger(numerator) ||
16+
!Number.isInteger(denominator) ||
17+
numerator < 1 ||
18+
denominator < 1
19+
) {
20+
throw new TypeError('Both inputs must be positive integers')
21+
}
22+
if (numerator >= denominator) {
23+
throw new RangeError('Numerator must be less than denominator')
24+
}
25+
const result = []
26+
let num = numerator
27+
let den = denominator
28+
while (num > 0) {
29+
const unitDen = Math.ceil(den / num)
30+
result.push(unitDen)
31+
num = num * unitDen - den
32+
den = den * unitDen
33+
const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b))
34+
const g = gcd(num, den)
35+
num = num / g
36+
den = den / g
37+
}
38+
return result
39+
}
40+
41+
export { egyptianFraction }
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { egyptianFraction } from '../EgyptianFraction.js'
2+
3+
describe('egyptianFraction', () => {
4+
test('egyptianFraction(2, 3) returns [2, 6]', () => {
5+
expect(egyptianFraction(2, 3)).toEqual([2, 6])
6+
})
7+
8+
test('egyptianFraction(3, 4) returns [2, 4]', () => {
9+
expect(egyptianFraction(3, 4)).toEqual([2, 4])
10+
})
11+
12+
test('egyptianFraction(1, 2) returns [2]', () => {
13+
expect(egyptianFraction(1, 2)).toEqual([2])
14+
})
15+
16+
test('result unit fractions sum to original fraction', () => {
17+
const denoms = egyptianFraction(2, 3)
18+
const sum = denoms.reduce((acc, d) => acc + 1 / d, 0)
19+
expect(sum).toBeCloseTo(2 / 3, 10)
20+
})
21+
22+
test('result unit fractions sum for 3/4', () => {
23+
const denoms = egyptianFraction(3, 4)
24+
const sum = denoms.reduce((acc, d) => acc + 1 / d, 0)
25+
expect(sum).toBeCloseTo(3 / 4, 10)
26+
})
27+
28+
test('all denominators are distinct', () => {
29+
const denoms = egyptianFraction(2, 3)
30+
const unique = new Set(denoms)
31+
expect(unique.size).toBe(denoms.length)
32+
})
33+
34+
test('throws TypeError for zero numerator', () => {
35+
expect(() => egyptianFraction(0, 3)).toThrow(TypeError)
36+
})
37+
38+
test('throws TypeError for negative input', () => {
39+
expect(() => egyptianFraction(-2, 3)).toThrow(TypeError)
40+
})
41+
42+
test('throws RangeError when numerator >= denominator', () => {
43+
expect(() => egyptianFraction(3, 3)).toThrow(RangeError)
44+
})
45+
46+
test('throws TypeError for float input', () => {
47+
expect(() => egyptianFraction(2.5, 3)).toThrow(TypeError)
48+
})
49+
})

0 commit comments

Comments
 (0)