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.2.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.2-py3-none-any.whl (9.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: primesgen-1.0.2.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.2.tar.gz
Algorithm Hash digest
SHA256 514bf73d13cf5d40884dd5d6dd6d06cbc94382eda2b85670505ebb13d2aaf355
MD5 0bfb7c6efeca329e7d9a6d7d55d6d07d
BLAKE2b-256 da6b606f5712e72c70f51041b07b84687bdaf1cfbd0c7f22339236721c2a6df7

See more details on using hashes here.

File details

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

File metadata

  • Download URL: primesgen-1.0.2-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.2-py3-none-any.whl
Algorithm Hash digest
SHA256 1a76b92ecb194a2d5cbfce038b9c8d26fbcd5f6b3d96a33c249b8c912066f983
MD5 fe359f4de12d0080879b503184e549d4
BLAKE2b-256 2392691d15aa3c5ee3fcad61a11e56d80c6d2f508f48c28a1132a1ca7ccf5c17

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