Skip to content

Instantly share code, notes, and snippets.

@DiTo97
Last active August 25, 2024 01:06
Show Gist options
  • Save DiTo97/d0808f41ac789ee9570b0d94153ff828 to your computer and use it in GitHub Desktop.
Save DiTo97/d0808f41ac789ee9570b0d94153ff828 to your computer and use it in GitHub Desktop.
A collection of prime finding algorithms implemented in pure python
import math
def sieve_of_atkin(n: int) -> list[int]:
"""The Sieve of Atkin prime finding algorithm, [1]_
References
----------
.. [1] A. O. L. Atkin, D. J. Bernstein, 2003
"""
if n < 3: return [2]
if n < 5: return [2, 3]
m = n + 1
P = [2, 3]
sieve = [False] * (m)
sqrtn = math.isqrt(n)
for x in range(1, sqrtn + 1):
for y in range(1, sqrtn + 1):
h = x**2
k = y**2
i = 4 * h + k
if i < m and (i % 12 == 1 or i % 12 == 5): sieve[i] = not sieve[i]
i = i - h
if i < m and i % 12 == 7: sieve[i] = not sieve[i]
i = i - 2 * k
if x > y and i < m and i % 12 == 11: sieve[i] = not sieve[i]
for x in range(5, sqrtn):
if sieve[x]:
z = x**2
for k in range(z, x + 1, z):
sieve[k] = False
for x in range(5, n):
if sieve[x]: P.append(x)
return P
def sieve_of_eratosthenes(n: int) -> list[int]:
"""The Sieve of Eratosthenes prime finding algorithm, [1]_
References
----------
.. [1] Nicomachus of Gerasa, 2nd c. AD
Introduction to Arithmetic
"""
sieve = [True] * (n + 1)
sqrtn = math.isqrt(n)
for x in range(2, sqrtn + 1):
if sieve[x]:
for i in range(x * x, n + 1, x):
sieve[i] = False
return [x for x in range(2, n) if sieve[x]]
def segmented_sieve_of_eratosthenes(n: int) -> list[int]:
"""The page-segmented Sieve of Eratosthenes prime finding algorithm, [1]_
Notes
-----
The algorithm trades space efficiency for time efficiency to the non-segmented algorithm.
References
----------
.. [1] C. Bays, R. H. Hudson, 1977
The Segmented Sieve of Eratosthenes and Primes in Arithmetic Progressions to 10^12
"""
shape = math.isqrt(n) + 1
sieve = [True] * shape
P = []
for x in range(2, shape):
if sieve[x]:
P.append(x)
for i in range(x * x, shape, x):
sieve[i] = False
lower = shape
while lower < n:
upper = min(lower + shape, n)
segments = [True] * (upper - lower)
for x in P:
start = max(x * x, (lower // x) * x)
if start < lower:
start += x
for i in range(start, upper, x):
segments[i - lower] = False
for x in range(lower, upper):
if segments[x - lower]:
P.append(x)
lower = upper
return P
def mixed_sieve(n: int) -> list[int]:
"""A prime finding algorithm mixed via rule-of-thumb"""
if n < 1_000: return sieve_of_atkin(n)
return sieve_of_eratosthenes(n)
def main():
pass
if __name__ == "__main__":
# Initialize the results
results = {
'sieve_of_eratosthenes': {'time': [], 'space': []},
'sieve_of_atkin': {'time': [], 'space': []},
'mixed_sieve': {'time': [], 'space': []},
# 'segmented_sieve_of_eratosthenes': {'time': [], 'space': []}, # FIXME: too slow
}
for limit in tqdm(limits):
print(f"Computing primes up to {limit}...")
for func in [sieve_of_eratosthenes, sieve_of_atkin, mixed_sieve]:
elapsed_time, mem_usage = measure(func, limit)
results[func.__name__]['time'].append(elapsed_time)
results[func.__name__]['space'].append(mem_usage)
plot_spacetime_efficiency(limits, results)
import matplotlib.pyplot as plt
def plot_timespace_efficiency(limits, results):
"""It plots the time and space efficiency of multiple configurations"""
plt.figure(figsize=(12, 6))
print(len(limits), results)
plt.subplot(1, 2, 1)
for func in results:
plt.plot(limits, results[func]['time'], label=func)
plt.xlabel('Limit')
plt.ylabel('Time (seconds)')
plt.xscale('log')
plt.yscale('log')
plt.legend()
plt.title('Time Efficiency')
plt.subplot(1, 2, 2)
for func in results:
plt.plot(limits, results[func]['space'], label=func)
plt.xlabel('Limit')
plt.ylabel('Space (MiB)')
plt.xscale('log')
plt.yscale('log')
plt.legend()
plt.title('Space Efficiency')
plt.tight_layout()
plt.show()
import typing
from typing import Any
from memory_profiler import memory_usage
def measure(closure: typing.Callable[[...], Any], *args: Any, **kwargs: Any):
"""It measures the time and space efficiency of a closure"""
start = time.perf_counter()
usage = memory_usage((closure, args, kwargs), interval=0.1, max_usage=True)
elapsed = time.time() - start
return elapsed, usage
matplotlib
memory-profiler
tqdm
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment