Home DSA Miller-Rabin Primality Test: Probabilistic Prime Detection Explained
Intermediate 3 min · July 14, 2026

Miller-Rabin Primality Test: Probabilistic Prime Detection Explained

Master the Miller-Rabin primality test: a probabilistic algorithm for efficient prime detection.

N
Naren Founder & Principal Engineer

20+ years shipping performance-critical code where algorithms decide the bill. Drawn from code that ran under real load.

Follow
Production
production tested
July 15, 2026
last updated
2,398
articles · all by Naren
Before you start⏱ 15-20 min read
  • Basic understanding of prime numbers and modular arithmetic.
  • Familiarity with big-O notation and algorithm analysis.
  • Experience with Python or another programming language.
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • Miller-Rabin is a probabilistic primality test that determines if a number is composite or probably prime.
  • It uses modular exponentiation and witness checks to detect composites with high accuracy.
  • For k iterations, the error probability is at most 4^(-k), making it practical for large numbers.
  • It is widely used in cryptography (e.g., RSA key generation) and competitive programming.
  • Deterministic variants exist for numbers up to certain bounds using specific witness sets.
✦ Definition~90s read
What is Miller-Rabin Primality Test?

Miller-Rabin is a probabilistic algorithm that quickly determines whether a given number is composite or probably prime, using modular exponentiation and random bases.

Imagine you're a detective trying to find if a number is prime.
Plain-English First

Imagine you're a detective trying to find if a number is prime. Instead of checking every possible divisor (which takes forever for large numbers), you ask a few clever questions that can catch composites almost every time. If the number passes all your questions, it's probably prime. Miller-Rabin is like that detective: it uses math tricks to quickly rule out composite numbers with very high confidence.

Prime numbers are the building blocks of number theory and modern cryptography. From RSA encryption to hash functions, the ability to quickly determine whether a large number is prime is crucial. But for numbers with hundreds of digits, deterministic primality tests like trial division or the AKS algorithm are either too slow or too complex. Enter the Miller-Rabin primality test: a probabilistic algorithm that trades a tiny chance of error for blazing speed.

Developed by Gary L. Miller and Michael O. Rabin, this test is the workhorse of primality testing in practice. It works by checking whether the number satisfies certain properties of prime numbers. If it fails any test, it's definitely composite. If it passes many tests, it's 'probably prime' with an error probability that can be made arbitrarily small.

In this tutorial, you'll learn the mathematics behind Miller-Rabin, how to implement it in Python, and how to use it in production systems. We'll also cover common pitfalls, debugging strategies, and interview questions. By the end, you'll be able to confidently apply this algorithm to real-world problems.

Mathematical Foundation

The Miller-Rabin test is based on two properties of prime numbers. Let n be an odd prime, and write n-1 = 2^s d, where d is odd. Then for any integer a not divisible by n, either a^d ≡ 1 (mod n) or a^(2^r d) ≡ -1 (mod n) for some 0 ≤ r < s. This is a consequence of Fermat's little theorem and the fact that if x^2 ≡ 1 (mod p) then x ≡ ±1 (mod p) for prime p.

If n is composite, there exists at least one a (called a witness) for which neither condition holds. The algorithm picks random a's and checks these conditions. If any a fails, n is composite. If all pass, n is probably prime.

The probability that a composite number passes a single test is at most 1/4. By repeating k times, the error probability drops to 4^(-k). For cryptographic applications, k=20 is common, giving an error probability of 4^(-20) ≈ 10^(-12).

miller_rabin_core.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def miller_rabin_test(n, a):
    # Write n-1 as 2^s * d
    s = 0
    d = n - 1
    while d % 2 == 0:
        s += 1
        d //= 2
    x = pow(a, d, n)
    if x == 1 or x == n - 1:
        return True  # probably prime
    for _ in range(s - 1):
        x = pow(x, 2, n)
        if x == n - 1:
            return True
    return False  # composite
🔥Why the conditions work
📊 Production Insight
In production, always use the built-in pow(a, d, n) for modular exponentiation; it's optimized and avoids overflow.
🎯 Key Takeaway
The test checks for nontrivial square roots of 1 modulo n, which only exist for composite numbers.

Full Algorithm Implementation

The full Miller-Rabin algorithm repeats the test with multiple bases. For random bases, we choose k random integers a in [2, n-2]. For deterministic variants, we use a fixed set of bases that are known to work for numbers up to a certain bound.

miller_rabin_full.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import random

def is_prime_miller_rabin(n, k=10):
    if n < 2:
        return False
    if n == 2 or n == 3:
        return True
    if n % 2 == 0:
        return False
    # Write n-1 as 2^s * d
    s = 0
    d = n - 1
    while d % 2 == 0:
        s += 1
        d //= 2
    # Witness loop
    for _ in range(k):
        a = random.randrange(2, n - 1)
        x = pow(a, d, n)
        if x == 1 or x == n - 1:
            continue
        for _ in range(s - 1):
            x = pow(x, 2, n)
            if x == n - 1:
                break
        else:
            return False  # composite
    return True  # probably prime
