Sieve of Eratosthenes

Sieve of Eratosthenes#



Programming Environment#

import math
#from   numba  import jit, njit, prange
import numpy  as np
from   typing import Iterator

def generate_integers (start : int = 0) -> Iterator[int]:
  n = start
  while True:
    yield n
    n += 1
num = generate_integers()
print(list(next(num) for _ in range(25)))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]
next(num)
25

Naive Python#

def p0 (p    : int,
        flag : bool = False) -> list[int]:
  num = generate_integers(2)
  arr = list(next(num) for _ in range(2, p + 1))

  primes = []
  prime  = arr.pop(0)  # remove the first number in the array of numbers
  primes.append(prime) # add it to our list of primes
  
  while prime <= p ** 0.5:
    if flag:
      print(prime, arr)
    arr   = list(i for i in arr if i % primes[-1] != 0) # remove all prime multiples from the array:
    prime = arr.pop(0)
    primes.append(prime)

  primes += arr
  return primes
%timeit p0(int(1e2))
%timeit p0(int(1e3))
%timeit p0(int(1e6))
15.6 µs ± 62.2 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
210 µs ± 1.19 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
1.02 s ± 20.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
p0(int(1e6))
[2,
 3,
 5,
 7,
 11,
 13,
 17,
 19,
 23,
 29,
 31,
 37,
 41,
 43,
 47,
 53,
 59,
 61,
 67,
 71,
 73,
 79,
 83,
 89,
 97,
 101,
 103,
 107,
 109,
 113,
 127,
 131,
 137,
 139,
 149,
 151,
 157,
 163,
 167,
 173,
 179,
 181,
 191,
 193,
 197,
 199,
 211,
 223,
 227,
 229,
 233,
 239,
 241,
 251,
 257,
 263,
 269,
 271,
 277,
 281,
 283,
 293,
 307,
 311,
 313,
 317,
 331,
 337,
 347,
 349,
 353,
 359,
 367,
 373,
 379,
 383,
 389,
 397,
 401,
 409,
 419,
 421,
 431,
 433,
 439,
 443,
 449,
 457,
 461,
 463,
 467,
 479,
 487,
 491,
 499,
 503,
 509,
 521,
 523,
 541,
 547,
 557,
 563,
 569,
 571,
 577,
 587,
 593,
 599,
 601,
 607,
 613,
 617,
 619,
 631,
 641,
 643,
 647,
 653,
 659,
 661,
 673,
 677,
 683,
 691,
 701,
 709,
 719,
 727,
 733,
 739,
 743,
 751,
 757,
 761,
 769,
 773,
 787,
 797,
 809,
 811,
 821,
 823,
 827,
 829,
 839,
 853,
 857,
 859,
 863,
 877,
 881,
 883,
 887,
 907,
 911,
 919,
 929,
 937,
 941,
 947,
 953,
 967,
 971,
 977,
 983,
 991,
 997,
 1009,
 1013,
 1019,
 1021,
 1031,
 1033,
 1039,
 1049,
 1051,
 1061,
 1063,
 1069,
 1087,
 1091,
 1093,
 1097,
 1103,
 1109,
 1117,
 1123,
 1129,
 1151,
 1153,
 1163,
 1171,
 1181,
 1187,
 1193,
 1201,
 1213,
 1217,
 1223,
 1229,
 1231,
 1237,
 1249,
 1259,
 1277,
 1279,
 1283,
 1289,
 1291,
 1297,
 1301,
 1303,
 1307,
 1319,
 1321,
 1327,
 1361,
 1367,
 1373,
 1381,
 1399,
 1409,
 1423,
 1427,
 1429,
 1433,
 1439,
 1447,
 1451,
 1453,
 1459,
 1471,
 1481,
 1483,
 1487,
 1489,
 1493,
 1499,
 1511,
 1523,
 1531,
 1543,
 1549,
 1553,
 1559,
 1567,
 1571,
 1579,
 1583,
 1597,
 1601,
 1607,
 1609,
 1613,
 1619,
 1621,
 1627,
 1637,
 1657,
 1663,
 1667,
 1669,
 1693,
 1697,
 1699,
 1709,
 1721,
 1723,
 1733,
 1741,
 1747,
 1753,
 1759,
 1777,
 1783,
 1787,
 1789,
 1801,
 1811,
 1823,
 1831,
 1847,
 1861,
 1867,
 1871,
 1873,
 1877,
 1879,
 1889,
 1901,
 1907,
 1913,
 1931,
 1933,
 1949,
 1951,
 1973,
 1979,
 1987,
 1993,
 1997,
 1999,
 2003,
 2011,
 2017,
 2027,
 2029,
 2039,
 2053,
 2063,
 2069,
 2081,
 2083,
 2087,
 2089,
 2099,
 2111,
 2113,
 2129,
 2131,
 2137,
 2141,
 2143,
 2153,
 2161,
 2179,
 2203,
 2207,
 2213,
 2221,
 2237,
 2239,
 2243,
 2251,
 2267,
 2269,
 2273,
 2281,
 2287,
 2293,
 2297,
 2309,
 2311,
 2333,
 2339,
 2341,
 2347,
 2351,
 2357,
 2371,
 2377,
 2381,
 2383,
 2389,
 2393,
 2399,
 2411,
 2417,
 2423,
 2437,
 2441,
 2447,
 2459,
 2467,
 2473,
 2477,
 2503,
 2521,
 2531,
 2539,
 2543,
 2549,
 2551,
 2557,
 2579,
 2591,
 2593,
 2609,
 2617,
 2621,
 2633,
 2647,
 2657,
 2659,
 2663,
 2671,
 2677,
 2683,
 2687,
 2689,
 2693,
 2699,
 2707,
 2711,
 2713,
 2719,
 2729,
 2731,
 2741,
 2749,
 2753,
 2767,
 2777,
 2789,
 2791,
 2797,
 2801,
 2803,
 2819,
 2833,
 2837,
 2843,
 2851,
 2857,
 2861,
 2879,
 2887,
 2897,
 2903,
 2909,
 2917,
 2927,
 2939,
 2953,
 2957,
 2963,
 2969,
 2971,
 2999,
 3001,
 3011,
 3019,
 3023,
 3037,
 3041,
 3049,
 3061,
 3067,
 3079,
 3083,
 3089,
 3109,
 3119,
 3121,
 3137,
 3163,
 3167,
 3169,
 3181,
 3187,
 3191,
 3203,
 3209,
 3217,
 3221,
 3229,
 3251,
 3253,
 3257,
 3259,
 3271,
 3299,
 3301,
 3307,
 3313,
 3319,
 3323,
 3329,
 3331,
 3343,
 3347,
 3359,
 3361,
 3371,
 3373,
 3389,
 3391,
 3407,
 3413,
 3433,
 3449,
 3457,
 3461,
 3463,
 3467,
 3469,
 3491,
 3499,
 3511,
 3517,
 3527,
 3529,
 3533,
 3539,
 3541,
 3547,
 3557,
 3559,
 3571,
 3581,
 3583,
 3593,
 3607,
 3613,
 3617,
 3623,
 3631,
 3637,
 3643,
 3659,
 3671,
 3673,
 3677,
 3691,
 3697,
 3701,
 3709,
 3719,
 3727,
 3733,
 3739,
 3761,
 3767,
 3769,
 3779,
 3793,
 3797,
 3803,
 3821,
 3823,
 3833,
 3847,
 3851,
 3853,
 3863,
 3877,
 3881,
 3889,
 3907,
 3911,
 3917,
 3919,
 3923,
 3929,
 3931,
 3943,
 3947,
 3967,
 3989,
 4001,
 4003,
 4007,
 4013,
 4019,
 4021,
 4027,
 4049,
 4051,
 4057,
 4073,
 4079,
 4091,
 4093,
 4099,
 4111,
 4127,
 4129,
 4133,
 4139,
 4153,
 4157,
 4159,
 4177,
 4201,
 4211,
 4217,
 4219,
 4229,
 4231,
 4241,
 4243,
 4253,
 4259,
 4261,
 4271,
 4273,
 4283,
 4289,
 4297,
 4327,
 4337,
 4339,
 4349,
 4357,
 4363,
 4373,
 4391,
 4397,
 4409,
 4421,
 4423,
 4441,
 4447,
 4451,
 4457,
 4463,
 4481,
 4483,
 4493,
 4507,
 4513,
 4517,
 4519,
 4523,
 4547,
 4549,
 4561,
 4567,
 4583,
 4591,
 4597,
 4603,
 4621,
 4637,
 4639,
 4643,
 4649,
 4651,
 4657,
 4663,
 4673,
 4679,
 4691,
 4703,
 4721,
 4723,
 4729,
 4733,
 4751,
 4759,
 4783,
 4787,
 4789,
 4793,
 4799,
 4801,
 4813,
 4817,
 4831,
 4861,
 4871,
 4877,
 4889,
 4903,
 4909,
 4919,
 4931,
 4933,
 4937,
 4943,
 4951,
 4957,
 4967,
 4969,
 4973,
 4987,
 4993,
 4999,
 5003,
 5009,
 5011,
 5021,
 5023,
 5039,
 5051,
 5059,
 5077,
 5081,
 5087,
 5099,
 5101,
 5107,
 5113,
 5119,
 5147,
 5153,
 5167,
 5171,
 5179,
 5189,
 5197,
 5209,
 5227,
 5231,
 5233,
 5237,
 5261,
 5273,
 5279,
 5281,
 5297,
 5303,
 5309,
 5323,
 5333,
 5347,
 5351,
 5381,
 5387,
 5393,
 5399,
 5407,
 5413,
 5417,
 5419,
 5431,
 5437,
 5441,
 5443,
 5449,
 5471,
 5477,
 5479,
 5483,
 5501,
 5503,
 5507,
 5519,
 5521,
 5527,
 5531,
 5557,
 5563,
 5569,
 5573,
 5581,
 5591,
 5623,
 5639,
 5641,
 5647,
 5651,
 5653,
 5657,
 5659,
 5669,
 5683,
 5689,
 5693,
 5701,
 5711,
 5717,
 5737,
 5741,
 5743,
 5749,
 5779,
 5783,
 5791,
 5801,
 5807,
 5813,
 5821,
 5827,
 5839,
 5843,
 5849,
 5851,
 5857,
 5861,
 5867,
 5869,
 5879,
 5881,
 5897,
 5903,
 5923,
 5927,
 5939,
 5953,
 5981,
 5987,
 6007,
 6011,
 6029,
 6037,
 6043,
 6047,
 6053,
 6067,
 6073,
 6079,
 6089,
 6091,
 6101,
 6113,
 6121,
 6131,
 6133,
 6143,
 6151,
 6163,
 6173,
 6197,
 6199,
 6203,
 6211,
 6217,
 6221,
 6229,
 6247,
 6257,
 6263,
 6269,
 6271,
 6277,
 6287,
 6299,
 6301,
 6311,
 6317,
 6323,
 6329,
 6337,
 6343,
 6353,
 6359,
 6361,
 6367,
 6373,
 6379,
 6389,
 6397,
 6421,
 6427,
 6449,
 6451,
 6469,
 6473,
 6481,
 6491,
 6521,
 6529,
 6547,
 6551,
 6553,
 6563,
 6569,
 6571,
 6577,
 6581,
 6599,
 6607,
 6619,
 6637,
 6653,
 6659,
 6661,
 6673,
 6679,
 6689,
 6691,
 6701,
 6703,
 6709,
 6719,
 6733,
 6737,
 6761,
 6763,
 6779,
 6781,
 6791,
 6793,
 6803,
 6823,
 6827,
 6829,
 6833,
 6841,
 6857,
 6863,
 6869,
 6871,
 6883,
 6899,
 6907,
 6911,
 6917,
 6947,
 6949,
 6959,
 6961,
 6967,
 6971,
 6977,
 6983,
 6991,
 6997,
 7001,
 7013,
 7019,
 7027,
 7039,
 7043,
 7057,
 7069,
 7079,
 7103,
 7109,
 7121,
 7127,
 7129,
 7151,
 7159,
 7177,
 7187,
 7193,
 7207,
 7211,
 7213,
 7219,
 7229,
 7237,
 7243,
 7247,
 7253,
 7283,
 7297,
 7307,
 7309,
 7321,
 7331,
 7333,
 7349,
 7351,
 7369,
 7393,
 7411,
 7417,
 7433,
 7451,
 7457,
 7459,
 7477,
 7481,
 7487,
 7489,
 7499,
 7507,
 7517,
 7523,
 7529,
 7537,
 7541,
 7547,
 7549,
 7559,
 7561,
 7573,
 7577,
 7583,
 7589,
 7591,
 7603,
 7607,
 7621,
 7639,
 7643,
 7649,
 7669,
 7673,
 7681,
 7687,
 7691,
 7699,
 7703,
 7717,
 7723,
 7727,
 7741,
 7753,
 7757,
 7759,
 7789,
 7793,
 7817,
 7823,
 7829,
 7841,
 7853,
 7867,
 7873,
 7877,
 7879,
 7883,
 7901,
 7907,
 7919,
 ...]

