From 5958ffaca225e2874b61d058ca2234b3f79dcc6d Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Mon, 16 Oct 2023 00:33:10 +0530 Subject: [PATCH 01/13] Separate common solution --- project_euler/problem_070/sol1.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/project_euler/problem_070/sol1.py b/project_euler/problem_070/sol1.py index f1114a280a31..5bc424e244e8 100644 --- a/project_euler/problem_070/sol1.py +++ b/project_euler/problem_070/sol1.py @@ -81,10 +81,23 @@ def solution(max_n: int = 10000000) -> int: >>> solution(10000) 4435 """ + totients = get_totients(max_n + 1) + + return common_solution(totients, max_n) + + +def common_solution(totients: list[int], max_n: int = 10000000) -> int: + """ + Finds the value of n from 1 to max such that n/φ(n) produces a minimum. + >>> common_solution(get_totients(101), 100) + 21 + + >>> common_solution(get_totients(10001), 10000) + 4435 + """ min_numerator = 1 # i min_denominator = 0 # φ(i) - totients = get_totients(max_n + 1) for i in range(2, max_n + 1): t = totients[i] From d6b16c6e1ccefba85a1393cbf0e378a4ffbb0519 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Mon, 16 Oct 2023 00:35:08 +0530 Subject: [PATCH 02/13] Add slicing_get_totients --- project_euler/problem_070/sol1.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/project_euler/problem_070/sol1.py b/project_euler/problem_070/sol1.py index 5bc424e244e8..823c38cac772 100644 --- a/project_euler/problem_070/sol1.py +++ b/project_euler/problem_070/sol1.py @@ -33,15 +33,15 @@ import numpy as np -def get_totients(max_one: int) -> list[int]: +def slow_get_totients(max_one: int) -> list[int]: """ Calculates a list of totients from 0 to max_one exclusive, using the definition of Euler's product formula. - >>> get_totients(5) + >>> slow_get_totients(5) [0, 1, 1, 2, 2] - >>> get_totients(10) + >>> slow_get_totients(10) [0, 1, 1, 2, 2, 4, 2, 6, 4, 6] """ totients = np.arange(max_one) @@ -54,6 +54,26 @@ def get_totients(max_one: int) -> list[int]: return totients.tolist() +def slicing_get_totients(max_one: int): + """ + Calculates a list of totients from 0 to max_one exclusive, using the + definition of Euler's product formula. + + >>> slicing_get_totients(5) + [0, 1, 1, 2, 2] + + >>> slicing_get_totients(10) + [0, 1, 1, 2, 2, 4, 2, 6, 4, 6] + """ + totients = np.arange(max_one) + + for i in range(2, max_one): + if totients[i] == i: + totients[i::i] -= totients[i::i] // i + + return totients.tolist() + + def has_same_digits(num1: int, num2: int) -> bool: """ Return True if num1 and num2 have the same frequency of every digit, False From fa8f7f9700ee963a828d67dde7de83531c10def9 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Mon, 16 Oct 2023 00:36:16 +0530 Subject: [PATCH 03/13] Add fast get_totients and calculate_prime_numbers --- project_euler/problem_070/sol1.py | 50 +++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/project_euler/problem_070/sol1.py b/project_euler/problem_070/sol1.py index 823c38cac772..fe5cdb980e70 100644 --- a/project_euler/problem_070/sol1.py +++ b/project_euler/problem_070/sol1.py @@ -30,9 +30,39 @@ """ from __future__ import annotations +from math import isqrt + import numpy as np +def calculate_prime_numbers(max_number: int) -> list[int]: + """ + Returns prime numbers below max_number. + See: https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes + + >>> calculate_prime_numbers(10) + [2, 3, 5, 7] + >>> calculate_prime_numbers(2) + [] + """ + if max_number <= 2: + return [] + + # List containing a bool value for every odd number below max_number/2 + is_prime = [True] * (max_number // 2) + + for i in range(3, isqrt(max_number - 1) + 1, 2): + if is_prime[i // 2]: + # Mark all multiple of i as not prime using list slicing + is_prime[i**2 // 2 :: i] = [False] * ( + # Same as: (max_number - (i**2)) // (2 * i) + 1 + # but faster than len(is_prime[i**2 // 2 :: i]) + len(range(i**2 // 2, max_number // 2, i)) + ) + + return [2] + [2 * i + 1 for i in range(1, max_number // 2) if is_prime[i]] + + def slow_get_totients(max_one: int) -> list[int]: """ Calculates a list of totients from 0 to max_one exclusive, using the @@ -74,6 +104,26 @@ def slicing_get_totients(max_one: int): return totients.tolist() +def get_totients(limit): + """ + Calculates a list of totients from 0 to max_one exclusive, using the + definition of Euler's product formula. + + >>> get_totients(5) + [0, 1, 1, 2, 2] + + >>> get_totients(10) + [0, 1, 1, 2, 2, 4, 2, 6, 4, 6] + """ + totients = np.arange(limit) + primes = calculate_prime_numbers(limit) + + for i in primes: + totients[i::i] -= totients[i::i] // i + + return totients.tolist() + + def has_same_digits(num1: int, num2: int) -> bool: """ Return True if num1 and num2 have the same frequency of every digit, False From 9bf26552a0b706165c4f52b024f180c293740883 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Mon, 16 Oct 2023 00:39:04 +0530 Subject: [PATCH 04/13] Add slow_solution and slicing_solution --- project_euler/problem_070/sol1.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/project_euler/problem_070/sol1.py b/project_euler/problem_070/sol1.py index fe5cdb980e70..9c74df8e448e 100644 --- a/project_euler/problem_070/sol1.py +++ b/project_euler/problem_070/sol1.py @@ -141,6 +141,36 @@ def has_same_digits(num1: int, num2: int) -> bool: return sorted(str(num1)) == sorted(str(num2)) +def slow_solution(max_n: int = 10000000) -> int: + """ + Finds the value of n from 1 to max such that n/φ(n) produces a minimum. + + >>> slow_solution(100) + 21 + + >>> slow_solution(10000) + 4435 + """ + totients = slow_get_totients(max_n + 1) + + return common_solution(totients, max_n) + + +def slicing_solution(max_n: int = 10000000) -> int: + """ + Finds the value of n from 1 to max such that n/φ(n) produces a minimum. + + >>> slicing_solution(100) + 21 + + >>> slicing_solution(10000) + 4435 + """ + totients = slicing_get_totients(max_n + 1) + + return common_solution(totients, max_n) + + def solution(max_n: int = 10000000) -> int: """ Finds the value of n from 1 to max such that n/φ(n) produces a minimum. From 8113df97bcefc832c7c5fcabe3e7c9ed8978ee56 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Mon, 16 Oct 2023 00:59:16 +0530 Subject: [PATCH 05/13] Add performance benchmark --- project_euler/problem_070/sol1.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/project_euler/problem_070/sol1.py b/project_euler/problem_070/sol1.py index 9c74df8e448e..729298085533 100644 --- a/project_euler/problem_070/sol1.py +++ b/project_euler/problem_070/sol1.py @@ -209,5 +209,26 @@ def common_solution(totients: list[int], max_n: int = 10000000) -> int: return min_numerator +def benchmark() -> None: + """ + Benchmark + """ + # Running performance benchmarks... + # Solution : 56.19136740000067 + # Slicing Sol : 70.83823779999875 + # Slow Sol : 118.29514729999937 + + from timeit import timeit + + print("Running performance benchmarks...") + + print(f"Solution : {timeit('solution()', globals=globals(), number=10)}") + print(f"Slicing Sol : {timeit('slicing_solution()', globals=globals(), number=10)}") + print(f"Slow Sol : {timeit('slow_solution()', globals=globals(), number=10)}") + + if __name__ == "__main__": - print(f"{solution() = }") + print(f"Solution : {solution()}") + print(f"Slicing Sol : {slicing_solution()}") + print(f"Slow Sol : {slow_solution()}") + benchmark() From 4f4218221d77699dd0777e81f0c955191a4bbdb3 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Mon, 16 Oct 2023 01:24:47 +0530 Subject: [PATCH 06/13] Fix broken wikipedia link --- project_euler/problem_070/sol1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project_euler/problem_070/sol1.py b/project_euler/problem_070/sol1.py index 729298085533..743320e0812b 100644 --- a/project_euler/problem_070/sol1.py +++ b/project_euler/problem_070/sol1.py @@ -26,7 +26,7 @@ References: Finding totients -https://en.wikipedia.org/wiki/Euler's_totient_function#Euler's_product_formula +https://en.wikipedia.org/wiki/Euler%27s_totient_function#Euler%27s_product_formula """ from __future__ import annotations From 3949c1b94c78e1a51031121c17e092a48383d684 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Mon, 16 Oct 2023 11:52:25 +0530 Subject: [PATCH 07/13] Add missing type hints --- project_euler/problem_070/sol1.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project_euler/problem_070/sol1.py b/project_euler/problem_070/sol1.py index 743320e0812b..6ff46bd7ea27 100644 --- a/project_euler/problem_070/sol1.py +++ b/project_euler/problem_070/sol1.py @@ -84,7 +84,7 @@ def slow_get_totients(max_one: int) -> list[int]: return totients.tolist() -def slicing_get_totients(max_one: int): +def slicing_get_totients(max_one: int) -> list[int]: """ Calculates a list of totients from 0 to max_one exclusive, using the definition of Euler's product formula. @@ -104,7 +104,7 @@ def slicing_get_totients(max_one: int): return totients.tolist() -def get_totients(limit): +def get_totients(limit) -> list[int]: """ Calculates a list of totients from 0 to max_one exclusive, using the definition of Euler's product formula. From bf95078ee2c887ee87a78b858e662ac2fd7ecb3c Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Mon, 16 Oct 2023 12:13:11 +0530 Subject: [PATCH 08/13] Add numpy calculate_prime_numbers --- project_euler/problem_070/sol1.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/project_euler/problem_070/sol1.py b/project_euler/problem_070/sol1.py index 6ff46bd7ea27..934e5031c91c 100644 --- a/project_euler/problem_070/sol1.py +++ b/project_euler/problem_070/sol1.py @@ -63,6 +63,32 @@ def calculate_prime_numbers(max_number: int) -> list[int]: return [2] + [2 * i + 1 for i in range(1, max_number // 2) if is_prime[i]] +def np_calculate_prime_numbers(max_number: int) -> list[int]: + """ + Returns prime numbers below max_number. + See: https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes + + >>> np_calculate_prime_numbers(10) + [2, 3, 5, 7] + >>> np_calculate_prime_numbers(2) + [] + """ + if max_number <= 2: + return [] + + # List containing a bool value for every odd number below max_number/2 + is_prime = np.ones(max_number // 2, dtype=bool) + + for i in range(3, isqrt(max_number - 1) + 1, 2): + if is_prime[i // 2]: + # Mark all multiple of i as not prime using list slicing + is_prime[i**2 // 2 :: i] = False + + primes = np.where(is_prime)[0] * 2 + 1 + primes[0] = 2 + return primes.tolist() + + def slow_get_totients(max_one: int) -> list[int]: """ Calculates a list of totients from 0 to max_one exclusive, using the From 9c9ad262d06b4b59ed23b7919b666bb69ac4b2ad Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Mon, 16 Oct 2023 12:15:52 +0530 Subject: [PATCH 09/13] Add solution using np_calculate_prime_numbers --- project_euler/problem_070/sol1.py | 37 ++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/project_euler/problem_070/sol1.py b/project_euler/problem_070/sol1.py index 934e5031c91c..74e4edba61b0 100644 --- a/project_euler/problem_070/sol1.py +++ b/project_euler/problem_070/sol1.py @@ -150,6 +150,26 @@ def get_totients(limit) -> list[int]: return totients.tolist() +def np_get_totients(limit) -> list[int]: + """ + Calculates a list of totients from 0 to max_one exclusive, using the + definition of Euler's product formula. + + >>> np_get_totients(5) + [0, 1, 1, 2, 2] + + >>> np_get_totients(10) + [0, 1, 1, 2, 2, 4, 2, 6, 4, 6] + """ + totients = np.arange(limit) + primes = np_calculate_prime_numbers(limit) + + for i in primes: + totients[i::i] -= totients[i::i] // i + + return totients.tolist() + + def has_same_digits(num1: int, num2: int) -> bool: """ Return True if num1 and num2 have the same frequency of every digit, False @@ -197,6 +217,21 @@ def slicing_solution(max_n: int = 10000000) -> int: return common_solution(totients, max_n) +def py_solution(max_n: int = 10000000) -> int: + """ + Finds the value of n from 1 to max such that n/φ(n) produces a minimum. + + >>> py_solution(100) + 21 + + >>> py_solution(10000) + 4435 + """ + totients = get_totients(max_n + 1) + + return common_solution(totients, max_n) + + def solution(max_n: int = 10000000) -> int: """ Finds the value of n from 1 to max such that n/φ(n) produces a minimum. @@ -207,7 +242,7 @@ def solution(max_n: int = 10000000) -> int: >>> solution(10000) 4435 """ - totients = get_totients(max_n + 1) + totients = np_get_totients(max_n + 1) return common_solution(totients, max_n) From 89cc321465a917ad05723d1eb1122578405d099b Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Mon, 16 Oct 2023 12:16:42 +0530 Subject: [PATCH 10/13] Update benchmarks --- project_euler/problem_070/sol1.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/project_euler/problem_070/sol1.py b/project_euler/problem_070/sol1.py index 74e4edba61b0..56a3fd9bf2a6 100644 --- a/project_euler/problem_070/sol1.py +++ b/project_euler/problem_070/sol1.py @@ -275,7 +275,8 @@ def benchmark() -> None: Benchmark """ # Running performance benchmarks... - # Solution : 56.19136740000067 + # Solution : 49.389978999999585 + # Py Solution : 56.19136740000067 # Slicing Sol : 70.83823779999875 # Slow Sol : 118.29514729999937 @@ -284,12 +285,14 @@ def benchmark() -> None: print("Running performance benchmarks...") print(f"Solution : {timeit('solution()', globals=globals(), number=10)}") + print(f"Py Solution : {timeit('py_solution()', globals=globals(), number=10)}") print(f"Slicing Sol : {timeit('slicing_solution()', globals=globals(), number=10)}") print(f"Slow Sol : {timeit('slow_solution()', globals=globals(), number=10)}") if __name__ == "__main__": print(f"Solution : {solution()}") + print(f"Py Solution : {py_solution()}") print(f"Slicing Sol : {slicing_solution()}") print(f"Slow Sol : {slow_solution()}") benchmark() From c2d35e5f0054fe7e3050c5c0c8c100af4b6ad230 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Fri, 29 Dec 2023 17:17:19 +0530 Subject: [PATCH 11/13] Remove py_solution and slicing_solution --- project_euler/problem_070/sol1.py | 102 ------------------------------ 1 file changed, 102 deletions(-) diff --git a/project_euler/problem_070/sol1.py b/project_euler/problem_070/sol1.py index 56a3fd9bf2a6..558dcfad3a0d 100644 --- a/project_euler/problem_070/sol1.py +++ b/project_euler/problem_070/sol1.py @@ -35,34 +35,6 @@ import numpy as np -def calculate_prime_numbers(max_number: int) -> list[int]: - """ - Returns prime numbers below max_number. - See: https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes - - >>> calculate_prime_numbers(10) - [2, 3, 5, 7] - >>> calculate_prime_numbers(2) - [] - """ - if max_number <= 2: - return [] - - # List containing a bool value for every odd number below max_number/2 - is_prime = [True] * (max_number // 2) - - for i in range(3, isqrt(max_number - 1) + 1, 2): - if is_prime[i // 2]: - # Mark all multiple of i as not prime using list slicing - is_prime[i**2 // 2 :: i] = [False] * ( - # Same as: (max_number - (i**2)) // (2 * i) + 1 - # but faster than len(is_prime[i**2 // 2 :: i]) - len(range(i**2 // 2, max_number // 2, i)) - ) - - return [2] + [2 * i + 1 for i in range(1, max_number // 2) if is_prime[i]] - - def np_calculate_prime_numbers(max_number: int) -> list[int]: """ Returns prime numbers below max_number. @@ -110,46 +82,6 @@ def slow_get_totients(max_one: int) -> list[int]: return totients.tolist() -def slicing_get_totients(max_one: int) -> list[int]: - """ - Calculates a list of totients from 0 to max_one exclusive, using the - definition of Euler's product formula. - - >>> slicing_get_totients(5) - [0, 1, 1, 2, 2] - - >>> slicing_get_totients(10) - [0, 1, 1, 2, 2, 4, 2, 6, 4, 6] - """ - totients = np.arange(max_one) - - for i in range(2, max_one): - if totients[i] == i: - totients[i::i] -= totients[i::i] // i - - return totients.tolist() - - -def get_totients(limit) -> list[int]: - """ - Calculates a list of totients from 0 to max_one exclusive, using the - definition of Euler's product formula. - - >>> get_totients(5) - [0, 1, 1, 2, 2] - - >>> get_totients(10) - [0, 1, 1, 2, 2, 4, 2, 6, 4, 6] - """ - totients = np.arange(limit) - primes = calculate_prime_numbers(limit) - - for i in primes: - totients[i::i] -= totients[i::i] // i - - return totients.tolist() - - def np_get_totients(limit) -> list[int]: """ Calculates a list of totients from 0 to max_one exclusive, using the @@ -202,36 +134,6 @@ def slow_solution(max_n: int = 10000000) -> int: return common_solution(totients, max_n) -def slicing_solution(max_n: int = 10000000) -> int: - """ - Finds the value of n from 1 to max such that n/φ(n) produces a minimum. - - >>> slicing_solution(100) - 21 - - >>> slicing_solution(10000) - 4435 - """ - totients = slicing_get_totients(max_n + 1) - - return common_solution(totients, max_n) - - -def py_solution(max_n: int = 10000000) -> int: - """ - Finds the value of n from 1 to max such that n/φ(n) produces a minimum. - - >>> py_solution(100) - 21 - - >>> py_solution(10000) - 4435 - """ - totients = get_totients(max_n + 1) - - return common_solution(totients, max_n) - - def solution(max_n: int = 10000000) -> int: """ Finds the value of n from 1 to max such that n/φ(n) produces a minimum. @@ -285,14 +187,10 @@ def benchmark() -> None: print("Running performance benchmarks...") print(f"Solution : {timeit('solution()', globals=globals(), number=10)}") - print(f"Py Solution : {timeit('py_solution()', globals=globals(), number=10)}") - print(f"Slicing Sol : {timeit('slicing_solution()', globals=globals(), number=10)}") print(f"Slow Sol : {timeit('slow_solution()', globals=globals(), number=10)}") if __name__ == "__main__": print(f"Solution : {solution()}") - print(f"Py Solution : {py_solution()}") - print(f"Slicing Sol : {slicing_solution()}") print(f"Slow Sol : {slow_solution()}") benchmark() From 6ff00c74f626e2d5ec3c2aa67e7d08ce29024d54 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Fri, 29 Dec 2023 17:19:04 +0530 Subject: [PATCH 12/13] Remove slow_solution and benchmark --- project_euler/problem_070/sol1.py | 69 ------------------------------- 1 file changed, 69 deletions(-) diff --git a/project_euler/problem_070/sol1.py b/project_euler/problem_070/sol1.py index 558dcfad3a0d..86ad7602e319 100644 --- a/project_euler/problem_070/sol1.py +++ b/project_euler/problem_070/sol1.py @@ -61,27 +61,6 @@ def np_calculate_prime_numbers(max_number: int) -> list[int]: return primes.tolist() -def slow_get_totients(max_one: int) -> list[int]: - """ - Calculates a list of totients from 0 to max_one exclusive, using the - definition of Euler's product formula. - - >>> slow_get_totients(5) - [0, 1, 1, 2, 2] - - >>> slow_get_totients(10) - [0, 1, 1, 2, 2, 4, 2, 6, 4, 6] - """ - totients = np.arange(max_one) - - for i in range(2, max_one): - if totients[i] == i: - x = np.arange(i, max_one, i) # array of indexes to select - totients[x] -= totients[x] // i - - return totients.tolist() - - def np_get_totients(limit) -> list[int]: """ Calculates a list of totients from 0 to max_one exclusive, using the @@ -119,21 +98,6 @@ def has_same_digits(num1: int, num2: int) -> bool: return sorted(str(num1)) == sorted(str(num2)) -def slow_solution(max_n: int = 10000000) -> int: - """ - Finds the value of n from 1 to max such that n/φ(n) produces a minimum. - - >>> slow_solution(100) - 21 - - >>> slow_solution(10000) - 4435 - """ - totients = slow_get_totients(max_n + 1) - - return common_solution(totients, max_n) - - def solution(max_n: int = 10000000) -> int: """ Finds the value of n from 1 to max such that n/φ(n) produces a minimum. @@ -146,19 +110,6 @@ def solution(max_n: int = 10000000) -> int: """ totients = np_get_totients(max_n + 1) - return common_solution(totients, max_n) - - -def common_solution(totients: list[int], max_n: int = 10000000) -> int: - """ - Finds the value of n from 1 to max such that n/φ(n) produces a minimum. - - >>> common_solution(get_totients(101), 100) - 21 - - >>> common_solution(get_totients(10001), 10000) - 4435 - """ min_numerator = 1 # i min_denominator = 0 # φ(i) @@ -172,25 +123,5 @@ def common_solution(totients: list[int], max_n: int = 10000000) -> int: return min_numerator -def benchmark() -> None: - """ - Benchmark - """ - # Running performance benchmarks... - # Solution : 49.389978999999585 - # Py Solution : 56.19136740000067 - # Slicing Sol : 70.83823779999875 - # Slow Sol : 118.29514729999937 - - from timeit import timeit - - print("Running performance benchmarks...") - - print(f"Solution : {timeit('solution()', globals=globals(), number=10)}") - print(f"Slow Sol : {timeit('slow_solution()', globals=globals(), number=10)}") - - if __name__ == "__main__": print(f"Solution : {solution()}") - print(f"Slow Sol : {slow_solution()}") - benchmark() From 480cf3ec546101f0f6f2db1657a86e17084a258f Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Fri, 29 Dec 2023 17:44:34 +0530 Subject: [PATCH 13/13] Move prime generator to maths/prime_sieve_eratosthenes.py --- maths/prime_sieve_eratosthenes.py | 32 ++++++++++++++++++++++++++++++- project_euler/problem_070/sol1.py | 30 ++--------------------------- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/maths/prime_sieve_eratosthenes.py b/maths/prime_sieve_eratosthenes.py index 32eef9165bba..40a8b9a13955 100644 --- a/maths/prime_sieve_eratosthenes.py +++ b/maths/prime_sieve_eratosthenes.py @@ -11,6 +11,10 @@ https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes """ +from math import isqrt + +import numpy as np + def prime_sieve_eratosthenes(num: int) -> list[int]: """ @@ -45,10 +49,36 @@ def prime_sieve_eratosthenes(num: int) -> list[int]: return [prime for prime in range(2, num + 1) if primes[prime]] +def np_prime_sieve_eratosthenes(max_number: int) -> list[int]: + """ + Returns prime numbers below max_number. + See: https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes + + >>> np_prime_sieve_eratosthenes(10) + [2, 3, 5, 7] + >>> np_prime_sieve_eratosthenes(2) + [] + """ + if max_number <= 2: + return [] + + # List containing a bool value for every odd number below max_number/2 + is_prime = np.ones(max_number // 2, dtype=bool) + + for i in range(3, isqrt(max_number - 1) + 1, 2): + if is_prime[i // 2]: + # Mark all multiple of i as not prime using list slicing + is_prime[i**2 // 2 :: i] = False + + primes = np.where(is_prime)[0] * 2 + 1 + primes[0] = 2 + return primes.tolist() + + if __name__ == "__main__": import doctest doctest.testmod() user_num = int(input("Enter a positive integer: ").strip()) - print(prime_sieve_eratosthenes(user_num)) + print(np_prime_sieve_eratosthenes(user_num)) diff --git a/project_euler/problem_070/sol1.py b/project_euler/problem_070/sol1.py index 86ad7602e319..649b30a80693 100644 --- a/project_euler/problem_070/sol1.py +++ b/project_euler/problem_070/sol1.py @@ -30,35 +30,9 @@ """ from __future__ import annotations -from math import isqrt - import numpy as np - -def np_calculate_prime_numbers(max_number: int) -> list[int]: - """ - Returns prime numbers below max_number. - See: https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes - - >>> np_calculate_prime_numbers(10) - [2, 3, 5, 7] - >>> np_calculate_prime_numbers(2) - [] - """ - if max_number <= 2: - return [] - - # List containing a bool value for every odd number below max_number/2 - is_prime = np.ones(max_number // 2, dtype=bool) - - for i in range(3, isqrt(max_number - 1) + 1, 2): - if is_prime[i // 2]: - # Mark all multiple of i as not prime using list slicing - is_prime[i**2 // 2 :: i] = False - - primes = np.where(is_prime)[0] * 2 + 1 - primes[0] = 2 - return primes.tolist() +from maths.prime_sieve_eratosthenes import np_prime_sieve_eratosthenes def np_get_totients(limit) -> list[int]: @@ -73,7 +47,7 @@ def np_get_totients(limit) -> list[int]: [0, 1, 1, 2, 2, 4, 2, 6, 4, 6] """ totients = np.arange(limit) - primes = np_calculate_prime_numbers(limit) + primes = np_prime_sieve_eratosthenes(limit) for i in primes: totients[i::i] -= totients[i::i] // i