7  Loops

Jupyter Notebook

Loops allow programs to rerun the same block of code multiple times. This might seem like a funny thing to want to do but it turns out that there are many important tasks that are repetitive in nature (perhaps with small changes for each successive repetition). A loop provides a succinct and efficient way to perform tasks of this nature.

7.1 for loops

The for loop is probably the most common loop you will encounter and is a good choice when you know beforehand exactly what things you want to loop over. Here is an example of a for loop that is used to add up the elements of a list.

thesum = 0
for i in [3,2,1,9.9]: 
    thesum += i

This would be equivalent to the following code:

thesum = 0

thesum = thesum + 3
thesum = thesum + 2
thesum = thesum + 1
thesum = thesum + 9.9

which isn’t that much longer than using a loop. However, as the list gets longer and/or the mathematical operations being performed get more complex the second method would get unreasonably long.

The correct language is to say that we are iterating over the list [3,2,1,9.9]. This means that the loop variable (i in this case but you can choose it to be whatever you want) gets assigned the values of the list elements, one by one, until it reaches the end of the list. You can use for loops to iterate over any multi-element object like lists or tuples. Python uses indentation to indicate where the loop ends. In this case there was only one statement inside to loop, but if you wanted more than one each line should be indented.

To Do:

  1. Add a print statement inside of the loop above to display the value of the variable thesum.
  2. Predict what the output will be and then run the code and verify that you were correct.
  3. Now change the loop variable to be named physics and verify that the loop still works as expected.

You can iterate over range objects and strings using for loops.

for i in ['Physics', 'is','so','fun']: # Iterate over a list of strings
    print(i)
Physics
is
so
fun
for i in range(5,50,3):  #Generates a list from 5 -> 50 with a step size of 3
    print(i)
5
8
11
14
17
20
23
26
29
32
35
38
41
44
47

These examples are so simple that you might wonder when a loop might actually be useful to you. Let’s see if we can build a loop to calculate the following sum

\[ \sum_{n=1}^{1000} {1\over n^2} \tag{7.1}\]

theSum = 0
for n in range(1,1000):
    theSum = theSum + 1/n**2
print(theSum)
1.6439335666815615

Here, n is being assigned the values 1,2,3,4....1000, one by one, until it gets all the way to 1000. Each time through the loop, n is different and the expression 1/n**2 evaluates to a new value. The variable theSum is updated each time through to be the running total of all calculations performed thus far. Here’s another example of a loop used to calculate the value of \(20!\):

theProduct = 1
for n in range(1,21):
    theProduct = theProduct * n #Multiply theProduct by n
print(theProduct)
2432902008176640000

Remember that the range function creates a list starting at \(1\), going up to \(21\) but not including it. The math library has a function called factorial that does the same thing. Let’s use it to check our answer:

from math import factorial
factorial(20)
2432902008176640000

7.1.1 Boolean Logic Inside Loops

Often when using loops, we only want a block of code to execute when some condition is satisfied. We can use boolean logic inside of the loop to accomplish this. For example, let’s write a loop to compute the following sum:

\[ \sum_{{n\over 5} \in \text{ Int and } {n\over 3} \in \text{ Int}} {1\over n^2} \]

which is similar to the one we did above, but this time we only want to include terms where \(n\) is a perfect multiple of both 5 and 3. To check to see if n is a perfect multiple of a number we can calculate the modulo (remainder after division) using the % operator and check that it is equal to zero.

theSum = 0
for n in range(1,1000):
    if n % 5 == 0 and n % 3 == 0:
        theSum = theSum + 1/n**2
print(theSum)
0.007243985583159138

To Do:

Perform the following modifications to the loop above.

  1. Increase the upper bound of the sum to go up to and include \(5000\).
  2. Only include the terms where n is a multiple of 5 and 3 or is a multiple of 7.
  3. Replace the statement that updates theSum to its shorthand version.

7.1.2 Zipping and Enumerating

There are times when it is necessary to iterate over two lists simultaneously. For example, let us say that we have a list of atomic numbers (AN) and a list of approximate atomic masses (mass) of the most abundant isotopes for the first six elements on the periodic table.

AN = [1,2,3,4,5,6]
mass = [1,4,7,9,11,12]

If we want to calculate the number of neutrons in each isotope, we need to subtract each atomic number from the atomic mass. To accomplish this, it would be nice to iterate over both lists simultaneously

7.1.2.1 Zipping

The simplest way to iterate over two lists simultaneously is to combine both lists into a single, iterable object and iterate over it once. The zip function does just that by merging two lists or tuples into a nested list

