From db7a1340fc908534b1c34db9c179cc91bc35a18e Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Mon, 16 Oct 2023 12:35:12 +0530 Subject: [PATCH 1/9] Add numpy calculate_prime_numbers --- project_euler/problem_187/sol1.py | 38 +++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/project_euler/problem_187/sol1.py b/project_euler/problem_187/sol1.py index 8944776fef50..597d885e5caf 100644 --- a/project_euler/problem_187/sol1.py +++ b/project_euler/problem_187/sol1.py @@ -13,6 +13,8 @@ from math import isqrt +import numpy as np + def slow_calculate_prime_numbers(max_number: int) -> list[int]: """ @@ -38,15 +40,15 @@ def slow_calculate_prime_numbers(max_number: int) -> list[int]: return [i for i in range(2, max_number) if is_prime[i]] -def calculate_prime_numbers(max_number: int) -> list[int]: +def py_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) + >>> py_calculate_prime_numbers(10) [2, 3, 5, 7] - >>> calculate_prime_numbers(2) + >>> py_calculate_prime_numbers(2) [] """ @@ -68,6 +70,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_solution(max_number: int = 10**8) -> int: """ Returns the number of composite integers below max_number have precisely two, @@ -100,7 +128,7 @@ def while_solution(max_number: int = 10**8) -> int: 10 """ - prime_numbers = calculate_prime_numbers(max_number // 2) + prime_numbers = py_calculate_prime_numbers(max_number // 2) semiprimes_count = 0 left = 0 @@ -123,7 +151,7 @@ def solution(max_number: int = 10**8) -> int: 10 """ - prime_numbers = calculate_prime_numbers(max_number // 2) + prime_numbers = py_calculate_prime_numbers(max_number // 2) semiprimes_count = 0 right = len(prime_numbers) - 1 From 11aac8472b170f472904aaa1afe07ecbd82a2f08 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Mon, 16 Oct 2023 12:38:51 +0530 Subject: [PATCH 2/9] Rename old solution to for_solution --- project_euler/problem_187/sol1.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project_euler/problem_187/sol1.py b/project_euler/problem_187/sol1.py index 597d885e5caf..b277996f1f85 100644 --- a/project_euler/problem_187/sol1.py +++ b/project_euler/problem_187/sol1.py @@ -142,12 +142,12 @@ def while_solution(max_number: int = 10**8) -> int: return semiprimes_count -def solution(max_number: int = 10**8) -> int: +def for_solution(max_number: int = 10**8) -> int: """ Returns the number of composite integers below max_number have precisely two, not necessarily distinct, prime factors. - >>> solution(30) + >>> for_solution(30) 10 """ From 8943088af146783cd9ff07448abc99f5625afd18 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Mon, 16 Oct 2023 12:39:37 +0530 Subject: [PATCH 3/9] Add numpy solution --- project_euler/problem_187/sol1.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/project_euler/problem_187/sol1.py b/project_euler/problem_187/sol1.py index b277996f1f85..131a81787980 100644 --- a/project_euler/problem_187/sol1.py +++ b/project_euler/problem_187/sol1.py @@ -167,6 +167,31 @@ def for_solution(max_number: int = 10**8) -> int: return semiprimes_count +def solution(max_number: int = 10**8) -> int: + """ + Returns the number of composite integers below max_number have precisely two, + not necessarily distinct, prime factors. + + >>> solution(30) + 10 + """ + + prime_numbers = np_calculate_prime_numbers(max_number // 2) + + semiprimes_count = 0 + right = len(prime_numbers) - 1 + for left in range(len(prime_numbers)): + if left > right: + break + for r in range(right, left - 2, -1): + if prime_numbers[left] * prime_numbers[r] < max_number: + break + right = r + semiprimes_count += right - left + 1 + + return semiprimes_count + + def benchmark() -> None: """ Benchmarks From d56844ab6c3fb748433ce31b2d0e4ce626f0b218 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Mon, 16 Oct 2023 12:41:24 +0530 Subject: [PATCH 4/9] Update benchmarks --- project_euler/problem_187/sol1.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/project_euler/problem_187/sol1.py b/project_euler/problem_187/sol1.py index 131a81787980..8bcdc0789726 100644 --- a/project_euler/problem_187/sol1.py +++ b/project_euler/problem_187/sol1.py @@ -199,7 +199,8 @@ def benchmark() -> None: # Running performance benchmarks... # slow_solution : 108.50874730000032 # while_sol : 28.09581200000048 - # solution : 25.063097400000515 + # for_sol : 25.063097400000515 + # solution : 5.219610300000568 from timeit import timeit @@ -207,6 +208,7 @@ def benchmark() -> None: print(f"slow_solution : {timeit('slow_solution()', globals=globals(), number=10)}") print(f"while_sol : {timeit('while_solution()', globals=globals(), number=10)}") + print(f"for_sol : {timeit('for_solution()', globals=globals(), number=10)}") print(f"solution : {timeit('solution()', globals=globals(), number=10)}") From 8b6b3a4ea4064cb3f0276da30fe9406fad2f2874 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Sun, 29 Oct 2023 17:40:45 +0530 Subject: [PATCH 5/9] =?UTF-8?q?Fix=20=F0=9F=90=9B=20in=20benchmarks=20in?= =?UTF-8?q?=20prime=5Fnumbers.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I noticed that the functions yield the result, so they calculated nothing in the benchmark function. --- maths/prime_numbers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/maths/prime_numbers.py b/maths/prime_numbers.py index 38cc6670385d..dbf489c35af6 100644 --- a/maths/prime_numbers.py +++ b/maths/prime_numbers.py @@ -97,9 +97,9 @@ def benchmark(): from timeit import timeit setup = "from __main__ import slow_primes, primes, fast_primes" - print(timeit("slow_primes(1_000_000_000_000)", setup=setup, number=1_000_000)) - print(timeit("primes(1_000_000_000_000)", setup=setup, number=1_000_000)) - print(timeit("fast_primes(1_000_000_000_000)", setup=setup, number=1_000_000)) + print(timeit("list(slow_primes(1_000))", setup=setup, number=1_000)) + print(timeit("list(primes(1_000))", setup=setup, number=1_000)) + print(timeit("list(fast_primes(1_000))", setup=setup, number=1_000)) if __name__ == "__main__": From 61219b40fc9380a6cc00c6049ac4d879472ece21 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Sun, 29 Oct 2023 17:54:24 +0530 Subject: [PATCH 6/9] Add prime helper fn in prime_sieve_eratosthenes.py --- maths/prime_sieve_eratosthenes.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/maths/prime_sieve_eratosthenes.py b/maths/prime_sieve_eratosthenes.py index 32eef9165bba..3afc4a7d86a6 100644 --- a/maths/prime_sieve_eratosthenes.py +++ b/maths/prime_sieve_eratosthenes.py @@ -11,6 +11,9 @@ https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes """ +import numpy as np +from math import isqrt + def prime_sieve_eratosthenes(num: int) -> list[int]: """ @@ -45,6 +48,34 @@ 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) + [2] + >>> np_prime_sieve_eratosthenes(1) + [] + """ + if max_number < 2: + return [] + + # List containing a bool value for every odd number below max_number/2 + is_prime = np.ones((max_number + 1) // 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 From 938c022c29bbce87208e66004c347b28db895a45 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Sun, 29 Oct 2023 18:09:23 +0530 Subject: [PATCH 7/9] Add benchmark fn in prime_sieve_eratosthenes.py --- maths/prime_sieve_eratosthenes.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/maths/prime_sieve_eratosthenes.py b/maths/prime_sieve_eratosthenes.py index 3afc4a7d86a6..dcc7d753c0fe 100644 --- a/maths/prime_sieve_eratosthenes.py +++ b/maths/prime_sieve_eratosthenes.py @@ -76,10 +76,25 @@ def np_prime_sieve_eratosthenes(max_number: int) -> list[int]: return primes.tolist() +def benchmark(): + """ + Benchmarks + """ + from timeit import timeit + + print("Running performance benchmarks...") + + functions = ["prime_sieve_eratosthenes", "np_prime_sieve_eratosthenes"] + for func in functions: + print(f"{func} : {timeit(f'{func}(10_000)', globals=globals(), number=10_000)}") + + 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)) + + benchmark() From e6535b1bff647850025a354f34bacb21aa3de1e1 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Sun, 29 Oct 2023 18:13:38 +0530 Subject: [PATCH 8/9] Sort imports in prime_sieve_eratosthenes.py --- maths/prime_sieve_eratosthenes.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/maths/prime_sieve_eratosthenes.py b/maths/prime_sieve_eratosthenes.py index dcc7d753c0fe..5183ba430152 100644 --- a/maths/prime_sieve_eratosthenes.py +++ b/maths/prime_sieve_eratosthenes.py @@ -11,9 +11,10 @@ https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes """ -import numpy as np from math import isqrt +import numpy as np + def prime_sieve_eratosthenes(num: int) -> list[int]: """ From 2e28bde50d33b1e655e457eaf46196f185b5c4c5 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Sun, 29 Oct 2023 18:33:35 +0530 Subject: [PATCH 9/9] Import prime helper fn --- project_euler/problem_187/sol1.py | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/project_euler/problem_187/sol1.py b/project_euler/problem_187/sol1.py index 8bcdc0789726..2bfb5b8f189e 100644 --- a/project_euler/problem_187/sol1.py +++ b/project_euler/problem_187/sol1.py @@ -13,7 +13,7 @@ from math import isqrt -import numpy as np +from maths.prime_sieve_eratosthenes import np_prime_sieve_eratosthenes def slow_calculate_prime_numbers(max_number: int) -> list[int]: @@ -70,32 +70,6 @@ def py_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_solution(max_number: int = 10**8) -> int: """ Returns the number of composite integers below max_number have precisely two, @@ -176,7 +150,7 @@ def solution(max_number: int = 10**8) -> int: 10 """ - prime_numbers = np_calculate_prime_numbers(max_number // 2) + prime_numbers = np_prime_sieve_eratosthenes((max_number - 1) // 2) semiprimes_count = 0 right = len(prime_numbers) - 1