HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonModerate

Checking for the nth prime number between 1 and 1,000,000, must compute 1,000,000th in under 3 minutes

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
nththenumber000thmustcompute000minutescheckingunder

Problem

My mission is as the title states.
Here is the original code, before optimizing slightly (but still not enough).
Revised code is in my answer, and it still needs work.

Also, I want to write this without having to import any libraries. Time is only there for me to test, of course, time.

Here's the original code, before optimizing:

print("Enter a number n, 1-1,000,000 to find the nth prime number.")
goal=int(input("Number: "))

if goal>1000000:         #closes the program if the number doesn't fit the parameters
    exit()
if goal==1:              #takes care of the first prime, which is 2
    print("2")
if goal==2:              #and then the second, which is 3
    print("3")

chk=5         #the next odd number after 3, which is already taken care of above
primes=2    #the number of primes recorded, after counting 2 and 3.
x=0         #the last recorded prime number
while primes9......
        odds=3         #check to see if it's divisible by the smallest odd number
        sqrtk=chk**.5  #all the way up to the square root of k

        while oddssqrtk:             #if k isn't divisible by any r       
                primes+=1        #we have a prime number
                x=chk         #record that number
                chk+=2        #check the next odd number to see if it's prime
                odds=3         #and reset r back to 3 to use it next time

if primes==goal:                  #once we've reached the desired amount of prime numbers
    print("The",goal,"prime number is:",x)      #print that prime number

Solution

Style (and related tools)

As described in Vogel612's answer, many things are wrong concerning the style. You can use pep8 (or its online version) to check it automatically and autopep8 to try to fix this automatically. You'll find various other convenient tools to check your code such as pylint, pyflakes or pychecker.

Separation of concerns and code organisation

You could create a function to look for the prime number and then call it when you want to print the result.

You'd get something like :

def get_nth_prime(goal):
    if goal > 1000000:
        return None
    if goal == 1:  # first prime is 2
        return 2
    if goal == 2:  # second prime is 3
        return 3

    chk = 5  # the next odd number after 3, which is already taken care of above
    primes = 2  # the number of primes recorded, after counting 2 and 3.
    while primes 9......
            odds = 3  # check to see if it's divisible by the smallest odd number
            sqrtk = chk ** .5  # all the way up to the square root of chk

            while odds  sqrtk:
                primes += 1  # we have a prime number
                chk += 2  # check the next odd number to see if it's prime
                odds = 3  # and reset odds back to 3 to use it next time

    if primes == goal:  # once we've reached the desired amount of prime numbers
        return chk - 2

goal = int(input("Enter a number n, 1-1,000,000 to find the nth prime number."))
print("The", goal, "prime number is:", get_nth_prime(goal))


Also, the usage is to write your code "that actually does stuff" behind an if main guard so that you can import your code if you want to re-use your function without having any polluting. In your case, it becomes :

def main():
    """Main function"""
    goal = int(input("Enter a number n, 1-1,000,000 to find the nth prime number."))
    print("The", goal, "prime number is:", get_nth_prime(goal))

if __name__ == "__main__":
    main()


Testing

Having extracted a function to get the n-th prime number, you can more easily write test. You can play it simple, manually :

assert get_nth_prime(1) == 2
assert get_nth_prime(2) == 3


Or you can go further :

assert [get_nth_prime(i) for i in range(1, 100)] == [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]


Now that we have test cases, rewriting the code is much easier.

A bit of logic

You can rewrite while odds < sqrtk or odds == sqrtk: : while odds <= sqrtk:

Then you can rewrite this using range : while odds < sqrtk or odds == sqrtk: and then the second test can be written using the infamous else on the for loop (the best way to understand it is to read it as nobreak). The code becomes :

for odds in range(3, int(chk ** .5) + 1, 2):
            if chk % odds == 0:  # ...chk is divisible by this odd number....
                chk += 2  # then raise the value of chk to the next odd number
                break  # and start the check from the beginning with next k
        else: # no break => no divisors => prime
            primes += 1  # we have a prime number
            chk += 2  # check the next odd number to see if it's prime


The check if chk<9 looks like a premature optimisation to me. We can get rid of it.

Then you have :

while primes  no divisors => prime
        primes += 1  # we have a prime number
        chk += 2  # check the next odd number to see if it's prime


Then you have chk += 2 that appear in two different places : let's move it to a single place :

while primes  no divisors => prime
        primes += 1
    chk += 2

if primes == goal:  # once we've reached the desired amount of prime numbers
    return chk - 2


There is no need to compare primes and goal at every single iteration as it won't be updated that often : you can move this to the place where primes is increased :

chk = 5  # the next odd number after 3, which is already taken care of above
primes = 2  # the number of primes recorded, after counting 2 and 3.
while True:
    for odds in range(3, int(chk ** .5) + 1, 2):
        if chk % odds == 0:  # ...chk is divisible by this odd number....
            break
    else: # no break => no divisors => prime
        primes += 1
        if primes == goal:
            return chk
    chk += 2


More functions

The test of primality can easily be written in a function on its own.

Luckily, I have this ready in my toolbox :

```
def is_prime(n):
"""Checks if a number is prime."""
if n 1000000:
return None
if goal == 1: # first prime is 2
ret

Code Snippets

def get_nth_prime(goal):
    if goal > 1000000:
        return None
    if goal == 1:  # first prime is 2
        return 2
    if goal == 2:  # second prime is 3
        return 3

    chk = 5  # the next odd number after 3, which is already taken care of above
    primes = 2  # the number of primes recorded, after counting 2 and 3.
    while primes < goal:
        if chk < 9:  # if the number is odd and below 9....
            primes += 1  # it's prime
            chk += 2  # and check the next odd number

        else:  # if it's =>9......
            odds = 3  # check to see if it's divisible by the smallest odd number
            sqrtk = chk ** .5  # all the way up to the square root of chk

            while odds < sqrtk or odds == sqrtk:  # if at any time...
                if chk % odds == 0:  # ...chk is divisible by this odd number....
                    chk += 2  # then raise the value of chk to the next odd number
                    break  # and start the check from the beginning with next k
                else:  # if not...
                    # check to see if it's divisible by the next odd number.
                    odds += 2

            # if chk isn't divisible by any odds up to the square root of chk
            if odds > sqrtk:
                primes += 1  # we have a prime number
                chk += 2  # check the next odd number to see if it's prime
                odds = 3  # and reset odds back to 3 to use it next time


    if primes == goal:  # once we've reached the desired amount of prime numbers
        return chk - 2


goal = int(input("Enter a number n, 1-1,000,000 to find the nth prime number."))
print("The", goal, "prime number is:", get_nth_prime(goal))
def main():
    """Main function"""
    goal = int(input("Enter a number n, 1-1,000,000 to find the nth prime number."))
    print("The", goal, "prime number is:", get_nth_prime(goal))


if __name__ == "__main__":
    main()
assert get_nth_prime(1) == 2
assert get_nth_prime(2) == 3
assert [get_nth_prime(i) for i in range(1, 100)] == [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]
for odds in range(3, int(chk ** .5) + 1, 2):
            if chk % odds == 0:  # ...chk is divisible by this odd number....
                chk += 2  # then raise the value of chk to the next odd number
                break  # and start the check from the beginning with next k
        else: # no break => no divisors => prime
            primes += 1  # we have a prime number
            chk += 2  # check the next odd number to see if it's prime

Context

StackExchange Code Review Q#66473, answer score: 10

Revisions (0)

No revisions yet.