Reputation: 15814
Is there something wrong with my solution to find the largest prime less than n
when n
can be up to ~10^230? Are there any suggestions for a better approach?
Here is my attempt, using the following version of a Miller-Rabin primality test in Python:
from random import randrange
small_primes = [
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,101,103,107,109,113,
127,131,137,139,149,151,157,163,167,173,
179,181,191,193,197,199,211,223,227,229,
233,239,241,251,257,263,269,271,277,281,
283,293,307,311,313,317,331,337,347,349,
353,359,367,373,379,383,389,397,401,409,
419,421,431,433,439,443,449,457,461,463,
467,479,487,491,499,503,509,521,523,541,
547,557,563,569,571,577,587,593,599,601,
607,613,617,619,631,641,643,647,653,659,
661,673,677,683,691,701,709,719,727,733,
739,743,751,757,761,769,773,787,797,809,
811,821,823,827,829,839,853,857,859,863,
877,881,883,887,907,911,919,929,937,941,
947,953,967,971,977,983,991,997
]
def probably_prime(n, k):
"""Return True if n passes k rounds of the Miller-Rabin primality
test (and is probably prime). Return False if n is proved to be
composite.
"""
if n < 2: return False
for p in small_primes:
if n < p * p: return True
if n % p == 0: return False
r, s = 0, n - 1
while s % 2 == 0:
r += 1
s //= 2
for _ in range(k):
a = randrange(2, n - 1)
x = pow(a, s, n)
if x == 1 or x == n - 1:
continue
for _ in range(r - 1):
x = pow(x, 2, n)
if x == n - 1:
break
else:
return False
return True
I start by testing probably_prime(n)
where I decrement and test each value of n
until I get a "probably prime" number. When I test this on values of n
= ~10^230, I am finding primes about 20-30 numbers apart. After reading more about prime gap, my results seem highly unlikely, as I should not be finding primes so frequently. I have tested k
values of up to 50,000, and I am getting the same answer. What am I doing wrong, and are there any suggestions for a better solution?
Upvotes: 2
Views: 188
Reputation: 353209
You're right, your code seems to be having difficulty as soon as it gets beyond the small_primes
table. Looking more closely, there's an error here:
for _ in range(r - 1):
x = pow(x, 2, n)
if x == n - 1:
break
else:
return False
You want to return False
(i.e. composite) if you never find x == n-1
(or you can short-circuit and return False
if x == 1
, I think: see here). This can be done simply by changing the indentation:
for _ in range(r - 1):
x = pow(x, 2, n)
if x == n - 1:
break
else:
return False
(The for/else
combination is really for/if-not-break
.)
After making this change, I get:
>>> sum(orig(p, 20) for p in range(10**6, 2*10**6))
54745
>>> sum(fixed(p, 20) for p in range(10**6, 2*10**6))
70435
>>> sum(orig(p, 20) for p in range(10**230, 10**230+10**3))
40
>>> sum(fixed(p, 20) for p in range(10**230, 10**230+10**3))
2
which is correct.
Upvotes: 5