Skip to main content

finds primes using Eratosthenes sieving algorithm

Project description

primesgen is a collection of implementations of the Eratosthenes Sieve algorithm

all timing results displayed are from a 2.3 GHz 8-Core Intel Core i9/16 GB 2400 MHz DDR4 machine (Darwin).

Basic Usage

get_primes_less_than

import time
from src.primesgen import get_primes_less_than

max_val = 1_000_000_000
start = time.time()

primes = get_primes_less_than(max_val)

duration = time.time() - start
print(f'\tfound all {len(primes):,} primes <= {max_val:,} in {duration:.2f} seconds')
> found all 50,847,533 primes <= 1,000,000,000 in 15.64 seconds

Globals and L1D Cache Size

There are 3 global values, and a function to get the L1D cache size in __init__.py.

'''non-segmented only, boundary between using Eratosthenes and IndexEratosthenes'''
sieve_factory_erat_indexed_cutoff = 800_000_000
'''segmented only, number of multiples allowed in mem before the OnDiskMultiples writes to disk'''
on_disk_multiples_block_size = 25_000
''' segmented only, boundary between using InMemMultiples and OnDiskMultiples'''
multiples_factory_ondisk_inmem_cutoff = 1_000_000

''' 'Darwin' works
    'Linux' is untested
    'Windows' (or others) is not implemented'''
def get_cache_size() -> int:

Implementations

primesgen.Erastothenes

Faster for numbers < 1,000,000,000

def _core(in_max_val: int) -> list[int]:
    sieve = np.ones(in_max_val + 1, dtype=bool)
    sieve[0] = sieve[1] = sieve[2::2] = False
    max_sqrt_value = math.ceil(math.sqrt(in_max_val + 1)) + 1
    for prime in (value for value in range(3, max_sqrt_value, 2) if sieve[value]):
        sieve[prime * prime::prime] = False
    return list(np.where(sieve)[0].astype(int))

primesgen.IndexedEratosthenes

Uses a 1/2 size sieve but requires extra calculations (see _to_val and _to_index). Faster for numbers > 1,000,0000. Here is a snippet:

def _to_val(index: int) -> int: return 2 * index + 3
def _to_index(value: float | int) -> int: return round(value * .5 - 1.5)

def _core(in_max_val: int) -> list[int]:
    max_val = in_max_val - 1 if in_max_val % 2 == 0 else in_max_val
    sieve = np.ones(IndexedEratosthenes._to_index(max_val) + 1, dtype=bool)
    max_sqrt_value_index = IndexedEratosthenes._to_index(math.ceil(math.sqrt(max_val + 1)) + 1)
    for p_val in (IndexedEratosthenes._to_val(p_index) for p_index in range(0, max_sqrt_value_index + 1) if
                  sieve[p_index]):
        sieve[IndexedEratosthenes._to_index(p_val * p_val)::p_val] = False
    return [IndexedEratosthenes._to_val(p_index) for p_index in np.where(sieve)[0]]

primesgen.SegmentedEratosthenes

Segments the sieve which allows processing larger numbers than the other implementations.

Using Defaults for the multiples and primes

import time

from src import get_cache_size
from src.multiples import factory as multiples_factory
from src.primes import factory as primes_factory
from src.primesgenseg import SegmentedEratosthenes

max_val = 1_000_000_000
primes = primes_factory()
multiples = multiples_factory(max_val)
segment_max_size = get_cache_size()
start = time.time()

prime_values = SegmentedEratosthenes(segment_max_size, primes, multiples).primes(max_val)

duration = time.time() - start
print(f'\tfound all {len(prime_values):,} primes <= {max_val:,} in {duration:.2f} seconds')
found all 50,847,533 primes <= 1,000,000,000 in 117.92 seconds

The defaults used are

primes = primes_factory()
multiples = multiples_factory(max_val)

primes_factory (default) creates primes that will reside in memory until return.

If a number >= multiples_factory_ondisk_inmem_cutoff, then the multiples in memory will be backed up disk (/var/tmp/) as needed, only having at most on_disk_multiples_block_size in memory.

Using a queue for primes

Give the primes_factory a queue.Queue, prime values will be broadcast immediately and not stored in memory. Here is an example with a custom queue consumer:

def example_queue_consumer(in_queue: queue.Queue[int]) -> None:
    # this is an example of someone consuming the primes if a queue is provided
    print_count = 1_000_000
    count = 0
    partial_count = 0
    while True:
        prime = in_queue.get()
        in_queue.task_done()
        if prime is not None:
            count += 1
            partial_count += 1
        if partial_count >= print_count or prime is None:
            print(f'\tfound {count:,} primes thus far')
            partial_count = 0
        if prime is None:
            break

max_value = 1_000_000_000
primes = primes_factory(primes_queue)
multiples = multiples_factory(max_val)
sieve = SegmentedEratosthenes(get_cache_size(), primes, multiples)

sieve_thread = Thread(target=sieve.primes, args=(max_value,))
print_thread = Thread(target=Queue.example_queue_consumer, args=(primes_queue,))
sieve_thread.start()
print_thread.start()
sieve_thread.join()
print_thread.join()
found 1,000,000 primes thus far
found 2,000,000 primes thus far
found 3,000,000 primes thus far
...
found 49,000,000 primes thus far
found 50,000,000 primes thus far
found 50,847,533 primes thus far

Tests

pytest results should look like

============================= test session starts ==============================
collecting ... collected 10 items

test_primesgen.py::test_100_000
test_primesgen.py::test_1_000_000
test_primesgen.py::test_10_000_000
test_primesgen.py::test_100_000_000
test_primesgen.py::test_35_123_231
test_primesgenseg.py::test_84_max_val_9_segment_size PASSED
test_primesgenseg.py::test_130_max_val_9_segment_size
test_primesgenseg.py::test_segment_boundaries
test_primesgenseg.py::test_100_000
test_primesgenseg.py::test_120_001

============================== 10 passed in 4.66s ==============================
PASSED

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

primesgen-1.0.1.tar.gz (8.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

primesgen-1.0.1-py3-none-any.whl (9.0 kB view details)

Uploaded Python 3

File details

Details for the file primesgen-1.0.1.tar.gz.

File metadata

  • Download URL: primesgen-1.0.1.tar.gz
  • Upload date:
  • Size: 8.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for primesgen-1.0.1.tar.gz
Algorithm Hash digest
SHA256 16bce42554734beedc2eaf9c1cc6691d4a93cce17163ca3d694f9b2a107162af
MD5 c8576a99b5ec986a7cd1242e6a8688b8
BLAKE2b-256 eb5787567ac975702259ed18965e750ad12443c0bd0848af95160a3b09324f57

See more details on using hashes here.

File details

Details for the file primesgen-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: primesgen-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 9.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for primesgen-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 fdb3fafa063539eece27281d9d45b6bd1a00c393bc085ef933da83196dd2a4cc
MD5 f1fe12eab31f36e4df85751b4656c0b9
BLAKE2b-256 f3d2f85657f71844b6dd0f1e10530d7c50285662659802158b78a1298b13d17a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page