From bbfabe1e84503fb914397a7b079d42ae2ab21298 Mon Sep 17 00:00:00 2001 From: Ryan Wu Date: Sat, 21 Feb 2026 05:30:37 -0500 Subject: [PATCH 1/7] Add problem 108 of project euler --- project_euler/problem_108/__init__.py | 0 project_euler/problem_108/sol1.py | 93 +++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 project_euler/problem_108/__init__.py create mode 100644 project_euler/problem_108/sol1.py diff --git a/project_euler/problem_108/__init__.py b/project_euler/problem_108/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_108/sol1.py b/project_euler/problem_108/sol1.py new file mode 100644 index 000000000000..3d3404fa989f --- /dev/null +++ b/project_euler/problem_108/sol1.py @@ -0,0 +1,93 @@ +""" +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(n : int) -> list[int]: + """ + Returns a list of all primes less than or equal to n + >>> find_primes(19) + [2, 3, 5, 7, 11, 13, 17, 19] + """ + sieve = [True] * (n + 1) + + for i in range(2, n + 1): + for j in range(2 * i, n + 1, i): + sieve[j] = False + return [i for i in range(2, n + 1) if sieve[i]] + + +def find_prime_factorizations(n : 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(n) + prime_factorizations = [dict() for _ in range(n + 1)] + + for p in primes: + for j in range(p, n + 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) + + def num_solutions(n): + return (num_divisors_of_square(prime_factorizations[n]) // 2) + 1 + + for i in range(2, upper_bound + 1): + if num_solutions(i) > target: + return i + +if __name__ == "__main__": + print(f"{solution() = }") From c89ecc86ef68b4e301ef9671537d1faab3c5fbb0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 21 Feb 2026 10:38:22 +0000 Subject: [PATCH 2/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- project_euler/problem_108/sol1.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/project_euler/problem_108/sol1.py b/project_euler/problem_108/sol1.py index 3d3404fa989f..6ab2222a3fa3 100644 --- a/project_euler/problem_108/sol1.py +++ b/project_euler/problem_108/sol1.py @@ -25,7 +25,7 @@ """ -def find_primes(n : int) -> list[int]: +def find_primes(n: int) -> list[int]: """ Returns a list of all primes less than or equal to n >>> find_primes(19) @@ -39,7 +39,7 @@ def find_primes(n : int) -> list[int]: return [i for i in range(2, n + 1) if sieve[i]] -def find_prime_factorizations(n : int) -> list[dict[int, int]]: +def find_prime_factorizations(n: 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 @@ -59,7 +59,7 @@ def find_prime_factorizations(n : int) -> list[dict[int, int]]: return prime_factorizations -def num_divisors_of_square(prime_factorization : dict[int, int]) -> int: +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 @@ -72,7 +72,7 @@ def num_divisors_of_square(prime_factorization : dict[int, int]) -> int: return num_divisors -def solution(target : int = 1000) -> int: +def solution(target: int = 1000) -> int: """ Returns the smallest n with more than 'target' solutions >>> solution() @@ -89,5 +89,6 @@ def num_solutions(n): if num_solutions(i) > target: return i + if __name__ == "__main__": print(f"{solution() = }") From 7c648a6c7bc86c962e171f9da32310ebe3de0b80 Mon Sep 17 00:00:00 2001 From: Ryan Wu Date: Sat, 21 Feb 2026 05:40:32 -0500 Subject: [PATCH 3/7] fix ruff check --- project_euler/problem_108/sol1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project_euler/problem_108/sol1.py b/project_euler/problem_108/sol1.py index 6ab2222a3fa3..401f4b0b9bed 100644 --- a/project_euler/problem_108/sol1.py +++ b/project_euler/problem_108/sol1.py @@ -47,7 +47,7 @@ def find_prime_factorizations(n: int) -> list[dict[int, int]]: [{}, {}, {2: 1}, {3: 1}, {2: 2}, {5: 1}, {2: 1, 3: 1}, {7: 1}] """ primes = find_primes(n) - prime_factorizations = [dict() for _ in range(n + 1)] + prime_factorizations = [{} for _ in range(n + 1)] for p in primes: for j in range(p, n + 1, p): From 2226db43a416d4010c8d38782be57991d1e543b1 Mon Sep 17 00:00:00 2001 From: Ryan Wu Date: Sat, 21 Feb 2026 05:51:27 -0500 Subject: [PATCH 4/7] follow conventions --- project_euler/problem_108/sol1.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/project_euler/problem_108/sol1.py b/project_euler/problem_108/sol1.py index 401f4b0b9bed..b748ed98ff83 100644 --- a/project_euler/problem_108/sol1.py +++ b/project_euler/problem_108/sol1.py @@ -25,32 +25,32 @@ """ -def find_primes(n: int) -> list[int]: +def find_primes(limit: int) -> list[int]: """ - Returns a list of all primes less than or equal to n + 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] * (n + 1) + sieve = [True] * (limit + 1) - for i in range(2, n + 1): - for j in range(2 * i, n + 1, i): + 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, n + 1) if sieve[i]] + return [i for i in range(2, limit + 1) if sieve[i]] -def find_prime_factorizations(n: int) -> list[dict[int, int]]: +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(n) - prime_factorizations = [{} for _ in range(n + 1)] + primes = find_primes(limit) + prime_factorizations = [{} for _ in range(limit + 1)] for p in primes: - for j in range(p, n + 1, p): + for j in range(p, limit + 1, p): j_factorization = prime_factorizations[j] x = j while x % p == 0: @@ -82,11 +82,9 @@ def solution(target: int = 1000) -> int: upper_bound = 210 ** ((int((2 * target - 1) ** 0.25) + 1) // 2) prime_factorizations = find_prime_factorizations(upper_bound) - def num_solutions(n): - return (num_divisors_of_square(prime_factorizations[n]) // 2) + 1 - for i in range(2, upper_bound + 1): - if num_solutions(i) > target: + num_solutions = (num_divisors_of_square(prime_factorizations[i]) // 2) + 1 + if num_solutions > target: return i From b4703f7bd795f19f325879cb4f0e7293579ab9a4 Mon Sep 17 00:00:00 2001 From: Ryan Wu Date: Sat, 21 Feb 2026 06:07:58 -0500 Subject: [PATCH 5/7] fix ruffs --- project_euler/problem_108/sol1.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/project_euler/problem_108/sol1.py b/project_euler/problem_108/sol1.py index b748ed98ff83..a2ea4942e5c4 100644 --- a/project_euler/problem_108/sol1.py +++ b/project_euler/problem_108/sol1.py @@ -47,14 +47,14 @@ def find_prime_factorizations(limit: int) -> list[dict[int, int]]: [{}, {}, {2: 1}, {3: 1}, {2: 2}, {5: 1}, {2: 1, 3: 1}, {7: 1}] """ primes = find_primes(limit) - prime_factorizations = [{} for _ in range(limit + 1)] + 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 + x //= p j_factorization[p] = j_factorization.get(p, 0) + 1 return prime_factorizations @@ -86,6 +86,8 @@ def solution(target: int = 1000) -> int: num_solutions = (num_divisors_of_square(prime_factorizations[i]) // 2) + 1 if num_solutions > target: return i + + return -1 if __name__ == "__main__": From 09ffcc829d66007b83dcf27d7da8bab943f2b68d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 21 Feb 2026 11:08:30 +0000 Subject: [PATCH 6/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- project_euler/problem_108/sol1.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project_euler/problem_108/sol1.py b/project_euler/problem_108/sol1.py index a2ea4942e5c4..362b78a937e8 100644 --- a/project_euler/problem_108/sol1.py +++ b/project_euler/problem_108/sol1.py @@ -47,7 +47,7 @@ def find_prime_factorizations(limit: int) -> list[dict[int, int]]: [{}, {}, {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)] + prime_factorizations: list[dict[int, int]] = [{} for _ in range(limit + 1)] for p in primes: for j in range(p, limit + 1, p): @@ -86,7 +86,7 @@ def solution(target: int = 1000) -> int: num_solutions = (num_divisors_of_square(prime_factorizations[i]) // 2) + 1 if num_solutions > target: return i - + return -1 From b54df74c8561c18199cf7209be7cf9766deb088c Mon Sep 17 00:00:00 2001 From: Ryan Wu Date: Sat, 21 Feb 2026 14:53:14 -0500 Subject: [PATCH 7/7] implemented euler 204 --- project_euler/problem_204/__init__.py | 0 project_euler/problem_204/sol1.py | 65 +++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 project_euler/problem_204/__init__.py create mode 100644 project_euler/problem_204/sol1.py diff --git a/project_euler/problem_204/__init__.py b/project_euler/problem_204/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_204/sol1.py b/project_euler/problem_204/sol1.py new file mode 100644 index 000000000000..193c4a5afe65 --- /dev/null +++ b/project_euler/problem_204/sol1.py @@ -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() = }")