💡Deterministic bases for n < 2^64
📊 Production Insight
In production, use a deterministic set of bases for numbers within a known range to avoid randomness and guarantee correctness.
🎯 Key Takeaway
Always handle small primes and even numbers separately before the main loop.

Optimization Techniques

Performance is critical when testing many large numbers. Key optimizations include: - Precompute s and d once per n. - Use early exit for small n or even numbers. - For repeated tests on the same n, cache results. - Use bit operations for division by 2. - In Python, the built-in pow() is highly optimized in C.

For bulk primality testing (e.g., generating many primes), consider using a sieve to eliminate obvious composites first.

optimized_miller_rabin.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def is_prime_optimized(n, bases):
    if n < 2:
        return False
    if n in (2, 3):
        return True
    if n % 2 == 0:
        return False
    s = (n - 1) & - (n - 1)  # lowest set bit
    d = (n - 1) // s
    s = s.bit_length() - 1
    for a in bases:
        if a % n == 0:
            continue
        x = pow(a, d, n)
        if x == 1 or x == n - 1:
            continue
        for _ in range(s - 1):
            x = pow(x, 2, n)
            if x == n - 1:
                break
        else:
            return False
    return True
⚠ Avoid common pitfalls
📊 Production Insight
For cryptographic applications, use a cryptographically secure random number generator for base selection.
🎯 Key Takeaway
Optimizations like bit operations and early exits can significantly speed up the test.

Error Probability and Iteration Count

The error probability after k iterations is at most 4^(-k). However, this bound is worst-case; for most composites, the probability is much lower. In practice, k=10 gives an error probability of about 10^(-6), which is acceptable for many non-cryptographic applications. For cryptography, k=20 or more is standard.

Note that the test only errs in one direction: it may call a composite prime, but never calls a prime composite. This makes it suitable for applications where false positives are tolerable but false negatives are not.

error_probability.pyPYTHON
1
2
3
4
5
6
# Example: compute error probability for k iterations
def error_prob(k):
    return 4**(-k)

for k in [1, 5, 10, 20, 40]:
    print(f"k={k}: error <= {error_prob(k):.2e}")
Output
k=1: error <= 2.50e-01
k=5: error <= 9.77e-04
k=10: error <= 9.54e-07
k=20: error <= 9.09e-13
k=40: error <= 8.27e-25
🔥Choosing k
📊 Production Insight
Always document the chosen k and the corresponding error bound in your codebase.
🎯 Key Takeaway
The error probability decreases exponentially with the number of iterations, making the test highly reliable with modest k.

Deterministic Variants

For numbers up to certain bounds, we can use a fixed set of bases that make the test deterministic. These sets have been proven to work for all numbers below the bound. For example: - n < 2^32: bases [2, 7, 61] - n < 2^64: bases [2, 325, 9375, 28178, 450775, 9780504, 1795265022] (or the smaller set [2, 3, 5, 7, 11, 13, 17]) - n < 3,317,044,064,679,887,385,961,981: bases [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]

Using these sets eliminates randomness and guarantees correctness.

deterministic_miller_rabin.pyPYTHON
1
2
3
4
5
6
7
8
9
10
def is_prime_deterministic(n):
    if n < 2:
        return False
    small_primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]
    for p in small_primes:
        if n % p == 0:
            return n == p
    # Use deterministic bases for n < 2^64
    bases = [2, 325, 9375, 28178, 450775, 9780504, 1795265022]
    return is_prime_optimized(n, bases)
💡When to use deterministic
📊 Production Insight
In production, combine deterministic tests for small numbers with probabilistic tests for larger ones.
🎯 Key Takeaway
Deterministic variants provide 100% accuracy for numbers within known bounds.

Real-World Applications

Miller-Rabin is used in
  • Cryptography: generating large primes for RSA, Diffie-Hellman, and DSA.
  • Random number generation: testing for primality in algorithms that require primes.
  • Competitive programming: solving problems that involve prime numbers up to 10^12 or more.
  • Scientific computing: number theory research and simulations.

In each case, the speed of Miller-Rabin makes it the go-to choice over deterministic tests like AKS.

rsa_prime_generation.pyPYTHON
1
2
3
4
5
6
7
8
9
import random

def generate_large_prime(bits):
    while True:
        n = random.getrandbits(bits)
        # Ensure n is odd and has the high bit set
        n |= (1 << (bits - 1)) | 1
        if is_prime_miller_rabin(n, k=20):
            return n
🔥RSA key generation
📊 Production Insight
Always use a cryptographically secure random generator (e.g., secrets.randbits) for key generation.
🎯 Key Takeaway
Miller-Rabin is essential for generating secure cryptographic keys efficiently.
● Production incidentPOST-MORTEMseverity: high

The RSA Key Generation Disaster: When a Composite Slipped Through

