mathprime

algorithms

sieve of Eratosthenes

classic

def sieve(limit):
    import math
    check = [True] * max(3, limit + 1)
    check[0] = False
    check[1] = False
    check[2] = True
 
    for i in range(4, len(check), 2):
        check[i] = False
    
    sqrt_limit = int(math.sqrt(limit))
    for i in range(3, sqrt_limit + 1, 2):
        if not check[i]:
            continue
        for j in range(i * i, len(check), i):
            check[j] = False
 
    return [i for i in range(limit + 1) if check[i]]

segmented

def segmented_sieve(limit):
    import math
    sieve_size = 10000
    
    sqrt_limit = int(math.sqrt(limit))
    primes = [2]
    is_prime = [True] * (sqrt_limit + 1)
    for x in range(3, sqrt_limit + 1, 2):
        if not is_prime[x]:
            continue
        primes.append(x)
        for m in range(x * x, sqrt_limit + 1, x):
            is_prime[m] = False
    del is_prime
 
    for start in range(0, limit + 1, sieve_size):
        cur_size = min(sieve_size, limit + 1 - start)
        block = [True] * cur_size
        for p in primes:
            i = p * (start // p + bool(start % p)) - start
            while i < cur_size:
                block[i] = False
                i += p
 
        if start == 0:
            block[0] = False
            block[1] = False
 
        for i in range(0, cur_size):
            if block[i]:
                primes.append(start + i)
 
    return primes

references

trial division

def trial_div(limit):
    import math
 
    all_primes = [2]
 
    for i in range(3, limit + 1, 2):
        is_prime = True
        root = int(math.sqrt(i))
        for p in all_primes:
            if p > root:
                break
            if i % p == 0:
                is_prime = False
                break
        if is_prime:
            all_primes.append(i)
 
    return all_primes

Dijkstra

def dijkstra(limit):
    multiples = [4]
    all_primes = [2]
    lim_prime_idx = 0  # index of the smallest prime doesn't need to check
 
    for x in range(3, limit + 1, 2):  # skip 2
        if x >= multiples[lim_prime_idx]:
            lim_prime_idx += 1
            # next prime already found due to p_{n + 1} < p_{n}^2
            multiples.append(all_primes[lim_prime_idx]**2)
 
        is_prime = True
        for i in range(1, lim_prime_idx):  # skip 2
            if multiples[i] < x:
                multiples[i] += 2 * all_primes[i] # only odd multiples
            if x == multiples[i]:
                is_prime = False
                break
 
        if is_prime:
            all_primes.append(x)
 
    return all_primes

intuition

  • fox x in range (primes[i - 1]**2, primes[i]**2) with primes[i - 1] and primes[i] are consecutive primes
    • only need to check for prime factors < primes[i]
    • open conjecture: there is a prime number in between the squares of 2 consecutive primes
  • to check for composites, maintain a list of multiples of all primes up to primes[i]
    • multiples[i] is the smallest multiple of the primes[i] that >= x
    • only need to extend multiples when the range (primes[i - 1]**2, primes[i]**2) changes
    • multiples[i] is initialized with primes[i]**2 because smaller multiples of primes[i] is divisible by some prime < primes[i]

visualization

![[assets/generate primes/attachment.jpg]]
(`Q` is the list of multiples, index starts from 1)

references

performance comparison

import time
import tracemalloc
 
limit = 10**6
 
def profile(func, limit):
    t = time.perf_counter()
    primes = func(limit)
    print("num primes:", len(primes))
    print("time:", time.perf_counter() - t)
    
    tracemalloc.clear_traces()
    tracemalloc.start()
    func(limit)
    cur, peak = tracemalloc.get_traced_memory()
    tracemalloc.stop()
    print("memory:", peak - cur)
    print()
 
print('sieve', '-' * 10)
profile(sieve, limit)
 
print('segmented sieve', '-' * 10)
profile(segmented_sieve, limit)
 
print('trial div', '-' * 10)
profile(trial_div, limit)
 
print('dijkstra' , '-' * 10)
profile(dijkstra, limit)
sieve ----------
num primes: 78498
time: 0.3589751999825239
memory: 11143176
 
segmented sieve ----------
num primes: 78498
time: 1.2164868999971077
memory: 3280104
 
trial div ----------
num primes: 78498
time: 1.4015573000069708
memory: 3143152
 
dijkstra ----------
num primes: 78498
time: 0.9058849000139162
memory: 3149888