We can implement primality testing by repeatedly trying to divide the number n
by every possible factor. If n
is not divisible by any of the possible factors, that means it is prime.
def is_prime(n):
'''Return True iff n is prime
Arguments:
n -- a non-negative integer
'''
if n <= 1:
return False
if n == 2:
return True
#Try to compute the remainder of the division of n by (i+2)
#for i = 0, 1, 2, ..., n-1
#If the remainder is 0, return straight away
for i in range(2, n):
if n % i == 0:
return False
#Haven't returned False, which means we checked every possible i and haven't found a divisor
return True
Note that it's possible to not check for divisibility by larger factors. That's because if $n = a\times b$, then it must be the case that at least one of $a$ and $b$ is smaller than $\sqrt{n}$ (if both are larger that $\sqrt{n}$, the product would be larger than $n$).
That means that we can write the code as
def is_prime(n):
'''Return True iff n is prime
Arguments:
n -- a non-negative integer
'''
if n <= 1:
return False
if n == 2:
return True
#Try to compute the remainder of the division of n by (i+2)
#for i = 0, 1, 2, ..., int(sqrt(n)) + 1
#If the remainder is 0, return straight away
for i in range(2, int(n**0.5)+1): # need to potentially go up to sqrt(n) exactly
if n % i == 0:
return False
#Haven't returned False, which means we checked every possible i and haven't found a divisor
return True
Suppose that instead of the (correct) design above, we do something like this:
def is_prime_bad(n):
if n <= 1:
return False
if n == 2: #optional
return True
for i in range(2, n):
if n % i == 0:
return False
else:
return True
The difference is inside the for-loop: instead of only saying for n % i == 0: return False
we also added an else
clause.
This would create a problem:
is_prime_bad(35)
True
The reason is_prime_bad()
returns True
(even though $35=5\times 7$, and so it isn't prime) is the else
clause. We try every i
between 2 and 34 (inclusive). When i
is 2, 35 % i
isn't 0, so we get to the return True
statement. Once that return statement is executed, the function returns, and is_prime_bad(35)
is evaluated to True.
That shouldn't happen: we should only return True
after trying all the possible i
's. Just because 35 isn't even (i.e., isn't divisible by 2), it doesn't mean it's prime. That's why the correct thing to do is to return True after the loop ends.
It is, however, okay to return False
inside the loop. That's because if we found even one divisor of n
, we know that n
is definitely not prime, and it's okay to return from the function.
One small note: you always must put something in the if clause. The following produces an error:
if <cond>:
else:
<do something>
You can use pass in order to not do anything in the if clause, but not produce an error.
if <cond>:
pass
else:
<do something>
Here is an if-statement which is equivalent to what we have in is_prime():
if n % i != 0:
pass
else:
return False
(We can make it even close to the original statement by using not with ==:
if not (n % i == 0):
pass
else:
return False
If you find yourself writing something like that, you should obviously rewrite the code and write instead what we have in the original function:
if n % i == 0:
return False