AN = [1,2,3,4,5,6]
mass = [1,4,7,9,11,12]

zipped = zip(AN,mass)

for pair in zipped:
    print(pair[1] - pair[0])
0
2
4
5
6
6

The zip objects are “single use” so you can’t reuse zipped in a later loop. If the two lists being zipped are not the same length, zip stops zipping when it reaches the end of the shorter list.

To Do:

  1. Print the variable zipped and inspect closely. Was the output what you expected?
  2. Now do print(tuple(zipped)) and inspect closely. Draw a conclusion.
  3. Add a few more entries to the list named AN, but don’t add the corresponding entries to the other list. Now the lists being zipped aren’t the same length. Inspect the output of the print statement to determine what zip does in this scenario.

7.1.2.2 Enumeration

A close relative to zip is enumerate which zips a list to the index value for that list (read that last statement again). It also returns a “single use” object that can be iterated over.

AN = [1,2,3,4,5,6]
mass = [1,4,7,9,11,12]

enum = enumerate(mass)

for idx,val in enum:
    print(val - AN[idx])
0
2
4
5
6
6

To Do:

  1. Repeat the previous To-Dos for the cell above.

7.1.3 List Comprehension

It is fairly common to use a for loop to populate a list with a sequence of numbers.

myList = []

for i in range(10):
    myList.append(i**2)

print(myList)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

This entire process can be compressed down into a single line by expressing the for loop in square brackets. This is known as list comprehension. The code below will generate the list as above.

myList = [i**2 for i in range(10)]
print(myList)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

List comprehension can take a little time to get used to but it is well worth it. It saves both time and space and makes code less cluttered. You can even add boolean expressions to your conditionals for further control of the final result.

myList = [i**2 for i in range(10) if i %2 == 0]  # Include only the evens.
print(myList)
[0, 4, 16, 36, 64]

To Do:

Think of your favorite mathematical function and modify the code above to generate a few samples from it.

7.2 while Loops

Logic can be combined with loops using something called a while loop. A while loop is a good choice when you don’t know beforehand exactly how many iterations of the loop will be executed but rather want the loop to continue to execute until some condition is met. As an example, notice that in Equation 7.1, the terms in the sum get progressively smaller as \(n\) gets bigger. It doesn’t make sense to continue adding to the sum once the terms get very small. Let’s compute this sum by looping until the fraction \({1 \over n^2}\) become smaller than \(1 \times 10^{-2}\).

term = 1  # Load the first term in the sum
s = term  # Initialize the sum
n = 1     # Set a counter
while term > 1e-2:  # Loop while term is bigger than 1e-2
    n = n +  1        #Add 1 to n so that it will count: 2,3,4,5
    term = 1./n**2    # Calculate the next term to add
    s = s +  term     # Add 1/n^2 to the running total

This loop will continue to execute until term<1e-2. Note that unlike the for loop, here you have to do your own counting if you need to know how many iterations have been performed. Be careful about what value n starts at and when it is incremented (n = n + 1). Also notice that term must be assigned prior to the start of the loop. If it wasn’t the loop’s first logical test would fail and the loop wouldn’t execute at all.

To Do:

  1. Decrease the threshold on the termination condition and observe any changes in the final result and how many more iterations are performed.
  2. After toying around with it for a while pick a termination condition that you feel will produce a result that is accurate.

while loops should be used with caution because you can easily write a faulty termination condition and inadvertently write a loop that runs forever. This happens because your termination condition was never met. An example of this is given below.

Warning: Do not execute the code block below!!

x = 0

while x != 10:
    x = x + 3
print("Done")

The loop above is intended to end after a few iterations when the value of x is equal to 10. However, closer inspection reveals that the value of x will never be equal to 10. After the first iteration x is equal to 3, then 6,9,12,15 and so on… but never 10. This loop will run forever because the termination condition is never met (x != 10 never produces a False)!! If you choose to use a while loop, triple check your termination condition to make sure you haven’t made a mental error. Avoiding the use of != or == in your termination condition will help too. Use <= or >= instead.

To Do:

  1. Modify the termination condition in the loop above so that it terminates when x gets larger than 15.
  2. Run the code and verify that you did it correct.

7.3 continue, break, and pass Commands

The continue, break, and pass commands are used to control the flow of code execution in loops. Here is a description of their usage:

Operator Description
break Exits a for/while loop.
continue Skips the remaining loop block and begins the next iteration.
pass No action; code contiues on

The break statement is useful when you want to completely stop a loop early. Here is our sum loop rewritten with a break statement added to stop the loop after 1000 iterations.

