Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
94 changes: 94 additions & 0 deletions project_euler/problem_108/sol1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"""
Problem 108: https://projecteuler.net/problem=108

Problem Statement:

In the following equation x, y, and n are positive integers.
1/x + 1/y = 1/n

For n = 4 there are exactly three distinct solutions:
1/5 + 1/20 = 1/4
1/6 + 1/12 = 1/4
1/8 + 1/8 = 1/4

What is the least value of n for which the number of distinct solutions
exceeds one-thousand?


Solution:

For a given n, the number of distinct solutions is (d(n * n) // 2) + 1,
where d is the divisor counting function. Find an arbitrary n with more
than 1000 solutions, so n is an upper bound for the answer. Find
prime factorizations for all i < n, allowing easy computation of d(i * i)
for i <= n. Then try all i to find the smallest.
"""


def find_primes(limit: int) -> list[int]:
"""
Returns a list of all primes less than or equal to 'limit'
>>> find_primes(19)
[2, 3, 5, 7, 11, 13, 17, 19]
"""
sieve = [True] * (limit + 1)

for i in range(2, limit + 1):
for j in range(2 * i, limit + 1, i):
sieve[j] = False
return [i for i in range(2, limit + 1) if sieve[i]]


def find_prime_factorizations(limit: int) -> list[dict[int, int]]:
"""
Returns a list of prime factorizations of 2...n, with prime
factorization represented as a dictionary of (prime, exponent) pairs
>>> find_prime_factorizations(7)
[{}, {}, {2: 1}, {3: 1}, {2: 2}, {5: 1}, {2: 1, 3: 1}, {7: 1}]
"""
primes = find_primes(limit)
prime_factorizations: list[dict[int, int]] = [{} for _ in range(limit + 1)]

for p in primes:
for j in range(p, limit + 1, p):
j_factorization = prime_factorizations[j]
x = j
while x % p == 0:
x //= p
j_factorization[p] = j_factorization.get(p, 0) + 1
return prime_factorizations


def num_divisors_of_square(prime_factorization: dict[int, int]) -> int:
"""
Returns the number of divisors of n * n, where n is the
number represented by the input prime factorization
>>> num_divisors_of_square({2: 2, 3: 2}) # n = 36
25
"""
num_divisors = 1
for _, e in prime_factorization.items():
num_divisors *= 2 * e + 1
return num_divisors


def solution(target: int = 1000) -> int:
"""
Returns the smallest n with more than 'target' solutions
>>> solution()
180180
"""

upper_bound = 210 ** ((int((2 * target - 1) ** 0.25) + 1) // 2)
prime_factorizations = find_prime_factorizations(upper_bound)

for i in range(2, upper_bound + 1):
num_solutions = (num_divisors_of_square(prime_factorizations[i]) // 2) + 1
if num_solutions > target:
return i

return -1


if __name__ == "__main__":
print(f"{solution() = }")
Empty file.
65 changes: 65 additions & 0 deletions project_euler/problem_204/sol1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"""
Problem 204: https://projecteuler.net/problem=204
Problem Statement:
A Hamming number is a positive number which has no prime factor larger than 5.
So the first few Hamming numbers are 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15.
There are 1105 Hamming numbers not exceeding 10^8.
We will call a positive number a generalised Hamming number of type n, if
it has no prime factor larger than n. Hence the Hamming numbers are the
generalised Hamming numbers of type 5.
How many generalised Hamming numbers of type 100 are there which don't exceed 10^9?
Solution:
Find all primes under 100, then find the number of integers that can
be constructed by multiplying some primes in the list repeatedly.
"""


def find_primes(limit: int) -> list[int]:
"""
Returns a list of all primes less than or equal to 'limit'
>>> find_primes(19)
[2, 3, 5, 7, 11, 13, 17, 19]
"""
sieve = [True] * (limit + 1)

for i in range(2, limit + 1):
for j in range(2 * i, limit + 1, i):
sieve[j] = False
return [i for i in range(2, limit + 1) if sieve[i]]


def num_hamming_numbers(val: int, primes: list[int], limit: int) -> int:
"""
Returns the number of n such that n <= limit and
n = val * p_1 * p_2... * p_k, where p_i are in 'primes'
>>> num_hamming_numbers(6, [3, 5], 90)
5
"""
if val > limit:
return 0

num = 1
for i in range(len(primes)):
num += num_hamming_numbers(val * primes[i], primes[i:], limit)
return num


def solution(hamming_type: int = 100, limit: int = 10**9) -> int:
"""
Returns the number of generalized Hamming numbers of type 'hamming_type'
not exceeding 'limit'
>>> solution(5, 10**8)
1105
"""
return num_hamming_numbers(1, find_primes(hamming_type), limit)


if __name__ == "__main__":
print(f"{solution() = }")
Loading