0

I am trying to find the 10001st prime using a basic logic: 1) Identify if a number is prime 2) Add it to a list if prime 3) Print the 10001st term in the list

Here is my code:

primelist=[]
import math
i=2
while True:
    for x in range(2, int(math.sqrt(i))):
        if i % x == 0:
            continue
        else:
            primelist.append(i)
    if len(primelist)== 10001:
        break 
print(primelist[-1])

Is the logic or code fundamentally wrong or inefficient?

What can I do to ameliorate/ make it work?

EDIT I have incremented i (i+=1) and used all() to check if everything was indivisible, in response to comments

primelist=[2]
import math
i=3
while True:
    for x in range(2, int(i**(1/2))):
        if all(i%x!=0 for x in range(2, int(math.sqrt(i)))):
            primelist.append(i)
    i+=1
    if len(primelist)== 10001:
        break
print(primelist[-1])
S.L
  • 175
  • 2
  • 12
  • @ShagunSodhani does that mean if i leave it for a while it will come out with the answer? Also im not trying to use sieve method... since i dont know how big 10001st prime is in the first place – S.L Apr 30 '17 at 12:33
  • 1
    It's wrong. **1** You never increment `i`. **2** Your code appends `i` to the list as soon as it finds an `x` that doesn't divide it. You should only do that after you've checked all the potential factors of `i`. And once you've fixed those issues there are various ways you can make the code more efficient. Eg, put 2 in `primelist` and thereafter forget about even numbers. – PM 2Ring Apr 30 '17 at 12:33
  • Thanks @PM2Ring for pointing out the mistakes that I didnt notice in haste. Shawn Li you may still use [this](http://stackoverflow.com/questions/37312540/finding-the-10001st-prime-number-in-python) to optimise your code. – Shagun Sodhani Apr 30 '17 at 12:37
  • I just noticed that your `x` range is too small: you need to go the next integer _after_ the square root. Eg, `range(3, int(i**.5) + 1, 2)`. And you can increment `i` by 2. – PM 2Ring Apr 30 '17 at 12:51
  • @PM2Ring doesn't python automatically do that, round up if you use the built in `int()` ? – S.L Apr 30 '17 at 12:52
  • No, `int` always rounds towards zero. `int(3.9)==3`, `int(-3.9)==-3` – PM 2Ring Apr 30 '17 at 12:54
  • 1
    Since you're now using `all` you need to get rid of that outer `for` loop. – PM 2Ring Apr 30 '17 at 13:00
  • @PM2Ring thank you very much – S.L Apr 30 '17 at 13:45
  • Not a problem. FWIW, I've just posted a more efficient version of your code, as well as a much faster version that uses sieving. – PM 2Ring May 01 '17 at 10:46
  • Could you explain what the question title has to do with the code shown in the question? – mkrieger1 May 01 '17 at 10:47
  • @mkrieger1 The original code (in the 1st code block) doesn't print anything, which the OP found puzzling. Of course, the reason it doesn't print is because it gets stuck in an infinite loop. But I agree that the question's title should be improved. What do you suggest? – PM 2Ring May 01 '17 at 10:50
  • My suggestion: "Prime number calculator produces no output" – PM 2Ring May 01 '17 at 10:53
  • Changed the title! – S.L May 01 '17 at 13:25

3 Answers3

1

This one should work if you want to stay with your algorithm:

import math

def is_prime(n):
    for x in range(2, int(math.sqrt(n)) + 1):
        if n % x == 0:
            return False
    return True

primelist = []
i = 2

while True:
    if is_prime(i) is True:
        primelist.append(i)
    i += 1
    if len(primelist) == 10001:
        break
print(primelist[-1])
Gleb Ignatev
  • 95
  • 1
  • 7
1

Keep the algorithm/code structure the same (no optimization done), so we can easily share several language points, please see inline comments:

primelist=[]
import math
i=2
while True:
    #changed to sqrt + 1, the second parameter of range is not inclusive, 
    #by adding 1, we make sure sqrt itself is included
    for x in range(2, int(math.sqrt(i) + 1)):
        if i % x == 0:
            #you want break not continue, continue will try 
            #next possible factor, since you already figured out that this 
            #is not a prime, no need to keep trying 
            #continue
            break
    #a for/else block, many python users never encountered this python
    #syntax. The else block is only triggered, if the for loop is naturally
    #completed without running into the break statement
    else:
        #a little debugging, to visually confirm we generated all primes
        print("adding {}".format(i))
        primelist.append(i)
    if len(primelist)== 11:
        break 
    #advance to the next number, this is important, 
    #otherwise i will always be 2
    i += 1
print(primelist[-1])

If you would like to optimize the algorithm, search online for "prime sieve".

Peter Pei Guo
  • 7,536
  • 17
  • 32
  • 51
0

Here's a version of your code that's a bit faster, and which doesn't use much RAM. We really don't need to build a list of the primes we find. We don't use the numbers in that list to find more primes, and we're really only interested in its length. So instead of building a list we merely keep count of the primes we find.

# Include 2 in the count
count = 1
i = 3
while True:
    if all(i % x for x in range(3, int(i ** 0.5) + 1, 2)):
        count += 1
    if count == 10001:
        break
    i += 2

print(i)

FWIW, here's a faster solution that uses sieving. We estimate the required size of the sieve using the prime number theorem. Wikipedia gives these bounds for p(n), the n'th prime number, which is valid for n >= 6:

log(n) + log(log(n)) - 1 < p(n) / n < log(n) + log(log(n))

We use the upper bound as the highest number in the sieve. For n < 6 we use a hard-coded list.

To save space, this sieve only holds odd numbers, so we treat the case of p(1) == 2 as a special case.

#!/usr/bin/env python3

''' Use a sieve of Eratosthenes to find nth prime

    Written by PM 2Ring 2017.05.01
    Sieve code derived from primes1 by Robert William Hanks
    See http://stackoverflow.com/a/3035188/4014959
'''

from math import log
from sys import argv

def main():
    num = int(argv[1]) if len(argv) > 1 else 10001

    if num == 1:
        # Handle 2 as a special case
        print(1, 2)
        return
    elif num < 6:
        # Use a pre-built table for (3, 5, 7, 11)
        primes = [1, 1, 1, 1, 0, 1]
    else:
        # Compute upper bound from Prime number theorem
        x = log(num)
        hi = int(num * (x + log(x)))
        print('upper bound', hi)

        # Create a boolean list of odd primes in range(hi)
        primes = [True] * (hi//2)
        for i in range(3, 1 + int(hi**0.5), 2):
            if primes[i//2]:
                primes[i*i//2::i] = [False] * ((hi - i*i - 1) // (2*i) + 1)

    # Count the primes until we get the nth one
    k = 0
    for i, b in enumerate(primes):
        if b:
            k += 1
            if k == num:
                break

    print(num, 2*i+1)


if __name__ == "__main__":
    main()

This code finds p(10001) = 104743 in under 0.15 seconds on my old single core 32 bit 2GHz machine running Python 3.6.0. It finds p(500000) = 7368787 in about 2.2 seconds.

PM 2Ring
  • 50,023
  • 5
  • 64
  • 150