n = int(1e8)
%time sieve = [True] * n
%time sieve = [True for _ in range(n)]
CPU times: user 20.1 ms, sys: 50.7 ms, total: 70.8 ms
Wall time: 71.9 ms
CPU times: user 1.3 s, sys: 127 ms, total: 1.43 s
Wall time: 1.46 s
# def sieve_of_eratosthenes (n):
#   sieve      = [True] * n
#   sieve[0:1] = [False, False]
#   for prime in range(2, n + 1):
#     if sieve[prime]:
#       for i in range(2*prime, n+1, prime):
#         sieve[i] = False
# def p1 (n):
#   sieve = [True] * n
#   p     = 2
#   while (p*p <= n):
#     if (sieve[p] == True):
#       for i in range(p*p, n+1, p):
#         sieve[i] = False
#     p += 1
#   for p in range(2, n+1):
#     if sieve[p]:
#       p
# %timeit p1(int(1e2))
# %timeit p1(int(1e3))
# %timeit p1(int(1e6))
# %timeit p1(int(1e7))

\[\begin{split} \begin{align*} n = 3 && 9 + 6k = 9, 15, 21, 27, 33, 39, 45, 51, 57, 63, 69, 75, 81, 87, 93, 99 \\ n = 5 && 25 + 10k = 25, 35, 45, 55, 65, 75, 85, 95 \\ n = 7 && 49 + 14k = 49, 63, 77, 91 \\ n = 11 && 121 + 22k = \dots \\ \end{align*} \end{split}\]

