Monday, September 8th, 2025¶
Last week, we looked at for
loops and determining whether a given number is prime.
n = 15
n_is_prime = True
for d in range(2,n):
if n % d == 0:
n_is_prime = False
if n_is_prime:
print('{} is prime.'.format(n))
else:
print('{} is not prime.'.format(n))
15 is not prime.
Note: in the code above, it will often happen that we perform many divisibility checks for no reason (for example, if 2
divides n
, then we will still unnecessarily check whether 3
, 4
, 5
, ... also divide n
). It would be helpful if we could escape the loop prematurely.
The break
and continue
commands¶
The break
command can be used within a loop to immediately exit the loop.
for i in range(10):
print(i)
if i == 5:
break
0 1 2 3 4 5
Exercise: Modify the prime-checking code above to exit the loop as soon as n
is determined to not be prime.
n = 15
n_is_prime = True
for d in range(2,n):
if n % d == 0:
n_is_prime = False
break
if n_is_prime:
print('{} is prime.'.format(n))
else:
print('{} is not prime.'.format(n))
15 is not prime.
Other times, we may want to immediately proceed to the next iteration of a loop. The continue
command can be used to accomplish this.
for i in range(10):
if i < 3:
continue
if i > 7:
break
print(i)
3 4 5 6 7
while
loops¶
Suppose that we want to find the first 100 cubes that have remainder 1
after division by 4
.
Problem: We don't know ahead of time how many numbers we'd have to check before we find 100 such cubes.
For these types of problems, we can use a while
loop. A while
loops will iteratiavely perform some operations as long as some Boolean expression is True
. The syntax is as follows:
while (some Boolean expression is True):
(do something)
Note: the (some Boolean expression)
can be (and often is) a variable that will be modified within the while
loop.
for i in range(10):
print(i)
0 1 2 3 4 5 6 7 8 9
i = 0
while i < 10:
print(i)
i = i + 1
0 1 2 3 4 5 6 7 8 9
We can also use break
to terminate a while
loop:
i = 0
while True:
print(i)
if i >= 9:
break
i = i + 1
0 1 2 3 4 5 6 7 8 9
Warnings:
- Unlike with
for
loops, it is very easy to end up with awhile
loop that runs forever. - Always make sure that your Boolean expression will eventually be
False
so that thewhile
loop can terminate. - Check that you are incrementing any necessary variables with each iteration.
If you find your code stuck running a while
loop, you can hit the stop button (black square in the toolbar near the top of the notebook) to try to get Python to interrupt the kernel. If this does not work, you can restart the kernel (refresh symbol to the right of the stop button) to shut down the notebook and restart.
There are some shortcuts for incrementing variables. In particular:
n += 1
is equivalent ton = n + 1
n -= 3
is equivalent ton = n - 3
n *= 7
is equivalent ton = n * 7
n = 5
n += 1
n *= 3
print(n)
18
i = 0
while i < 10:
print(i)
i += 1
0 1 2 3 4 5 6 7 8 9
Exercise: Write code that will find the first 100 cubes that have remainder 1
after division by 4
.
n = 1
cubes_with_remainder_1 = []
while len(cubes_with_remainder_1) < 100:
cube = n**3
if cube % 4 == 1:
cubes_with_remainder_1.append(cube)
n += 1
print(cubes_with_remainder_1)
[1, 125, 729, 2197, 4913, 9261, 15625, 24389, 35937, 50653, 68921, 91125, 117649, 148877, 185193, 226981, 274625, 328509, 389017, 456533, 531441, 614125, 704969, 804357, 912673, 1030301, 1157625, 1295029, 1442897, 1601613, 1771561, 1953125, 2146689, 2352637, 2571353, 2803221, 3048625, 3307949, 3581577, 3869893, 4173281, 4492125, 4826809, 5177717, 5545233, 5929741, 6331625, 6751269, 7189057, 7645373, 8120601, 8615125, 9129329, 9663597, 10218313, 10793861, 11390625, 12008989, 12649337, 13312053, 13997521, 14706125, 15438249, 16194277, 16974593, 17779581, 18609625, 19465109, 20346417, 21253933, 22188041, 23149125, 24137569, 25153757, 26198073, 27270901, 28372625, 29503629, 30664297, 31855013, 33076161, 34328125, 35611289, 36926037, 38272753, 39651821, 41063625, 42508549, 43986977, 45499293, 47045881, 48627125, 50243409, 51895117, 53582633, 55306341, 57066625, 58863869, 60698457, 62570773]
Exercise: Write code that will find the first 10 prime numbers.
primes = []
n = 2
while len(primes) < 10:
n_is_prime = True
for d in range(2,n):
if n % d == 0:
n_is_prime = False
break
if n_is_prime:
primes.append(n)
n += 1
print(primes)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
Functions in Python¶
Very often, we want to write code that can be applied to many different inputs. We can accomplish this by writing a Python function. The syntax for defining functions in Python is:
def function_name(some_inputs):
...do something...
def hello_world():
print('Hello world!')
After defining a function, we can call upon it to execute the function's code.
hello_world()
hello_world()
hello_world()
Hello world! Hello world! Hello world!
Exercise: Write a function called is_prime
that takes in a variable n
and prints whether or not n
is prime.
def is_prime(n):
n_is_prime = True
for d in range(2,n):
if n % d == 0:
n_is_prime = False
break
if n_is_prime:
print('{} is prime.'.format(n))
else:
print('{} is not prime.'.format(n))
for n in range(2,20):
is_prime(n)
2 is prime. 3 is prime. 4 is not prime. 5 is prime. 6 is not prime. 7 is prime. 8 is not prime. 9 is not prime. 10 is not prime. 11 is prime. 12 is not prime. 13 is prime. 14 is not prime. 15 is not prime. 16 is not prime. 17 is prime. 18 is not prime. 19 is prime.
It is often more useful to have functions like the above return either a True
or a False
Boolean depending on whether n
is prime or not.
More generally, we often want functions to return some value to the caller. This can be done using a return
statement:
Exercise: Rewrite the is_prime
function to return
a Boolean True
if the input is prime and False
if not.
def is_prime(n):
n_is_prime = True
for d in range(2,n):
if n % d == 0:
n_is_prime = False
break
if n_is_prime:
return True
else:
return False
for n in range(2,20):
#print(n, is_prime(n))
if is_prime(n):
print('{} is prime.'.format(n))
else:
print('{} is not prime.'.format(n))
2 is prime. 3 is prime. 4 is not prime. 5 is prime. 6 is not prime. 7 is prime. 8 is not prime. 9 is not prime. 10 is not prime. 11 is prime. 12 is not prime. 13 is prime. 14 is not prime. 15 is not prime. 16 is not prime. 17 is prime. 18 is not prime. 19 is prime.
Let's use our is_prime
function to modify our previous while
loop that searched for primes.
primes = []
n = 2
while len(primes) < 10:
if is_prime(n):
primes.append(n)
n += 1
print(primes)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
Note: Variables that are defined within a function only exist within that function. In the above example, we've defined n_is_prime
within the is_prime
function. This is a completely different variable to the globally defined variable n_is_prime
that was defined at the top of this notebook. If a function calls upon a variable name that it has not defined, it will look outside the function for that variable name.
def test_function():
hello_this_is_a_test = 'MTH 337'
print(hello_this_is_a_test)
test_function()
MTH 337
print(hello_this_is_a_test)
--------------------------------------------------------------------------- NameError Traceback (most recent call last) Cell In[27], line 1 ----> 1 print(hello_this_is_a_test) NameError: name 'hello_this_is_a_test' is not defined
this_is_a_second_test = 'Hello'
def test_function():
print(this_is_a_second_test)
test_function()
Hello
this_is_a_third_test = 'Hello'
def test_function():
this_is_a_third_test = 'Goodbye'
test_function()
print(this_is_a_third_test)
Hello
Note: A function will terminate as soon as it hits a return
statement.
def test_function():
print('Hello')
return 5
print('Goodbye')
test_function()
Hello
5
Exercise: Write a function get_primes
that takes in an input n
and returns a list of all prime numbers between 2
and n
(inclusively).
Modules in Python¶
When starting a Jupyter notebook, Python loads in a base set of commands that are made available (e.g. print
, list
, int
, etc.). However, there are many other commands that are not loaded by default that we sometimes want to make use of. We can import various modules that contain all kinds of additional functionality.
For example, suppose we want to consider the efficiency of our get_primes
function. We can import the time
module to gain access to different time-related tools, which we can use to time our function.
Importing a module¶
We can use the syntax import <some module>
to make that module available to us. After doing so, we can use the syntax <some module>.<some function or object>
to call upon variables functions or objects contained in that module.
import time
time.asctime()
'Mon Sep 8 16:28:25 2025'
We can use the time
function from the time
module to time our get_primes
function.
time.time()
1757363340.2630873
t0 = time.time()
t1 = time.time()
print(t1 - t0)
15.103442668914795
Importing from a module¶
Sometimes we just want to make use of a single function or object from within a module. We can use the syntax from <some module> import <some function or object>
to gain direct access to the function or object. After doing so, we can simply use <some function or object>
directly rather than looking back inside the module (that is, we do not need to include <some module>.
before calling upon the function/object).
asctime()
--------------------------------------------------------------------------- NameError Traceback (most recent call last) Cell In[41], line 1 ----> 1 asctime() NameError: name 'asctime' is not defined
time.asctime()
'Mon Sep 8 16:36:18 2025'
from time import asctime
asctime()
'Mon Sep 8 16:36:37 2025'
Optimization¶
Let's think of ways that we can improve the efficiency of our get_primes
function. By extension, we will want to improve the efficiency of our is_prime
function.
def is_prime(n):
n_is_prime = True
for d in range(2,n):
if n % d == 0:
n_is_prime = False
break
if n_is_prime:
return True
else:
return False
n = 1000000007
t0 = time.time()
print(is_prime(n))
t1 = time.time()
print(t1-t0)
True 65.03244352340698
We can import the sqrt
function from the math
module in order to compute square roots.
Exercise: Rewrite the is_prime
function to take advantage of the $\sqrt{n}$ optimization that we've discussed.
from math import sqrt
def new_is_prime(n):
n_is_prime = True
for d in range(2,int(sqrt(n))+1):
if n % d == 0:
n_is_prime = False
break
if n_is_prime:
return True
else:
return False
n = 1000000007
t0 = time.time()
print(new_is_prime(n))
t1 = time.time()
print(t1-t0)
True 0.006369113922119141
Exercise: Compare the efficiency of using this optimization by timing the old version of is_prime
against the new version.