Symptom
Users reported that encrypted messages could be decrypted by multiple different public keys, and some keys were unexpectedly weak.
Assumption
The developer assumed that the built-in primality test in the crypto library was deterministic and infallible.
Root cause
The library used a fixed number of Miller-Rabin iterations (1) for performance, and a rare composite number passed the test, leading to generation of an RSA modulus with a composite factor.
Fix
Increased the number of Miller-Rabin iterations to 64 and added a secondary deterministic test for numbers below a threshold. Also implemented a fallback to the Baillie-PSW test.
Key lesson
  • Never trust a single primality test iteration; always use multiple rounds.
  • Understand the error bounds of probabilistic algorithms and set parameters accordingly.
  • Use deterministic tests for numbers within known bounds (e.g., < 2^64).
  • Always validate cryptographic parameters with multiple independent checks.
  • Document the assumptions and limitations of the algorithms you use.
Production debug guideSymptom to Action4 entries
Symptom · 01
A number that should be prime is reported as composite.
Fix
Check for integer overflow in modular exponentiation. Verify base selection is within range [2, n-2]. Ensure n is odd and > 2.
Symptom · 02
A composite number is reported as probably prime.
Fix
Increase the number of iterations k. For n < 2^64, use deterministic witness set. Verify that n is not a perfect square.
Symptom · 03
Performance is too slow for large numbers.
Fix
Optimize modular exponentiation using exponentiation by squaring. Use early exit if n is even or small. Consider using a faster language or GPU for bulk testing.
Symptom · 04
Memory usage spikes during testing.
Fix
Avoid storing large intermediate results. Use iterative modular exponentiation instead of recursion. Ensure no unnecessary object creation.
★ Quick Debug Cheat SheetCommon issues and immediate actions for Miller-Rabin implementation.
False composite
Immediate action
Check base selection
Commands
print(bases)
assert all(2 <= a <= n-2 for a in bases)
Fix now
Ensure bases are in [2, n-2] and n is odd.
False prime+
Immediate action
Increase iterations
Commands
k = 64
print(4**(-k))
Fix now
Use at least 20 iterations for cryptographic applications.
Slow performance+
Immediate action
Check modular exponentiation
Commands
timeit pow(a, d, n)
profile code
Fix now
Use built-in pow() with three arguments.
AlgorithmTypeTime ComplexityError ProbabilityPractical Use
Trial DivisionDeterministicO(√n)NoneSmall numbers (< 10^12)
Fermat TestProbabilisticO(k log n)Unbounded (Carmichael numbers)Not recommended alone
Miller-RabinProbabilisticO(k log^3 n)≤ 4^(-k)Most widely used
AKSDeterministicO(log^6 n)NoneTheoretical interest
⚙ Quick Reference
6 commands from this guide
FileCommand / CodePurpose
miller_rabin_core.pydef miller_rabin_test(n, a):Mathematical Foundation
miller_rabin_full.pydef is_prime_miller_rabin(n, k=10):Full Algorithm Implementation
optimized_miller_rabin.pydef is_prime_optimized(n, bases):Optimization Techniques
error_probability.pydef error_prob(k):Error Probability and Iteration Count
deterministic_miller_rabin.pydef is_prime_deterministic(n):Deterministic Variants
rsa_prime_generation.pydef generate_large_prime(bits):Real-World Applications

Key takeaways

1
Miller-Rabin is a fast probabilistic primality test with an error probability that can be made arbitrarily small.
2
Always handle edge cases (small numbers, even numbers) separately.
3
For numbers within known bounds, use deterministic bases for guaranteed correctness.
4
In production, choose the number of iterations based on the required confidence level.
5
Use the built-in pow() function for modular exponentiation to avoid overflow and improve performance.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
Implement the Miller-Rabin primality test in Python.
Q02SENIOR
Explain why Miller-Rabin is probabilistic and how the error probability ...
Q03SENIOR
How would you modify Miller-Rabin to be deterministic for 64-bit integer...
Q04SENIOR
Compare Miller-Rabin with the AKS primality test.
Q01 of 04SENIOR

Implement the Miller-Rabin primality test in Python.

ANSWER
Provide a function that takes n and k, handles edge cases, and returns True if n is probably prime. Include the decomposition of n-1 into 2^s * d and the witness loop.
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
What is the difference between Miller-Rabin and Fermat primality test?
02
Can Miller-Rabin be made deterministic?
03
How many iterations should I use?
04
What is the time complexity of Miller-Rabin?
05
Is Miller-Rabin used in practice?
N
Naren Founder & Principal Engineer

20+ years shipping performance-critical code where algorithms decide the bill. Drawn from code that ran under real load.

Follow
Verified
production tested
July 15, 2026
last updated
2,398
articles · all by Naren
🔥

That's Number Theory. Mark it forged?

3 min read · try the examples if you haven't

Previous
Extended Euclidean Algorithm: Bezout Identity and Modular Inverses
8 / 9 · Number Theory
Next
Euler's Totient Function: Properties and Applications