Optimizing Primality Testing for Large Inputs: From Trial Division to Miller-Rabin in TheAlgorithms/Python
TheAlgorithms/Python scales primality testing from small integers to 1024-bit cryptographic numbers by combining deterministic trial division with 6k ± 1 optimization, the Sieve of Eratosthenes for bulk queries, and a hybrid pipeline of low-prime filtering with Miller-Rabin tests.
Optimizing primality testing for large inputs requires balancing mathematical rigor with computational efficiency. TheAlgorithms/Python repository demonstrates this progression through mathematically-focused modules that handle everything from quick checks on small integers to cryptographically secure prime generation. The implementations progress from simple O(√n) trial division to sophisticated probabilistic algorithms that remain tractable even for numbers exceeding 2⁵⁰⁰.
Input Validation and Early-Exit Strategies
Before executing heavy computation, the repository rejects invalid inputs immediately to avoid wasted cycles. In maths/prime_check.py, the is_prime function validates types and ranges at lines 42‑45, raising a ValueError for non-integer or negative inputs. This early-exit pattern ensures that subsequent algorithms operate only on valid positive integers greater than or equal to 2.
Trial Division with 6k ± 1 Optimization
For small-to-medium inputs (roughly n < 10¹²), the repository relies on optimized trial division rather than complex probabilistic tests. The is_prime function in maths/prime_check.py (lines 46‑57) implements the classic 6k ± 1 optimization, which exploits the fact that all primes greater than 3 must be of the form 6k ± 1.
The implementation skips multiples of 2 and 3 entirely, iterating only through candidate divisors in that arithmetic progression:
# From maths/prime_check.py - lines 53-56
for i in range(5, int(math.sqrt(number)) + 1, 6):
if number % i == 0 or number % (i + 2) == 0:
return False
This approach reduces the number of modulus operations by approximately two-thirds compared to naive trial division, achieving O(√n) complexity while maintaining deterministic correctness for all inputs within the 64-bit range.
Bulk Prime Generation with the Sieve of Eratosthenes
When an application requires testing many numbers against a shared upper bound, the repository uses the Sieve of Eratosthenes to amortize costs. The prime_sieve_eratosthenes function in maths/prime_sieve_eratosthenes.py generates a complete list of primes up to n in O(n log log n) time using a boolean array to mark composites.
This deterministic method is ideal for pre-computing prime tables or handling tiny inputs (n < 10⁶) efficiently. The module guards interactive I/O within if __name__ == "__main__" blocks (lines 48‑55) to ensure the sieve remains a pure function with no overhead when imported.
Deterministic Miller-Rabin for Large Numbers
For numbers up to 3.3 × 10²⁴, the repository provides a deterministic Miller-Rabin implementation in ciphers/deterministic_miller_rabin.py (lines 6‑88). This algorithm guarantees correctness without randomness by selecting specific pre-computed bases derived from established mathematical bounds.
The miller_rabin function chooses a subset of small primes as witnesses based on the input size, running the strong probable prime test deterministically. This bridges the gap between the efficiency of probabilistic methods and the certainty of exhaustive trial division for cryptographic-scale integers that fit within standard integer types.
Probabilistic Miller-Rabin and Hybrid Filtering
When inputs exceed the deterministic bound or require arbitrary precision, the repository falls back to probabilistic Miller-Rabin with an additional optimization layer. The ciphers/rabin_miller.py module implements a hybrid strategy that combines fast pre-filtering with statistical testing:
-
Low-prime table filtering: Before invoking expensive modular exponentiation,
is_prime_low_num(lines 28‑35 and 94‑100) checks divisibility against a hard-coded table of approximately 200 small primes. This eliminates the vast majority of composite candidates instantly. -
Random-base testing: For surviving candidates,
rabin_miller(lines 6‑25) selects 5 random bases and performs the strong probable prime test, offering a configurable false-positive rate (typically 4⁻⁵) with no false negatives. -
Large-prime generation: The
generate_large_primefunction (lines 13‑18) generates random k-bit integers and tests them with the hybrid pipeline until finding a prime. Because prime density near 2ᵏ is approximately 1/(k·ln 2), the expected number of trials remains low even for 1024-bit keys.
Algorithm Selection by Input Size
According to the TheAlgorithms/Python source code, the repository organizes its primality testing into three performance tiers:
- Tiny inputs (n < 10⁶): Use
prime_sieve_eratosthenesfor bulk generation oris_primefor individual checks. - Medium inputs (10⁶ ≤ n < 10¹²): Rely on the 6k ± 1 trial division in
maths/prime_check.py. - Large cryptographic inputs (n ≥ 2⁵⁰⁰): Employ the hybrid pipeline in
ciphers/rabin_miller.py, guaranteeing no false negatives with a configurable false-positive rate.
Practical Code Examples
# 1. Simple deterministic check for small numbers
from maths.prime_check import is_prime
print(is_prime(563)) # → True
print(is_prime(67483)) # → False
# 2. Generate all primes ≤ 100 using the Eratosthenes sieve
from maths.prime_sieve_eratosthenes import prime_sieve_eratosthenes
print(prime_sieve_eratosthenes(100))
# → [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]
# 3. Deterministic Miller‑Rabin for numbers below 3.3e24
from ciphers.deterministic_miller_rabin import miller_rabin
print(miller_rabin(3_215_031_751)) # → True
print(miller_rabin(3_215_031_753)) # → False
# 4. Probabilistic Miller‑Rabin / large‑prime generation (cryptographic size)
from ciphers.rabin_miller import generate_large_prime
prime_1024 = generate_large_prime(keysize=1024) # ~300‑digit prime
print(prime_1024)
# True – verified by the internal Rabin‑Miller test
# 5. Hybrid approach: fast filter + deterministic test
from ciphers.rabin_miller import is_prime_low_num
print(is_prime_low_num(2_152_302_898_747)) # → True (deterministic)
print(is_prime_low_num(2_152_302_898_749)) # → False
Summary
- Early validation in
maths/prime_check.pyprevents wasted computation on invalid inputs. - The 6k ± 1 optimization reduces trial division costs by 66% for medium-sized numbers.
- The Sieve of Eratosthenes provides efficient bulk prime generation in O(n log log n) time.
- Deterministic Miller-Rabin guarantees correctness for numbers below 3.3 × 10²⁴ using pre-computed bases.
- Probabilistic Miller-Rabin with low-prime pre-filtering handles arbitrary-precision cryptographic inputs with configurable accuracy.
Frequently Asked Questions
What is the 6k ± 1 optimization in primality testing?
The 6k ± 1 optimization exploits the fact that all prime numbers greater than 3 must be of the form 6k ± 1, meaning they are adjacent to multiples of 6. In maths/prime_check.py, the algorithm skips multiples of 2 and 3 entirely, iterating through range(5, int(sqrt(n)) + 1, 6) and testing only i and i + 2. This reduces the number of division operations by approximately two-thirds compared to naive trial division.
When should I use deterministic versus probabilistic Miller-Rabin?
Use deterministic Miller-Rabin (from ciphers/deterministic_miller_rabin.py) when your input is guaranteed to be less than 3.3 × 10²⁴ and you require absolute certainty without the risk of probabilistic false positives. Use probabilistic Miller-Rabin (from ciphers/rabin_miller.py) for arbitrary-precision integers exceeding that bound, or when generating large cryptographic primes where the deterministic bound does not apply.
How does the low-prime filter improve performance for large numbers?
The low-prime filter in ciphers/rabin_miller.py (lines 28‑35) checks divisibility against the first ~200 small primes before invoking the expensive Miller-Rabin modular exponentiation. Because the vast majority of random large integers are divisible by small primes, this filter eliminates composites instantly without costly computation, making the hybrid approach significantly faster than pure Miller-Rabin for large inputs.
What is the maximum number size supported by deterministic Miller-Rabin in TheAlgorithms/Python?
The deterministic Miller-Rabin implementation in ciphers/deterministic_miller_rabin.py supports numbers below 3.3 × 10²⁴ (approximately 2⁸⁰). For inputs exceeding this bound, the repository recommends the probabilistic variant in ciphers/rabin_miller.py, which uses random bases and accepts a configurable negligible error probability.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →