With the Sieve of Eratosthenes I achieved very satisfactory results. Remember that as the sieve returns the list of all prime numbers smaller than the input value, we will have to enter the highest prime value that the program will accept. The statement says that it is guaranteed that a number will not be requested after 200,000 °, so we must first know what this value is: 2,750,159 .
A fairly simple implementation of the sieve is:
def sieve_of_eratosthene(N):
# Cria-se uma lista referente a todos os inteiros entre 0 e N:
A = [True] * (N+1)
# Define os números 0 e 1 como não primos:
A[0] = A[1] = False
# Percorra a lista até encontrar o primeiro número primo:
for value, prime in enumerate(A):
# O número é primo?
if prime:
# Retorna o número primo:
yield value
# Remova da lista todos os múltiplos do número enontrado:
for i in range(value**2, N+1, value):
A[i] = False
So, we can get all the first 200,000 prime numbers by doing:
primes = list(sieve_of_eratosthene(2750159))
And generate the output as follows:
print("Saída:", [primes[i] for i in [7, 1, 199999, 4]]) # Saída: [19, 3, 2750159, 11]
Being [7, 1, 199999, 4]
the program entry.
Note: The 199,999 prime number will be 2,750,131, as expected, only if the number 2 occupies the index 1. Considering that the implementation sets the index 0 to the value 2, the value expected on exit would be referring to the 199,998 ° index.
Using the timeit
module for time measurement, I get an average time of 100 executions of 0.69539769177063s
.
See working at Ideone .
A brief explanation of the code
The first line of the function, A = [True] * (N+1)
creates a list of N+1
elements, all set to True
, initially indicating that all values between 0 and N are prime numbers. Soon after, values 0 and 1 are defined as False
, indicating that these are not prime numbers. And with a loop of repetition, all other values are traversed, and whenever it finds a prime value, it returns it and removes from the list, defining as False
, all numbers that are multiples of that found prime. For a better view, the A
list would look something like:
A = [False, False, True, True, False, True, False, ...]
Indicating that 0 is not prime, 1 is not prime, 2 is prime, 3 is prime, 4 is not prime, 5 is prime, 6 is not prime, and so on. What the enumerate
function does is to return a pair of values where the first represents the index in the list and the second the value itself. So, doing enumerate(A)
would return something similar to:
[(0, False), (1, False), (2, True), (3, True), (4, False), (5, True), (6, False), ...]
And that's why in for
there are two values. The first value returned from enumerate
is assigned to value
and second to prime
, so when prime
is true, we know value
will be a prime number.
for value, prime in enumerate(A):
...
While yield
plays the role of return
, however, for a generator . I confess that I ended up complicating more than necessary in this code using a generator, because since the generator should be converted to a list, I could generate it directly. The code looks like this:
def sieve_of_eratosthene(N):
# Lista de números primos:
numbers = []
# Cria-se uma lista referente a todos os inteiros entre 0 e N:
A = [True] * (N+1)
# Define os números 0 e 1 como não primos:
A[0] = A[1] = False
# Percorra a lista até encontrar o primeiro número primo:
for value, prime in enumerate(A):
# O número é primo?
if prime:
# Retorna o número primo:
numbers.append(value)
# Remova da lista todos os múltiplos do número enontrado:
for i in range(value**2, N+1, value):
A[i] = False
return numbers
See working at Ideone .
Note that the main difference is that where before I used yield
, now I used append
of a list.
An extra implementation
As discussed by LINQ in its response, having to know the n -th prime value in advance can be considered a limitation of the solution. A practical alternative is to apply the mathematical concept presented at the end of this answer to calculate a prime value close to the desired one. Knowing that the n -th prime number, Pn, is less than n ln(n) + n ln(ln(n))
, if we calculate all the prime values up to this value we are sure that we will have calculated the value of Pn. Something like:
def sieve_of_eratosthene(N):
N = floor(N*log(N) + N*(log(log(N))))
# Lista de números primos:
numbers = []
# Cria-se uma lista referente a todos os inteiros entre 0 e N:
A = [True] * (N+1)
# Define os números 0 e 1 como não primos:
A[0] = A[1] = False
# Percorra a lista até encontrar o primeiro número primo:
for value, prime in enumerate(A):
# O número é primo?
if prime:
# Retorna o número primo:
numbers.append(value)
# Remova da lista todos os múltiplos do número enontrado:
for i in range(value**2, N+1, value):
A[i] = False
return numbers
See working at Ideone .
With the addition of the first line, we can now call the sieve_of_eratosthene(2e5)
function to get the first 200,000 prime numbers. In fact, you get even more, so the runtime can increase.
Possible alternative
It is mathematically proved, if I understand correctly, that the n -th prime number, being n greater than or equal to 6, will belong to the set of numbers defined by: / p>
For example, for n = 199999
, the following set is obtained:
2741586 < P_n < 2941585
That contains the expected value 2750159; however, this will not be the only prime number in this range, so the challenge following this logic would be to correctly identify the value within the range.