Skip to content

Commit bbfabe1

Browse files
committed
Add problem 108 of project euler
1 parent 678dedb commit bbfabe1

File tree

2 files changed

+93
-0
lines changed

2 files changed

+93
-0
lines changed

project_euler/problem_108/__init__.py

Whitespace-only changes.

project_euler/problem_108/sol1.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
"""
2+
Problem 108: https://projecteuler.net/problem=108
3+
4+
Problem Statement:
5+
6+
In the following equation x, y, and n are positive integers.
7+
1/x + 1/y = 1/n
8+
9+
For n = 4 there are exactly three distinct solutions:
10+
1/5 + 1/20 = 1/4
11+
1/6 + 1/12 = 1/4
12+
1/8 + 1/8 = 1/4
13+
14+
What is the least value of n for which the number of distinct solutions
15+
exceeds one-thousand?
16+
17+
18+
Solution:
19+
20+
For a given n, the number of distinct solutions is (d(n * n) // 2) + 1,
21+
where d is the divisor counting function. Find an arbitrary n with more
22+
than 1000 solutions, so n is an upper bound for the answer. Find
23+
prime factorizations for all i < n, allowing easy computation of d(i * i)
24+
for i <= n. Then try all i to find the smallest.
25+
"""
26+
27+
28+
def find_primes(n : int) -> list[int]:
29+
"""
30+
Returns a list of all primes less than or equal to n
31+
>>> find_primes(19)
32+
[2, 3, 5, 7, 11, 13, 17, 19]
33+
"""
34+
sieve = [True] * (n + 1)
35+
36+
for i in range(2, n + 1):
37+
for j in range(2 * i, n + 1, i):
38+
sieve[j] = False
39+
return [i for i in range(2, n + 1) if sieve[i]]
40+
41+
42+
def find_prime_factorizations(n : int) -> list[dict[int, int]]:
43+
"""
44+
Returns a list of prime factorizations of 2...n, with prime
45+
factorization represented as a dictionary of (prime, exponent) pairs
46+
>>> find_prime_factorizations(7)
47+
[{}, {}, {2: 1}, {3: 1}, {2: 2}, {5: 1}, {2: 1, 3: 1}, {7: 1}]
48+
"""
49+
primes = find_primes(n)
50+
prime_factorizations = [dict() for _ in range(n + 1)]
51+
52+
for p in primes:
53+
for j in range(p, n + 1, p):
54+
j_factorization = prime_factorizations[j]
55+
x = j
56+
while x % p == 0:
57+
x /= p
58+
j_factorization[p] = j_factorization.get(p, 0) + 1
59+
return prime_factorizations
60+
61+
62+
def num_divisors_of_square(prime_factorization : dict[int, int]) -> int:
63+
"""
64+
Returns the number of divisors of n * n, where n is the
65+
number represented by the input prime factorization
66+
>>> num_divisors_of_square({2: 2, 3: 2}) # n = 36
67+
25
68+
"""
69+
num_divisors = 1
70+
for _, e in prime_factorization.items():
71+
num_divisors *= 2 * e + 1
72+
return num_divisors
73+
74+
75+
def solution(target : int = 1000) -> int:
76+
"""
77+
Returns the smallest n with more than 'target' solutions
78+
>>> solution()
79+
180180
80+
"""
81+
82+
upper_bound = 210 ** ((int((2 * target - 1) ** 0.25) + 1) // 2)
83+
prime_factorizations = find_prime_factorizations(upper_bound)
84+
85+
def num_solutions(n):
86+
return (num_divisors_of_square(prime_factorizations[n]) // 2) + 1
87+
88+
for i in range(2, upper_bound + 1):
89+
if num_solutions(i) > target:
90+
return i
91+
92+
if __name__ == "__main__":
93+
print(f"{solution() = }")

0 commit comments

Comments
 (0)