Sol CH 5
Sol CH 5
for initialization, i.e., what the timer needs to do prior to timing of the
loop. If you look carefully at the string-part of this second argument,
you notice an import statement for add and x. You may wonder why
you have to do that when they are defined in your code above, but stay
relaxed about that, it is simply the way this timer function works. What
is required for the timer function to execute the code given in the first
argument, must be provided in the setup argument, even if it is defined
in the code above.
The following line,
x_time = t.timeit(10000) # Time 10000 runs of the whole loop
will cause the whole loop to actually be executed, not a single time, but
10000 times! There will be one recorded time, the time required to run
the loop 10000 times. Thus, if an average time for a single run-through
of the loop is desired, we must divide the recorded time by (in this case)
10000. Often, however, the total time is fine for comparison between
alternatives. The print command brings the recorded time to the screen,
before the next loop is timed in an equivalent way.
Why is the loop run 10000 times? To get reliable timings, the execution
times must be on the order of seconds, that is why. How many times the
requested code snippet needs to be run, will of course depend on the
code snippet in question. Sometimes, a single execution is enough. Other
times, many more executions than 10000 are required. Some trial and
error is usually required to find an appropriate number.
Executing the program produces the following result,
Time, function call: 2.22121 seconds
Time: 1.4738 seconds
So, using the function add to fill the array, takes 50% longer time!
5.7 Exercises
By comparing the printout and the code, it should be clear how program
execution goes.
Filename: read_nested_for_loops.py.
Solution. Exceptions will occur if the user gives text as input, or simply
nothing (i.e., just press enter), or if Ctrl-c is typed, or if division by zero
is requested (i.e., b = 0). In any of these cases, the program should stay
in control and handle the situation appropriately.
The program might be implemented as:
import sys
N = 4
i = 1
while i <= N:
# get the numbers
try:
a = float(input(’a: ’))
b = float(input(’b: ’))
except ValueError:
print(’\nError - you must give an integer or a real number!’)
i = i + 1
continue
except KeyboardInterrupt:
print(’\nOk, you want to stop’)
sys.exit(1)
i = i + 1
Filename: compute_division.py.
x = sym.symbols(’x’)
# Compute the 5 first terms in a Taylor series for sin(x) around x = 0.
T_sin5 = sym.sin(x).series(x, 0, 5).removeO()
5.7 Exercises 175
print(T_sin5)
Taylor_sin = sym.lambdify([x], T_sin5, modules=’numpy’)
import numpy as np
import matplotlib.pyplot as plt
Filename: symbolic_Taylor.py.
Remarks. Part of your task here, is to find and understand the required
documentation. Most likely, this means that you have to seek more
information than found in our book. You might have to read about the
Taylor series (perhaps use Wikipedia or Google), and you probably have
to look into more details about how Taylor series are handled with sympy.
To your comfort, this is a very typical situation for engineers and
scientists. They need to solve a problem, but do not (yet!) have the
required knowledge for all parts of the problem. Being able to find and
understand the required information is then very important.
Fn = Fn−1 + Fn−2 , F0 = 1, F1 = 1, n = 2, 3, . . .
Thus, the sequence starts out as
ln(en+1 /en )
q= .
ln(en /en−1 )
This is derived by considering the error model for 3 consecutive iterations,
dividing one equation by the other and solving for q. If then a series
of iterations is run, we can compute a sequence of values for q as the
iteration counter n increases. As n increases, the computed q values
are expected to approach the convergence rate that characterizes the
particular iterative method. For the ratio we are looking at here, the
convergence ratio is 1.
Extend your module with a function compute_rates, which takes
an array (or a list) F with (e.g., 20) Fibonacci numbers as input and
computes (and prints) the corresponding values for q. Call the function
14
https://en.wikipedia.org/wiki/Johannes_Kepler
5.7 Exercises 177
from the test block and run the program. Do the convergence rates
approach the expected value?
Later, in Chapter 6.6.2, you will learn that convergence rates are very
useful when testing (verifying) software.
Solution. The code may be written as
import numpy as np
def make_Fibonacci(N):
F = np.zeros(N+1, int)
F[0] = 1
F[1] = 1
for n in range(2, N+1, 1):
F[n] = F[n-1] + F[n-2]
return F
def converging_ratio(F):
golden_ratio = (1 + np.sqrt(5))/2 # exact answer of limit
print(’Ratios:’)
for n in range(0, len(F)-1, 1):
print(’Difference: {:g}’.format(abs(F[n+1]/F[n] - golden_ratio)))
return
def compute_rates(F):
N = len(F)
ratio = np.zeros(N)
golden_ratio = (1 + np.sqrt(5))/2 # exact answer of limit
for n in range(0, N-1, 1):
ratio[n+1] = F[n+1]/F[n]
if __name__ == ’__main__’:
N = 20
F = make_Fibonacci(N=N)
print(’F: {}’.format(F))
converging_ratio(F)
compute_rates(F)
Ratios:
Difference: 0.618034
Difference: 0.381966
178 5 Some more Python essentials
Difference: 0.118034
Difference: 0.0486327
Difference: 0.018034
Difference: 0.00696601
Difference: 0.00264937
Difference: 0.00101363
Difference: 0.00038693
Difference: 0.000147829
Difference: 5.64607e-05
Difference: 2.15668e-05
Difference: 8.23768e-06
Difference: 3.14653e-06
Difference: 1.20186e-06
Difference: 4.59072e-07
Difference: 1.7535e-07
Difference: 6.69777e-08
Difference: 2.55832e-08
Difference: 9.77191e-09
Convergence rates:
[0.50000000000000022, 2.4404200904125539, 0.75503055617987125,
1.1188262733771752, 0.95885034928008239, 1.0162993425577451,
0.99386095485285253, 1.0023574544293534, 0.99910136923846515,
1.0003435140407022, 0.99986882838273905, 1.0000501087954012,
0.99998086100207262, 1.0000073105520131, 0.9999972075672463,
1.0000010668003683, 0.99999959442072428, 1.000000145950763,
0.99999997066155721]
From the output, we note that the sequence of Fibonacci numbers appears
to be fine. Also, from the printed “difference”, it seems that Kepler was
right about the ratio of two consecutive numbers. Finally, the convergence
rate approaches 1, just as expected.
Filename: Fibonacci_numbers.py.
Apart from the header, each line represents one box. However, since they
all have the same bottom surface area, that area (10.0) is only given for
5.7 Exercises 179
the first box. For that first box, also the height (3.0) is given, as it is for
each of the following boxes.
a) Write down a formula for computing the total volume of all boxes
represented in the file. That formula should be written such that a
minimum of multiplications and additions is used.
Solution. Basically we have to sum up bottom surface area times height
for all boxes, and since the bottom surface area is the same, we have
a common factor. Thus, considering n boxes, our formula for the total
volume V becomes
n
V =A hi ,
i=1
filename = ’box_data.dat’
infile = open(filename, ’r’) # Open file for reading
line = infile.readline() # Read and skip first line
Running the program gives 170.0m3 as the total volume. This compares
favorably to what we get by hand.
180 5 Some more Python essentials
c) In the file box_data.dat, after the last line (containing the height of
the “last” box), insert a couple of empty lines, i.e. just press enter a few
times. Then, save the file and run the program anew. What happens?
Explain briefly.
Solution. Unless you already have handled this in your code, you get
an error message, since each line read is supposed to contain a height.
Filename: total_volume_boxes.py.
1
A= |(x1 y2 + x2 y3 + · · · + xn−1 yn + xn y1 ) − (y1 x2 + y2 x3 + · · · + yn−1 xn + yn x1 )| .
2
Write a function polyarea(x, y) that takes two coordinate arrays with
the vertices as arguments and returns the area.
5.7 Exercises 181
We may traverse this string, letter by letter, by the for loop for letter
in gene. The length of the string is given by len(gene), so an alternative
traversal over an index i is for i in range(len(gene)). Letter number
i is reached through gene[i], and a substring from index i up to, but
not including j, is created by gene[i:j].
a) Write a function freq(letter, text) that returns the frequency of
the letter letter in the string text, i.e., the number of occurrences of
letter divided by the length of text. Call the function to determine the
frequency of C and G in the gene string above. Compute the frequency
by hand too.
b) Write a function pairs(letter, text) that counts how many times
a pair of the letter letter (e.g., GG) occurs within the string text. Use
the function to determine how many times the pair AA appears in the
string gene above. Perform a manual counting too to check the answer.
c) Write a function mystruct(text) that counts the number of a certain
structure in the string text. The structure is defined as G followed by A
or T until a double GG. Perform a manual search for the structure too to
control the computations by mystruct.
Solution. Here is a program:
gene = ’AGTCAATGGAATAGGCCAAGCGAATATTTGGGCTACCA’
def mystruct(text):
counter = 0
for i in range(len(text)):
# Search for the structure from position i
if text[i] == ’G’:
print(’found G at {:d}’.format(i))
# Search among A and T letters
j = i + 1
5.7 Exercises 183
Filename: count_substrings.py.
Remarks. You are supposed to solve the tasks using simple program-
ming with loops and variables. While a) and b) are quite straightforward,
c) quickly involves demanding logic. However, there are powerful tools
available in Python that can solve the tasks efficiently in very compact
code: a) text.count(letter)/len(text); b) text.count(letter*2);
c) len(re.findall(’G[AT]+?GG’, text)). That is, there is rich func-
tionality for analysis of text in Python and this is particularly useful in
analysis of gene sequences.
n = 0
for d1, d2 in dice:
if d1 + d2 == 7:
n += 1
Filename: combine_sets.py.