\( \begin{aligned} & 3^2 + 2.3k = 3(2k + 3) = \phantom{1} 6k + \phantom{1} 9 = \phantom{1} 6(k + 1) + \phantom{1} 3 = 3(2k' + 1) && \text{odd multiples of 3} \\ & 5^2 + 2.5k = 5(2k + 5) = 10k + 25 = 10(k + 2) + \phantom{1} 5 = 5(2k' + 1) && \text{odd multiples of 5 but not 3} \\ & 7^2 + 2.7k = 7(2k + 7) = 14k + 49 = 14(k + 3) + \phantom{1} 7 = 7(2k' + 1) && \text{odd multiples of 7 but not 3, 5} \\ \end {aligned} \)

# FULL SIEVE
n     = 100
sieve = [True] * n
for i in range(3, int(n ** 0.5) + 1, 2):
  if sieve[i]:
    sieve[i*i::2*i] = [False] * ((n - i * i - 1) // (2 * i) + 1)
  print(i, len(np.flatnonzero(sieve)), np.flatnonzero(sieve))
print([2] + [i for i in range(3, n, 2) if sieve[i]])
3
 84 [ 0  1  2  3  4  5  6  7  8 10 11 12 13 14 16 17 18 19 20 22 23 24 25 26
 28 29 30 31 32 34 35 36 37 38 40 41 42 43 44 46 47 48 49 50 52 53 54 55
 56 58 59 60 61 62 64 65 66 67 68 70 71 72 73 74 76 77 78 79 80 82 83 84
 85 86 88 89 90 91 92 94 95 96 97 98]
5 78 [ 0  1  2  3  4  5  6  7  8 10 11 12 13 14 16 17 18 19 20 22 23 24 26 28
 29 30 31 32 34 36 37 38 40 41 42 43 44 46 47 48 49 50 52 53 54 56 58 59
 60 61 62 64 66 67 68 70 71 72 73 74 76 77 78 79 80 82 83 84 86 88 89 90
 91 92 94 96 97 98]
7 75 [ 0  1  2  3  4  5  6  7  8 10 11 12 13 14 16 17 18 19 20 22 23 24 26 28
 29 30 31 32 34 36 37 38 40 41 42 43 44 46 47 48 50 52 53 54 56 58 59 60
 61 62 64 66 67 68 70 71 72 73 74 76 78 79 80 82 83 84 86 88 89 90 92 94
 96 97 98]
9 75 [ 0  1  2  3  4  5  6  7  8 10 11 12 13 14 16 17 18 19 20 22 23 24 26 28
 29 30 31 32 34 36 37 38 40 41 42 43 44 46 47 48 50 52 53 54 56 58 59 60
 61 62 64 66 67 68 70 71 72 73 74 76 78 79 80 82 83 84 86 88 89 90 92 94
 96 97 98]
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
# HALF SIEVE
n     = 100
sieve = [True] * (n//2)
for i in range(3, int(n**0.5) + 1, 2):
  if sieve[i//2]:
    sieve[i*i//2::i] = [False] * ((n - i*i - 1) // (2*i) + 1)
  print(i, len(np.flatnonzero(sieve)), np.flatnonzero(sieve))
print([2] + [2*i + 1 for i in range(1, n//2) if sieve[i]])
3 34 [ 0  1  2  3  5  6  8  9 11 12 14 15 17 18 20 21 23 24 26 27 29 30 32 33
 35 36 38 39 41 42 44 45 47 48]
5 28 [ 0  1  2  3  5  6  8  9 11 14 15 18 20 21 23 24 26 29 30 33 35 36 38 39
 41 44 45 48]
7 25 [ 0  1  2  3  5  6  8  9 11 14 15 18 20 21 23 26 29 30 33 35 36 39 41 44
 48]
9 25 [ 0  1  2  3  5  6  8  9 11 14 15 18 20 21 23 26 29 30 33 35 36 39 41 44
 48]
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
def p2 (n):
  """ FULL SIEVE
  Returns a list of primes p < n.
  https://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n/2068548
  """
  sieve = [True] * n
  for i in range(3, int(n ** 0.5) + 1, 2):
    if sieve[i]:
      sieve[i*i::2*i] = [False] * ((n - i * i - 1) // (2 * i) + 1)
  return [2] + [i for i in range(3, n, 2) if sieve[i]]

def p3 (n):
  """ HALF SIEVE
  Returns a list of primes p < n.
  https://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n/2068548
  """
  sieve = [True] * (n//2)
  for i in range(3, int(n**0.5) + 1, 2):
    if sieve[i//2]:
      sieve[i*i//2::i] = [False] * ((n - i*i - 1) // (2*i) + 1)
  return [2] + [2*i + 1 for i in range(1, n//2) if sieve[i]]
%timeit p2(int(1e7))
%timeit p3(int(1e7))
208 ms ± 5.13 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
209 ms ± 3.13 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

NumPy and Numba#

def p4 (n : int) -> np.ndarray:
  """Sieve of Eratosthenes"""
  flags = np.full(shape = n, fill_value = True) #np.ones(shape = n, dtype = bool)
  flags[0], flags[1] = False, False # 0 and 1 are not prime
  for i in range(2, int(n ** 0.5) + 1, 2):
    if flags[i]:
      flags[i*i::i] = False
  return np.flatnonzero(a = flags)

%timeit p4(int(1e7))
12.7 ms ± 89.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
@njit(parallel = True, fastmath = True)
def p5 (n : int) -> np.ndarray:
  """Sieve of Eratosthenes"""
  primes = np.full(shape = n, fill_value = True)
  primes[0], primes[1] = False, False # 0 and 1 are not prime
  for i in prange(2, int(n ** 0.5) + 1, 2):
    if primes[i]:
      primes[i*i::i] = False
  return np.flatnonzero(a = primes)

%timeit p5(int(1e7))
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[17], line 1
----> 1 @njit(parallel = True, fastmath = True)
      2 def p5 (n : int) -> np.ndarray:
      3   """Sieve of Eratosthenes"""
      4   primes = np.full(shape = n, fill_value = True)

NameError: name 'njit' is not defined

def p6 (n : int) -> np.ndarray:
  """ Returns an array of primes, 3 <= p < n """
  sieve = np.full(shape = n//2, fill_value = True) #np.ones(n//2, dtype=bool)
  for i in range(3, int(n**0.5) + 1, 2):
    if sieve[i//2]:
      sieve[i*i//2::i] = False
  return 2*np.nonzero(a = sieve)[0][1::] + 1

%timeit p6(int(1e7))
2.5 s ± 67.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
def p7 (n : int) -> np.ndarray:
  """ Input n>=6, Returns an array of primes, 2 <= p < n """
  sieve = np.full(shape = n//3 + (n%6==2), fill_value = True) #np.ones(n//3 + (n%6==2), dtype=bool)
  for i in range(3, int(n**0.5)//3 + 1):
    if sieve[i]:
      k = 3*i + 1 | 1
      sieve[k*k//3            ::2*k] = False
      sieve[k*(k-2*(i&1)+4)//3::2*k] = False
  return np.r_[2,3,((3*np.nonzero(sieve)[0][1:]+1)|1)]

%timeit p7(int(1e7))
2.57 s ± 78.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit p0(int(1e7))
%timeit p1(int(1e7))
%timeit p2(int(1e7))
%timeit p3(int(1e7))
%timeit p4(int(1e7))
%timeit p5(int(1e7))
%timeit p6(int(1e7))
%timeit p7(int(1e7))