Primality testing¶

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.

In [1]:
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

In [2]:
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

More on designing loops: return inside for¶

Suppose that instead of the (correct) design above, we do something like this:

In [3]:
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:

In [4]:
is_prime_bad(35)
Out[4]:
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.

pass¶

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 
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]: