Sieve of Eratosthenes

Open In Colab

In this notebook, I implement a few versions of the sieve of Eratosthenes for finding prime numbers.

Starting from $n=2$ and given the set $P$ of prime numbers up until $n-1$, we check if $n$ is divisible by any $p \in P$. If not, $P \leftarrow P + \{n\}$.

from math import sqrt

from math import sqrt, pi
import numpy as np

cached_primes = [2]
evaluated = 2

The first version of the sieve works just like explained at the top.

def eratosthenes(n):
    primes = []
    for i in range(2, n):
        for p in primes:
            if i % p == 0:
                break
        else:
            primes.append(i)

    return primes

The second version starts with the number 2 (the only even prime) already in the set of primes and only checks the primality of odd numbers. It should be twice as fast.

def eratosthenes_no_evens(n):
    primes = [2]
    for i in range(3, n, 2):
        for p in primes:
            if i % p == 0:
                break
        else:
            primes.append(i)

    return primes

The last version uses all the gimmicks from the previous ones, but also caches the primes found in previous runs and reuses them in subsequent calls of the function.

def eratosthenes_cached(n):
   global evaluated, cached_primes

   if evaluated < n:
      start = evaluated if evaluated % 2 != 0 else evaluated + 1
      for i in range(start, n + 1, 2):
         for p in cached_primes:
            if i % p == 0:
              break
         else:
            cached_primes.append(i)

   evaluated = n

   for i in range(len(cached_primes)):
     if cached_primes[i]>=n:
       return cached_primes[:i]

   return cached_primes

Check if the output of all functions is equal for an arbitrary number of input integers.

from numpy.random import randint

for v in randint(5000, size=100):
  a = eratosthenes(v)
  b = eratosthenes_no_evens(v)
  c = eratosthenes_cached(v)

  if not a == b == c:
    raise RuntimeError('not equal')

Let’s check the performance of the three functions.

%timeit -r 5 -n 30 [eratosthenes(v) for v in randint(5000, size=100)]
%timeit -r 5 -n 30 [eratosthenes_no_evens(v) for v in randint(5000, size=100)]
%timeit -r 5 -n 30 [eratosthenes_cached(v) for v in randint(5000, size=100)]
30 loops, best of 5: 430 ms per loop
30 loops, best of 5: 404 ms per loop
30 loops, best of 5: 200 ms per loop

We can see that, because the third function caches primes from previous runs, it is twice as fast than the other two.




Enjoy Reading This Article?

Here are some more articles you might like to read next:

  • Approximating $\pi$ using Euler's identity with primes
  • Answering questions from an Obsidian database with LLMs + RAG
  • Running a Gemma-powered question-answering chatbot locally with LangChain + Ollama
  • Using task-spooler to queue experiments on Linux