term = 1  # Load the first term in the sum
s = term  # Initialize the sum
n = 1     # Set a counter
while term > 1e-10:  # Loop while term is bigger than 1e-10
    n +=  1        #Add 1 to n so that it will count: 2,3,4,5
    term = 1./n**2    # Calculate the next term to add
    s += term     # Add 1/n^2 to the running total
    if n > 1000:
        print('This is taking too long. I''m outta here...')
        break
This is taking too long. Im outta here...

The continue statement is similar to break except that instead of stopping the loop, it only stops the current iteration of the loop. All code below the continue statement will be skipped and the next iteration will begin. For example, if you wanted to do the sum from equation ?? but only include those terms for which n is a multiple of 3, it could be done like this:

term = 1  # Load the first term in the sum
s = term  # Initialize the sum
n = 1     # Set a counter
while term > 1e-10:  # Loop while term is bigger than 1e-10
    n +=  1        #Add 1 to n so that it will count: 2,3,4,5
    if n % 3 != 0:
        continue
    term = 1./n**2    # Calculate the next term to add
    s += term     # Add 1/n^2 to the running total

Now, when the value of n is not a multiple of 3, the sum will not be updated and the associated terms are effectively skipped.

Finally, the pass statement does nothing. Seriously!! It is merely a place holder for code that has not bee written yet. Usually, you’ll use the pass statement to run and test code without errors due to missing code.

7.4 Flash Cards

  1. Explain the basic structure and key elements of a for loop.
  2. Explain the basic structure and key elements of a while loop.
  3. What does the break statement do?
  4. What does the continue statement do?
  5. What does the pass statement do?
  6. What does the zip function do? Give an example.
  7. What does the enumerate function do? Give an example.
  8. What is list comprehension? Give an example.
  9. Where is the account of Jesus walking on the water?

7.5 Exercises

  1. Use a loop to construct a list containing the first 10 fibonacci numbers that are multiples of \(5\). (The modulo operator (%) is a good and efficient way to see if a number is a multiple of another.)

Hint/Answer: The 10th fibonacci number that is a multiple of 5 is: 12,586,269,025

# Python code here.
  1. Summations appear often in science and mathematics. One such summation is called the Riemann Zeta function and is given by \[ \zeta(n) = \sum_{k=1}^\infty {1\over k^n} = {1\over 1^n} + {1\over 2^n} + {1\over 3^n} + \dots\]

    1. Use a for loop to evaluate \(\zeta(2)\) and \(\zeta(4)\) and verify that \(\zeta(2) = {\pi^2 \over 6}\) and \(\zeta(4) = {\pi^4 \over 90}\). This summation is called an infinite sum and we surely don’t want our loop to continue forever!! Instead, an infinite sum like this one can be approximated with a for loop with a large number of iterations. To determine if you have included enough terms, increase the number of terms steadily and watch for the final result to stop changing appreciably.
    2. Now repeat part 1 using list comprehension and the sum function?
    3. Repeat the exercise one final time using a while loop. Write the while loop so that it stops iterating when the value of \({1\over k^n}\) gets smaller than \(1 \times 10^{-6}\).
#Python code here
  1. (Prime numbers) A prime number is a number that is not the product of two smaller numbers except \(1\). (i.e it is only evenly divisible by itself and \(1\)) To determine whether a number (\(n\)) is prime, check to see if the number is divisible by any integer starting at \(2\) and ending at \(\sqrt{n}\) (rounded up). If the number is divisible by even one of these integers, you know the number isn’t prime.

    1. Write a for loop that determines whether a specified number is prime or not. Try your loop out on the following numbers: \(75\), \(71\), and \(91\).
    2. Repeat step 1 using a while loop. Be sure to include a fail-safe with a break statement to avoid infinite loops.
    3. Now enclose your loop from either step 1 or step 2 inside another for loop to find all of the prime numbers that are less than \(500\). Put all of these numbers in a list. Hint: You should find that there are \(95\) prime numbers less than \(500\)
    4. Finally, enclose your loop from either step 1 or step 2 inside another while loop to find the first \(500\) prime numbers. As always with while loops be sure to include a fail-safe with a break statement to avoid infinite loops. Put all of these numbers into a list and verify that the list contains \(500\) numbers. Hint: The 500th prime number is \(3571\)
