Here's a modified version of the range sieve program I linked to in the comments. It uses the prime number theorem to get an approximation of the density of primes near hi
and uses that density to estimate lo
such that the number of primes in range(lo, hi)
>= num
. For further info please see the Mathworld article on the Prime Counting Function.
Generally, there's no simple way to accurately estimate the number of primes in a given range. FWIW, in special cases it's easy to show that there are no primes in a given range, eg every number in range(n! + 2, n! + n + 1)
is clearly divisible by a number in range(2, n + 1)
.
The code below (hopefully :) ) over-estimates the required range. But we don't want it to waste time by using a value for lo
that's too low. In some cases, lo
won't be small enough, especially when num
is very small, but it should be ok if you use sensible values for num
.
#! /usr/bin/env python
''' Prime range sieve.
Written by PM 2Ring 2014.10.15
2015.05.24 Modified to find the `num` highest primes < `hi`
'''
import sys
from math import log
def potential_primes():
''' Make a generator for 2, 3, 5, & thence all numbers coprime to 30 '''
s = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29)
for i in s:
yield i
s = (1,) + s[3:]
j = 30
while True:
for i in s:
yield j + i
j += 30
def range_sieve(lo, hi):
''' Create a list of all primes in the range(lo, hi) '''
#Mark all numbers as prime
primes = [True] * (hi - lo)
#Eliminate 0 and 1, if necessary
for i in range(lo, min(2, hi)):
primes[i - lo] = False
ihi = int(hi ** 0.5)
for i in potential_primes():
if i > ihi:
break
#Find first multiple of i: i >= i*i and i >= lo
ilo = max(i, 1 + (lo - 1) // i ) * i
#Determine how many multiples of i >= ilo are in range
n = 1 + (hi - ilo - 1) // i
#Mark them as composite
primes[ilo - lo : : i] = n * [False]
return [i for i,v in enumerate(primes, lo) if v]
def main():
hi = int(sys.argv[1]) if len(sys.argv) > 1 else 1000000
num = int(sys.argv[2]) if len(sys.argv) > 2 else 1000
#Estimate required range using the prime number theorem
#The constant 1.25506 is from the MathWorld article on
#the Prime Counting Function
d = num * log(hi) * 1.25506
#An alternate estimator. Sometimes estimates a little too low.
#d = num * (log(hi) + log(log(hi)))
lo = max(2, hi - int(d))
print 'lo =', lo
primes = range_sieve(lo, hi)
print len(primes), 'primes found'
for i, p in enumerate(reversed(primes[-num:]), 1): print '%2d: %d' % (i, p)
if __name__ == '__main__':
main()
output when called with $ python range_sieve.py 1234567890 60
lo = 1234566314
67 primes found
1: 1234567811
2: 1234567801
3: 1234567799
4: 1234567783
5: 1234567759
6: 1234567723
7: 1234567717
8: 1234567679
9: 1234567669
10: 1234567583
11: 1234567571
12: 1234567553
13: 1234567517
14: 1234567469
15: 1234567361
16: 1234567337
17: 1234567309
18: 1234567303
19: 1234567291
20: 1234567289
21: 1234567267
22: 1234567261
23: 1234567249
24: 1234567223
25: 1234567181
26: 1234567171
27: 1234567151
28: 1234567129
29: 1234567097
30: 1234567093
31: 1234567069
32: 1234567063
33: 1234567007
34: 1234566997
35: 1234566959
36: 1234566953
37: 1234566947
38: 1234566899
39: 1234566887
40: 1234566863
41: 1234566769
42: 1234566761
43: 1234566737
44: 1234566713
45: 1234566691
46: 1234566677
47: 1234566643
48: 1234566601
49: 1234566581
50: 1234566559
51: 1234566547
52: 1234566533
53: 1234566527
54: 1234566521
55: 1234566499
56: 1234566497
57: 1234566491
58: 1234566481
59: 1234566479
60: 1234566467