1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
| from typing import List
from math import gcd
from fractions import Fraction
class Solution:
def __init__(self):
# Cache for previously computed fractions: (a, b) -> list of denominators
self.cache = {}
def egyptian_fraction(self, a: int, b: int) -> List[int]:
"""
Alternative (heuristic) Egyptian fraction generator that uses a bounded
search over candidate first denominators and falls back to the greedy
algorithm when needed. Signature matches Method 1: egyptian_fraction(a, b).
Returns a list of denominators representing the unit fractions.
"""
if (a, b) in self.cache:
return self.cache[(a, b)]
# Reduce to lowest terms
g = gcd(a, b)
a //= g
b //= g
if a == 1:
self.cache[(a, b)] = [b]
return [b]
# Always compute the greedy fallback
greedy_result = self._greedy_approach(a, b)
# Bounded search parameters (tunable): limit candidates to small denominators
MAX_CAND = min(b, 20)
best_result = greedy_result
best_max_denom = max(greedy_result) if greedy_result else float('inf')
# Try a few small first-denominator candidates to improve the decomposition
for d in range(2, MAX_CAND):
unit_frac = Fraction(1, d)
frac = Fraction(a, b)
if unit_frac >= frac:
break
remainder = frac - unit_frac
# avoid exploding denominators: skip if remainder denominator becomes huge
if remainder.denominator > 10000:
continue
remainder_egyptian = self._greedy_approach(remainder.numerator, remainder.denominator)
candidate = [d] + remainder_egyptian
# Choose candidate with smaller maximal denominator (heuristic)
cand_max = max(candidate) if candidate else float('inf')
if cand_max < best_max_denom or (cand_max == best_max_denom and len(candidate) < len(best_result)):
best_result = candidate
best_max_denom = cand_max
self.cache[(a, b)] = best_result
return best_result
def _greedy_approach(self, a: int, b: int) -> List[int]:
"""Standard greedy algorithm for Egyptian fractions."""
result: List[int] = []
while a != 0:
unit_denom = (b + a - 1) // a
result.append(unit_denom)
a = a * unit_denom - b
b = b * unit_denom
if a != 0:
g = gcd(a, b)
a //= g
b //= g
return result
def _try_better_representation(self, a: int, b: int) -> List[int] | None:
"""
(Optional helper) -- kept for completeness but not used by default in the
bounded search above. Returns a candidate or None.
"""
best_result = None
min_max_denom = float('inf')
for i in range(2, min(b, 50)):
try:
frac = Fraction(a, b)
unit_frac = Fraction(1, i)
if frac > unit_frac:
remainder = frac - unit_frac
if remainder.denominator <= 1000:
remainder_egyptian = self._greedy_approach(remainder.numerator, remainder.denominator)
candidate = [i] + remainder_egyptian
max_denom = max(candidate)
if max_denom < min_max_denom:
best_result = candidate
min_max_denom = max_denom
except Exception:
continue
return best_result
def test_alternative_approach():
sol = Solution()
test_cases = [
(2, 3),
(4, 13),
(5, 6),
(7, 12),
(11, 12),
]
for a, b in test_cases:
greedy = sol._greedy_approach(a, b)
optimized = sol.egyptian_fraction(a, b)
print(f"{a}/{b}:")
print(f" Greedy: {' + '.join(f'1/{d}' for d in greedy)}")
print(f" Optimized: {' + '.join(f'1/{d}' for d in optimized)}")
print()
if __name__ == "__main__":
test_alternative_approach()
|