# Python code here
  1. The greatest common divisor (GCD) of two numbers is the greatest positive integer that divides each of the numbers. For example, the GCD of \(30\) and \(20\) is \(10\) (i.e. There is no number greater than \(10\) that divides both \(30\) and \(20\)). One algorithm for finding this number is called Euclid’s algorithm. The process for Euclid’s algorithm is as follows: (as long as both numbers are not negative.)

    • Find the remainder of dividing the larger number by the smaller number.
    • Replace the larger number with the number found in step 1.
    • Repeat this process until one of the numbers is zero. At this point, the other number is the GCD.
    1. Use a while loop to find the GCD of \(105\) and \(252\). You may want to find the GCD of smaller numbers first to verify that your code is working.
    2. The least common multiple (LCM) of two numbers is the smallest number that two or more numbers can divide evenly. For example, the LCM of \(6\) and \(4\) is \(12\) (i.e. There is no number smaller than \(12\) that is also a multiple of both \(6\) and \(4\)). The LCM can be found from the GCD using the following expression: \(LCM(a,b) = {a \times b\over GCD(a,b)}\). Calculate the LCM of \(105\) and \(252\).
    3. If \(GCD(a,b) = 1\) then the numbers are considered co-prime. Can you find a co-prime pair?
#Code here.
  1. Another interesting summation is given by: \[ f(n) = \sum_{k=0}^\infty (-1)^k {1\over n^k} = {1\over n^0} - {1\over n^1} + {1\over n^2} - {1\over n^3} + {1\over n^4} - \dots\] This sum converges (approaches) the value \({n \over n + 1}\) only if \(n > 1\).

    1. Use a for loop to evaluate \(f(2)\) and \(f(4)\) and verify that \(f(2) = {2 \over 2 + 1} = {2\over 3}\) and \(f(4) = {4 \over 4 + 1} = {4\over 5}\). This summation is called an infinite sum and we surely don’t want our loop to continue forever!! Instead, an infinite sum like this one can be approximated with a for loop with a large number of iterations. To determine if you have included enough terms, increase the number of terms steadily and watch for the final result to stop changing appreciably.
    2. Now repeat part 1 using list comprehension and the sum function?
    3. Repeat the exercise one final time using a while loop. Write the while loop so that it stops iterating when the value of \({1\over n^k}\) gets smaller than \(1 \times 10^{-6}\).
    4. Now investigate what happens when \(n = 0.5\). Do this by running your for loop from part 1 for more and more iterations and observing whether the sum approach one value (converges) or increases without bound (diverges).
  2. In the cell below you will find a dictionary containing the mass(in kg) and diameter of the planets in our solar system. Use a loop to iterate over this dictionary and calculate the acceleration due to gravity at the surface of the planet. \[ g = {G M \over R^2}\]. (\(G = 6.67 \times 10^{-11}\)) Then add this value to the list for each planet. Print the dictionary when you are done to verify that each dictionary entry is a list with three elements.

planetData = {"Mercury": [0.33e24,4.879e6],"Venus":[4.87e24,1.2104e7],"Earth":[5.97e24,1.2756e7],"Mars":[0.642e24,6.792e6],"Jupiter":[1.898e27,1.42984e8],"Saturn":[5.68e26,1.20536e8],"Uranus":[8.68e25,5.1118e7],"Neptune":[1.02e26,4.9528e7],"Pluto":[0.013e24,2.376e7]}

(Extra Credit) A transcendental equation is one that cannot be solved analytically (with pencil and paper). Try solving the following equation for x to see what I mean: \[ {\cos x \over x} = 1\] One numerical method for solving an equation like this involves first rearranging it to look like this: \[ x = \cos x\] and then using a loop to repeatedly evaluating the right hand side, using the result of the previous evaluation as the input, until \(x\) and \(\cos(x)\) are essentially equal (to within \(1 \times 10^{-3}\) is sufficient). 1 The first few iterations of this process might look something like this: \[(x_\text{old})_1 = 2 \text{ (starting guess)}\] \[(x_\text{new})_1 = \cos((x_\text{old})_1)\] \[(x_\text{old})_2 = (x_\text{new})_1\] \[(x_\text{new})_2 = \cos((x_\text{old})_2)\] \[(x_\text{old})_3 = (x_\text{new})_2\] \[(x_\text{new})_3 = \cos((x_\text{old})_3)\]

  • 1 This is called successive relaxation.

    1. Use a while loop to solve this equation and report the correct value for x.
    2. Plug the answer back into the equation to verify that it indeed does satisfy the equation.
    3. Report how many iterations it took to solve the problem.
    4. Decrease the value in the termination condition and observe how the number of iterations and the accuracy of the answer change.
    x = 2 # starting guess