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
def sieve_of_atkin(limit):
"""The Sieve of Atkin prime finding algorithm, [1]_
References
----------
.. [1] A. O. L. Atkin, D. J. Bernstein, 2003
"""
P = [2,3]
sieve=[False]*(limit+1)
sqrt_limit = int(limit ** 0.5)
for x in range(1,sqrt_limit+1):
for y in range(1,sqrt_limit+1):
h = x ** 2
k = y ** 2
n = 4*h + k
if n<=limit and (n%12==1 or n%12==5) : sieve[n] = not sieve[n]
n = 3*h+k
if n<= limit and n%12==7 : sieve[n] = not sieve[n]
n = 3*h - k
if x>y and n<=limit and n%12==11 : sieve[n] = not sieve[n]
for n in range(5,sqrt_limit):
if sieve[n]:
z = n ** 2
for k in range(z,limit+1,z):
sieve[k] = False
for n in range(5,limit):
if sieve[n]: P.append(n)
return P
def sieve_of_eratosthenes(n):
"""The Sieve of Eratosthenes prime finding algorithm, [1]_
References
----------
.. [1] Nicomachus of Gerasa, 2nd c. AD
Introduction to Arithmetic
"""
sieve = [True] * (n+1)
for x in range(2, int(n**0.5) + 1):
if sieve[x]:
for i in range(x*x, n+1, x):
sieve[i] = False
return [i for i in range(2, n) if sieve[i]]
def segmented_sieve_of_eratosthenes(n):
"""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
"""
limit = math.isqrt(n) + 1
primes = []
sieve = [True] * limit
for p in range(2, limit):
if sieve[p]:
primes.append(p)
for i in range(p*p, limit, p):
sieve[i] = False
low_limit = limit
while low_limit < n:
high_limit = min(low_limit + limit, n)
segments = [True] * (high_limit - low_limit)
for p in primes:
start = max(p*p, (low_limit // p) * p)
if start < low_limit:
start += p
for i in range(start, high_limit, p):
segments[i - low_limit] = False
for i in range(low_limit, high_limit):
if segments[i - low_limit]:
primes.append(i)
low_limit = high_limit
return primes
def mixed_sieve(n):
"""A prime finding algorithm mixed via rule-of-thumb"""
if n < 1_000: return sieve_of_atkin(n)
return sieve_of_eratosthenes(n)
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_spacetime_efficiency(limits, results):
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()
from memory_profiler import memory_usage
def measure(func, *args, **kwargs):
start_time = time.time()
mem_usage = memory_usage((func, args, kwargs), interval=0.1, max_usage=True)
elapsed_time = time.time() - start_time
return elapsed_time, mem_usage
matplotlib
memory-profiler
tqdm
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment