Linear Algebra and Programming Skills¶

Dr Jon Shiach, Department of Computing and Mathematics, Manchester Metropolitan University


Loops¶

Learning Outcomes¶

On successful completion of this page readers will be able to:

  • use for loops to repeat a set of commands a given number of times;
  • use while loops to repeat a set of commands whilst a given logical condition is true;
  • use loop control to continue or break a loop based on a logical condition;
  • nest loops within loops.

The whole point of computer programming is to get the computer to do tasks that would take a human too long to complete. A lot of this is to do with repitition, performing the same calculations or commands over and over again. There are two constructs to help us do this: for loops and while loops.


For loops¶

If we want to repeat the execution of a set of commands a given number of times then we can use a for loop.

for variable in range:
    commands

The for declaration requires a loop variable and a range followed by a colon :. The commands that we want to repeat are written underneath the for loop declaration and indented. The commands within the for loop are repeated for each value of the loop variable from the range. The for loop ends with the next non-indented command.

If the commands that are within a for loop do not require the loop variable then it is common to use _ as the loop variable.

Example 1¶

The code below uses the for loop to print the integer numbers 0 to 4. Enter it into the code cell below and execute it to see the result.

for i in range(5):
    print(i)
In [1]:
for i in range(5):
    print(i)
0
1
2
3
4

Here the loop variable is i and the range is defined by range(5) which generates a list of the 5 numbers from 0 to 4. The print(i) command was repeated 5 times, once for each value from the range. The use of end=", " in the print() command means that the values are printed on the same line separated by commas.

Example 2¶

The $n$th Fibonnacci number is calculated using $F_{n} = F_{n-1} + F_{n-2}$ where the first two Fibonacci numbers are $F_1=0$ and $F_2=1$, e.g.,

$$0, 1, 1, 2, 3, 5, 8, 13, \ldots$$
The following code uses a for loop to print the first 20 Fibonacci numbers. Enter it into the code cell below and execute it.

a, b = 0, 1

for _ in range(20):
    print(f"{a} ")
    a, b = b, a + b
In [2]:
a, b = 0, 1

for _ in range(20):
    print(f"{a} ")
    a, b = b, a + b
0 
1 
1 
2 
3 
5 
8 
13 
21 
34 
55 
89 
144 
233 
377 
610 
987 
1597 
2584 
4181 

Note that the first two Fibonacci numbers are defined before using a for loop repeat the calculation 20 times.

Example 3¶

The following program uses a for loop to sum the elements of the array X. Enter this into the code cell below and execute it.

import numpy as np

X = np.array([ 1, 7, -4, 3, -5 ])
total = 0

for i in range(5):
    total += X[i]

print(f"The sum of the five numbers is {total}")
In [3]:
import numpy as np

X = np.array([ 1, 7, -4, 3, -5 ])
total = 0

for i in range(5):
    total += X[i]

print(f"The sum of the five numbers is {total}")
The sum of the five numbers is 2

Exercise 1 - For loops¶

  1. Use a for loop to print "hello world" 10 times.
In [4]:
for _ in range(10):
    print("hello world")
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
  1. Use a for loop to print the first 20 even numbers.
In [5]:
for i in range(1, 21):
    print(2 * i)
2
4
6
8
10
12
14
16
18
20
22
24
26
28
30
32
34
36
38
40
  1. Use a for loop to calculate the sum of the first 100 natural numbers.
In [6]:
n, mysum = 100, 0

for i in range(1, n + 1):
    mysum += i
    
print(f"The sum of the first {n} numbers is {mysum}")
The sum of the first 100 numbers is 5050
  1. Use a for loop to calculate $52!$ (the number of ways you can shuffle a deck of cards).
In [7]:
x = 1

for i in range(1, 53):
    x *= i
    
print(x)
80658175170943878571660636856403766975289505440883277824000000000000
  1. Use a for loop to calculate the 10th term and the sum of the first 10 terms of the geometric series: $3, 12, 48, 192, \ldots$
In [8]:
n, a, r, mysum = 10, 3, 4, 3

for _ in range(n - 1):
    a *= r
    mysum += a

print(f"The 10th term is {a} and the sum of the first 10 terms is {mysum}")
The 10th term is 786432 and the sum of the first 10 terms is 1048575
  1. Use for loops to calculate the mean $\bar{x}$ and standard deviation $\sigma$ of the numbers: 1.2, 4.3, 5.7, 1.4, 7.2, 3.5.

    $$ \bar{x} = \frac{1}{n}\sum_{i=1}^n x_i, \qquad \sigma = \sqrt{\frac{\displaystyle\sum_{i=1}^n (\bar{x}-x_i)^2}{n-1}}.$$
In [9]:
X = np.array([1.2, 4.3, 5.7, 1.4, 7.2, 3.5])
n, xbar, sigma = len(X), 0, 0

for x in X:
    xbar += x

xbar /= n

for x in X:
    sigma += (xbar - x) ** 2

sigma = np.sqrt(sigma / (n - 1))
print(f"The mean is {xbar:0.4f} and the standard deviation is {sigma}")
The mean is 3.8833 and the standard deviation is 2.3659388552256937
  1. The series expansion of $\sin(x)$ is

    $$\sin(x) = \sum_{n=0}^\infty \frac{(-1)^n}{(2n+1)!}x^{2n+1} = x - \frac{x}{3!} + \frac{x^5}{5!} - \frac{x^7}{7!} + \cdots$$
    Use a for loop to compute $\sin\left(\frac{\pi}{4}\right)$ using the first 5 terms.

Hint: the command factorial(x) from the math library computes the value of $x!$

In [10]:
import math

x, sinx = math.pi / 4, 0

for n in range(5):
    sinx += (-1) ** n / math.factorial(2 * n + 1) * x ** (2 * n + 1)    

print(f"sin(pi/4) = {sinx}")
sin(pi/4) = 0.7071067829368671

While loops¶

If we want to repeat the execution of a set of commands when we don't know how many repititions is required then we can use a while loop.

while logical condition:
    commands

The indented commands will be exectuted as long as the logical condition returns a True result.

Example 4¶

The commands below use a while loop to print the integer numbers between 0 and 5 (i.e., similar to example 1). Enter them into the code cell below and execute it.

i = 0

while i < 5:
    print(i)
    i += 1
In [11]:
i = 0

while i < 5:
    print(i)
    i += 1
0
1
2
3
4

Note that we needed a variable i which is incremented by 1 at each iteration to keep track of the number of iterations we have done. If we didn't increment i then the logical condition i < 5 would always be true and the while loop would repeat the print(i) command forever. This is a common programming error known as an infinite loop which can be exited by clicking on the ◾ button.

Example 5¶

The program below uses a while loop to print the first 20 Fibonacci numbers. Enter it into the code cell below and execute it.

a, b = 0, 1
n = 0

while n < 20:
    print(a)
    a, b = b, a + b
    n += 1
In [12]:
a, b = 0, 1
n = 0

while n < 20:
    print(a)
    a, b = b, a + b
    n += 1
0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181

Exercise 2 - While loops¶

  1. Use a while loop to print "hello again" ten times.
In [13]:
i = 0

while i < 10:
    print("hello again")
    i += 1
hello again
hello again
hello again
hello again
hello again
hello again
hello again
hello again
hello again
hello again
  1. Use a while loop to print the first 20 odd numbers.
In [14]:
i, x = 0, 1

while i < 20:
    print(x)
    x += 2
    i += 1
1
3
5
7
9
11
13
15
17
19
21
23
25
27
29
31
33
35
37
39
  1. Use a while loop to calculate the sum of the first 100 natural numbers.
In [15]:
i, mysum = 0, 0

while i < 101:
    mysum += i
    i += 1
    
print(f"The sum of the first 100 natural numbers is {mysum}")
The sum of the first 100 natural numbers is 5050
  1. The Collatz conjecture states that the series generated by the following rules will eventually reach 1.

    $$x_{n+1} = \begin{cases} \dfrac{x_n}{2}, & \text{if $x_n$ is even}, \\ 3x_n+1, & \text{if $x_n$ is odd} \end{cases}$$
    Use a while loop to print the numbers in this series for a starting value of $x_0=100$. How many steps were required to reach 1? The first few numbers in this sequence are

    $$100 \to 50 \to 25 \to 76 \to 38 \to \ldots$$

Hint: The command x % y returns the remainder of $x \div y$.

In [1]:
x, steps = 100, 0
print(x, end="")

while x > 1:
    if x % 2 == 0:
        x //= 2
    else:
        x = 3 * x + 1
    
    steps += 1
    print(f" -> {x}", end="")
    
print(f"\n{steps} steps were required to reach 1")
100 -> 50 -> 25 -> 76 -> 38 -> 19 -> 58 -> 29 -> 88 -> 44 -> 22 -> 11 -> 34 -> 17 -> 52 -> 26 -> 13 -> 40 -> 20 -> 10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1
25 steps were required to reach 1

Loop control¶

Sometimes it is neccessary to interrupt the normal flow of a loop to either exit the loop before all iterations have completed or to move to the next iteration immediately without performing any further commands in the same iteration. To do this Python uses the break and continue commands.

Break¶

The break command is used to exit a loop.

Example 7¶

The ratio of successive Fibonacci numbers converge to the golden ratio $\varphi = \dfrac{1+\sqrt{5}}{2} = 1.618\ldots$, i.e.,

$$\frac{1}{1} = 1, \qquad \frac{2}{1} = 2, \qquad \frac{3}{2} = 1.5, \qquad \frac{5}{3} = 1.6667, \qquad \frac{8}{5} = 1.6, \qquad \ldots$$
The program below uses a while loop to calculate approximations of $\varphi$ ceasing iterations when the difference between two successive iterations is less than $10^{-6}$. Enter the following commands into the code cell below and execute it.

phi = (1 + math.sqrt(5)) / 2 # exact value of phi
a, b = 1, 1

while True:
    phi_estimate = b / a
    a, b = b, a + b

    if abs(phi_estimate - b / a) < 1e-6:
        break

print(f"The exact value of the golden ratio is {phi:1.8f}")
print(f"An approximation of the golden ratio using Fibonacci numbers is {phi_estimate:1.8f}")
In [17]:
phi = (1 + math.sqrt(5)) / 2 # exact value of phi
a, b = 1, 1

while True:
    phi_estimate = b / a
    a, b = b, a + b
    
    if abs(phi_estimate - b / a) < 1e-6:
        break

print(f"The exact value of the golden ratio is {phi:1.8f}")
print(f"An approximation of the golden ratio using Fibonacci numbers is {phi_estimate:1.8f}")
The exact value of the golden ratio is 1.61803399
An approximation of the golden ratio using Fibonacci numbers is 1.61803445

Here the logical condition used in the while loop is always True so the indented commands will repeat undefinitely unless the break command is executed. Note that we started the Fibonacci series at $F_2=1$ and $F_3=1$ since $\dfrac{F_2}{F_1}=\dfrac{1}{0}$ will cause a divide by zero error.

Continue¶

The continue command moves to the next cycle of a loop.

Example 8¶

The program below uses a for loop to calculate the sum of five numbers. Negative numbers are removed from the calculation. Enter it into the code cell below and execute it.

X = np.array([ 1, 7, -4, 3, -5 ])
total = 0

for i in range(5):
    if X[i] < 0:
        continue

    total += X[i]

print(f"The sum of the five numbers (excluding negatives) is {total}")
In [18]:
X = np.array([ 1, 7, -4, 3, -5 ])
total = 0

for i in range(5):
    if X[i] < 0:
        continue

    total += X[i]

print(f"The sum of the five numbers (excluding negatives) is {total}")
The sum of the five numbers (excluding negatives) is 11

Exercise 3 - Loop control¶

  1. Use for loops to loop through the numbers between 2 and 100 and print those numbers which are primes.

    Hint: loop through all numbers between 2 and 100 and use another loop to check all numbers between 2 and that number to see if it has a factor that is not itself (the x % y command will come in useful here).
In [19]:
for n in range(2, 101):
    isprime = True
    
    for factor in range(2, n):
        if n % factor == 0:
            isprime = False
            break
    
    if isprime:
        print(n)
            
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
  1. Use a while loop to create a game where the user has to guess the value of a random integer between 0 and 10. For each guess the game will tell the user whether their guess is greater or less than the random integer. The user wins the game if they guess correctly within 5 guesses.

Hints:

  • The command guess = int(input("Enter your guess > ")) can be used to prompt the user to enter a number.
  • The NumPy command np.random.randint(a, b) generates a randon integer in the range a to b.
In [20]:
import numpy as np

n, guess, remaining = np.random.randint(0, 10), -1, 5

while remaining > 0:
    guess = int(input("Enter your guess > "))
    
    if guess == n:
        print("You win!")
        break
    elif guess > n:
        print("Your guess is too high", end="")
    elif guess < n:
        print("Your guess is too low", end="")
    
    remaining -= 1
    
    if remaining > 0:
        print(f", you have {remaining} guesses remaining")
    else:
        print(f"\nNo guesses left, you lose. The number was {n}")
Enter your guess > 5
Your guess is too low, you have 4 guesses remaining
Enter your guess > 7
You win!

Nested loops¶

A loop can be contained or nested within another loop.

Example 9¶

The program below uses nested for loops to generate a multiplication square. Enter it into the code cell below and execute it to see the result.

M = np.zeros((10, 10))
rows, cols = M.shape

for i in range(rows):
    for j in range(cols):
        M[i,j] = (i + 1) * (j + 1)

print(M)
In [21]:
M = np.zeros((10, 10))
rows, cols = M.shape

for i in range(rows):
    for j in range(cols):
        M[i,j] = (i + 1) * (j + 1)

print(M)
[[  1.   2.   3.   4.   5.   6.   7.   8.   9.  10.]
 [  2.   4.   6.   8.  10.  12.  14.  16.  18.  20.]
 [  3.   6.   9.  12.  15.  18.  21.  24.  27.  30.]
 [  4.   8.  12.  16.  20.  24.  28.  32.  36.  40.]
 [  5.  10.  15.  20.  25.  30.  35.  40.  45.  50.]
 [  6.  12.  18.  24.  30.  36.  42.  48.  54.  60.]
 [  7.  14.  21.  28.  35.  42.  49.  56.  63.  70.]
 [  8.  16.  24.  32.  40.  48.  56.  64.  72.  80.]
 [  9.  18.  27.  36.  45.  54.  63.  72.  81.  90.]
 [ 10.  20.  30.  40.  50.  60.  70.  80.  90. 100.]]

The first loop use used to loop through the rows of M and for each value of i we have another loop to loop through the columns of M. Note that we had to add 1 to i and j when calculating the product of the row and column since Python uses zero indexing.


Exercise 4 - Nested loops¶

  1. The matrix multiplication of an $m\times p$ matrix $A$ and a $p\times n$ matrix $B$ is defined by

    $$[AB]_{ij} = \sum_{k=1}^p a_{ik}b_{kj}.$$
    Use three nested for loops (one each for $i$, $j$ and $k$) to calculate $AB$ give the two matrices below
$$A = \begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{bmatrix}, \qquad B = \begin{bmatrix} 10 & 11 & 12 \\ 13 & 14 & 15 \\ 16 & 17 & 18 \end{bmatrix}.$$
In [22]:
import numpy as np

A = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
B = np.array([[10, 11, 12], [13, 14, 15], [16, 17, 18]])
AB = np.zeros((A.shape[0], B.shape[1]))

for i in range(A.shape[0]):
    for j in range(B.shape[1]):
        for k in range(A.shape[1]):
            AB[i,j] += A[i,k] * B[k,j]
            
print(AB)
[[ 84.  90.  96.]
 [201. 216. 231.]
 [318. 342. 366.]]