prime numbers
efficient algorithms
programming
code optimization
computational mathematics

Most efficient code for the first 10000 prime numbers?

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

Generating the first 10,000 prime numbers is a classic algorithm problem. The naive approach tests each number with many divisions, which works but wastes computation. For this range, the Sieve of Eratosthenes is typically the most efficient and easiest to maintain.

This article compares practical methods and shows a production-friendly implementation strategy.

Core Sections

1) Baseline trial division

python
1import math
2
3def is_prime(n: int) -> bool:
4    if n < 2:
5        return False
6    if n % 2 == 0:
7        return n == 2
8    limit = int(math.isqrt(n))
9    for d in range(3, limit + 1, 2):
10        if n % d == 0:
11            return False
12    return True

Trial division is useful for learning and small inputs, but cumulative cost grows quickly.

2) Sieve of Eratosthenes approach

python
1import math
2
3def first_n_primes(n: int):
4    # upper bound for nth prime (valid for n >= 6)
5    if n < 6:
6        upper = 15
7    else:
8        upper = int(n * (math.log(n) + math.log(math.log(n)))) + 10
9
10    while True:
11        sieve = bytearray(b"\x01") * (upper + 1)
12        sieve[0:2] = b"\x00\x00"
13        for p in range(2, int(math.isqrt(upper)) + 1):
14            if sieve[p]:
15                sieve[p*p:upper+1:p] = b"\x00" * (((upper - p*p) // p) + 1)
16
17        primes = [i for i, flag in enumerate(sieve) if flag]
18        if len(primes) >= n:
19            return primes[:n]
20        upper *= 2

For 10,000 primes, this runs fast and avoids repeated primality checks.

3) Complexity and memory

Sieve runtime is roughly O(n log log n) for numbers up to limit, with linear memory in the chosen range. For this problem size, memory is modest and performance is strong.

4) Practical optimization notes

  • use math.isqrt for integer square roots,
  • represent sieve in bytearray for compact storage,
  • pre-size with known bounds and retry if needed.

5) Validation

python
1primes = first_n_primes(10000)
2assert len(primes) == 10000
3assert primes[0] == 2
4assert primes[-1] == 104729

Simple assertions catch off-by-one and bound issues quickly.

6) Production checklist for prime generation performance

Code examples are necessary, but production readiness depends on how this pattern behaves under failure, load, and operational drift. Before rollout, define success criteria that are measurable. A useful baseline is three metrics: correctness (for example, expected output match rate), reliability (error rate and retry behavior), and latency (p95 or p99 execution time). Capture these metrics in a repeatable test environment rather than relying on ad hoc local runs. If external systems are involved, include at least one synthetic fault scenario such as timeout, malformed payload, or temporary dependency outage. This confirms the implementation fails predictably and recovers in a controlled way.

Document environment assumptions close to the code. Include runtime version constraints, required environment variables, and exact dependency versions used during validation. Many regressions come from mismatched environments rather than algorithmic changes. A short README snippet or inline comment that names these assumptions can prevent repeated troubleshooting later. Also define ownership for operational issues: who receives alerts, what threshold triggers action, and what rollback path is acceptable. Without explicit ownership and rollback criteria, otherwise small incidents can take longer to resolve.

A practical rollout sequence is:

  1. Run automated checks (lint, unit tests, static validation) in CI.
  2. Execute a smoke test against representative input sizes.
  3. Validate one failure mode and verify error visibility in logs.
  4. Deploy behind a feature flag or phased rollout if possible.
  5. Monitor key metrics for a defined stabilization window.
bash
1# Example operator workflow
2make lint
3make test
4./scripts/smoke_check.sh

Finally, keep a short limitations section. State what the current approach intentionally does not optimize or support. This prevents accidental misuse by future contributors and keeps design discussions grounded in explicit tradeoffs. For long-lived systems, schedule periodic review of this implementation, especially after runtime upgrades or library changes. A lightweight maintenance cadence often catches compatibility issues before they become production incidents.

Common Pitfalls

  • Assuming a too-small upper bound and returning fewer primes than requested.
  • Forgetting to mark 0 and 1 as non-prime.
  • Recomputing square roots or ranges unnecessarily inside hot loops.
  • Using Python lists of booleans when compact arrays are more memory-efficient.
  • Not validating the final count and last prime for correctness.

Summary

For the first 10,000 primes, a sieve-based implementation is usually the best balance of speed, simplicity, and reliability. Trial division remains useful for checks or tiny workloads, but full generation should prefer the sieve. With robust bounds and basic assertions, prime generation code becomes both fast and easy to trust.


Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.