mirror of
https://github.com/TheAlgorithms/Python.git
synced 2024-11-30 16:31:08 +00:00
1ebae5d43e
* Add comments and wikipedia link in calculate_prime_numbers * Add improved calculate_prime_numbers * Separate slow_solution and new_solution * Use for loops in solution * Separate while_solution and new solution * Add performance benchmark * Add doctest for calculate_prime_numbers * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Removed white space --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
163 lines
4.4 KiB
Python
163 lines
4.4 KiB
Python
"""
|
|
Project Euler Problem 187: https://projecteuler.net/problem=187
|
|
|
|
A composite is a number containing at least two prime factors.
|
|
For example, 15 = 3 x 5; 9 = 3 x 3; 12 = 2 x 2 x 3.
|
|
|
|
There are ten composites below thirty containing precisely two,
|
|
not necessarily distinct, prime factors: 4, 6, 9, 10, 14, 15, 21, 22, 25, 26.
|
|
|
|
How many composite integers, n < 10^8, have precisely two,
|
|
not necessarily distinct, prime factors?
|
|
"""
|
|
|
|
from math import isqrt
|
|
|
|
|
|
def slow_calculate_prime_numbers(max_number: int) -> list[int]:
|
|
"""
|
|
Returns prime numbers below max_number.
|
|
See: https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
|
|
|
|
>>> slow_calculate_prime_numbers(10)
|
|
[2, 3, 5, 7]
|
|
|
|
>>> slow_calculate_prime_numbers(2)
|
|
[]
|
|
"""
|
|
|
|
# List containing a bool value for every number below max_number/2
|
|
is_prime = [True] * max_number
|
|
|
|
for i in range(2, isqrt(max_number - 1) + 1):
|
|
if is_prime[i]:
|
|
# Mark all multiple of i as not prime
|
|
for j in range(i**2, max_number, i):
|
|
is_prime[j] = False
|
|
|
|
return [i for i in range(2, max_number) if is_prime[i]]
|
|
|
|
|
|
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_solution(max_number: int = 10**8) -> int:
|
|
"""
|
|
Returns the number of composite integers below max_number have precisely two,
|
|
not necessarily distinct, prime factors.
|
|
|
|
>>> slow_solution(30)
|
|
10
|
|
"""
|
|
|
|
prime_numbers = slow_calculate_prime_numbers(max_number // 2)
|
|
|
|
semiprimes_count = 0
|
|
left = 0
|
|
right = len(prime_numbers) - 1
|
|
while left <= right:
|
|
while prime_numbers[left] * prime_numbers[right] >= max_number:
|
|
right -= 1
|
|
semiprimes_count += right - left + 1
|
|
left += 1
|
|
|
|
return semiprimes_count
|
|
|
|
|
|
def while_solution(max_number: int = 10**8) -> int:
|
|
"""
|
|
Returns the number of composite integers below max_number have precisely two,
|
|
not necessarily distinct, prime factors.
|
|
|
|
>>> while_solution(30)
|
|
10
|
|
"""
|
|
|
|
prime_numbers = calculate_prime_numbers(max_number // 2)
|
|
|
|
semiprimes_count = 0
|
|
left = 0
|
|
right = len(prime_numbers) - 1
|
|
while left <= right:
|
|
while prime_numbers[left] * prime_numbers[right] >= max_number:
|
|
right -= 1
|
|
semiprimes_count += right - left + 1
|
|
left += 1
|
|
|
|
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 = 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
|
|
"""
|
|
# Running performance benchmarks...
|
|
# slow_solution : 108.50874730000032
|
|
# while_sol : 28.09581200000048
|
|
# solution : 25.063097400000515
|
|
|
|
from timeit import timeit
|
|
|
|
print("Running performance benchmarks...")
|
|
|
|
print(f"slow_solution : {timeit('slow_solution()', globals=globals(), number=10)}")
|
|
print(f"while_sol : {timeit('while_solution()', globals=globals(), number=10)}")
|
|
print(f"solution : {timeit('solution()', globals=globals(), number=10)}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
print(f"Solution: {solution()}")
|
|
benchmark()
|