Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Sol CH 4

Download as pdf or txt
Download as pdf or txt
You are on page 1of 17

128 4 Functions and the writing of code

The constructions met in this chapter, and the previous chapter, are
characterized by a grouping of statements that generally span several
lines (although it is possible to write simpler cases on a single line,
when statements are separated with a semi-colon). Such construc-
tions are often referred to as compound statements, having headers
(ending with a colon) and bodies (with indented statements)a .
Interactive handling of compound statements is straight forward.
For example, a for loop may be written (and executed) like
In [1]: for i in [1, 2, 3]: # write header, press enter
...: print(i) # indent comes automatically
...: # press only enter, i.e., finished
1
2
3

When the header has been typed in and we press enter, we are
automatically given the indent on the next line. We can then proceed
directly by writing print(i) and press enter again. We then want
to finish our loop, which is understood when we simply press enter,
writing nothing else.
a
https://docs.python.org/3/reference/compound_stmts.html

4.3 Exercises

Exercise 4.1: Errors with colon, indent, etc.


Write the program ball_function.py as given in the text and confirm
that the program runs correctly. Then save a copy of the program and
use that program during the following error testing.
You are supposed to introduce errors in the code, one by one. For
each error introduced, save and run the program, and comment how well
Python’s response corresponds to the actual error. When you are finished
with one error, re-set the program to correct behavior (and check that it
works!) before moving on to the next error.
a) Change the first line from def y(v0, t): to def y(v0, t), i.e.,
remove the colon.
Solution. Running the program gives a syntax error:
def y(v0, t)
4.3 Exercises 129

^
SyntaxError: invalid syntax

Python repeats the line where it found a problem and then tells us that
the line has a syntax error. It is up to the programmer to find the error,
although a little "hat" is used to show were in the line Python thinks the
problem is. In this case, that "hat" is placed underneath where the colon
should have been placed.
b) Remove the indent in front of the statement g = 9.81 inside the
function y, i.e., shift the text four spaces to the left.
Solution. Running the program gives an indentation error:
g = 9.81
^
IndentationError: expected an indented block

Python repeats the line where it suspects a missing indentation. In the


error message, Python refers to indentation of a "block", meaning that it
expects all the code lines of the function body to be indented. Here, it
means that g = 9.81 should be shifted to the right (indented).
c) Now let the statement g = 9.81 inside the function y have an indent
of three spaces (while the remaining two lines of the function have four).
Solution. Running the program gives an indentation error, differing
slightly from the one we just experienced above:
return v0*t - 0.5*g*t**2
^
IndentationError: unexpected indent

Python repeats the line where it found an unexpected indentation. The


thing is that the first line (here g = 9.81) sets the minimum indentation
for the function. If larger indents are to be used for succeeding lines
(within the function), it must be done according to syntax rules (see the
text). In the present case, indenting return v0*t - 0.5*g*t**2 violates
the rules. Larger indents would be relevant, e.g., for the statements within
a for loop.
d) Remove the left parenthesis in the first statement def y(v0, t):
Solution. Running the program gives a syntax error:
def yv0, t):
^
SyntaxError: invalid syntax

Python repeats the line where it found the syntax problem and states
that (somewhere in this line) there is a syntax error.
130 4 Functions and the writing of code

e) Change the first line of the function definition from def y(v0, t):
to def y(v0):, i.e., remove the parameter t (and the comma).
Solution. Running the program gives a type error:
print(y(v0, time))

TypeError: y() takes 1 positional argument but 2 were given

Python repeats the line where it found a syntax problem and tells us
that the function y is used in the wrong way, since two arguments were
used when calling it. To Python, this is the logical way of responding,
since Python chooses to believe that our definition of the function was
correct. This definition (which actually is what is wrong!) states that
the function takes only one parameter. By comparing with the function
definition, it is up to the programmer to understand whether such an
error is in the function definition (as here) or in the function call.
f) Change the first occurrence of the command print(y(v0, time))
to print(y(v0)).
Solution. Running the program gives a type error:
print(y(v0))

TypeError: y() missing 1 required positional argument: ’t’

Python repeats the line where it found a syntax problem and tells us that
the function y is used in the wrong way, since one positional argument
is missing in the call. Again, Python discovered a mismatch between
function definition and use of the function. Now, the definition specifies
two positional parameters, whereas the call uses only one.
Filename: errors_colon_indent_etc.py.

Exercise 4.2: Reading code 1


a) Read the following code and predict the printout produced when the
program is executed.
def f(x):
return x**2
def g(x):
return 2*x

for x in [1, 5, 6, 9]:


if x < 5:
# case 1
4.3 Exercises 131

print(f(x))
elif x == 5:
# case 2
print(2*f(x))
elif x > 5 and x < 8:
# case 3
print(f(x+4) + g(x*2) - g(2))
else:
# case 4
y = x + 2
print(g(y))

Solution. The following numbers are printed: 1, 50, 120, 22, in this
order.
b) Type in the code and run the program to confirm that your predictions
are correct.
Filename: read_code_1.py.

Exercise 4.3: Reading code 2


a) Read the following code and predict the printout produced when the
program is executed.
def f(x):
if x < 2:
return 0
else:
return 2*x

x = 0
for i in range(0, 4, 1):
x += i
print(x)

for i in range(0, 4, 1):


x += i*i
print(x)

for i in range(0, 4, 1):


print(f(3*i-1))

Solution. The following numbers are printed: 0, 1, 3, 6, 20, 0, 4, 10, 16,


in this order.
b) Type in the code and run the program to confirm that your predictions
are correct.
Filename: read_code_2.py.
132 4 Functions and the writing of code

Exercise 4.4: Functions for circumference and area of a circle


Write a program that takes a circle radius r as input from the user and
then computes the circumference C and area A of the circle. Implement
the computations of C and A as two separate functions that each takes r
as input parameter. Print C and A to the screen along with an appropriate
text. Run the program with r = 1 and confirm that you get the right
answer.
Solution. The code reads:
import math as m

def circumference(r):
return 2*m.pi*r
def area(r):
return m.pi*r**2

r = float(input(’Give the radius of a circle: ’))


C = circumference(r)
A = area(r)
print(’Circumference: {:g} , Area: {:g}’.format(C, A))

Running the program, choosing r = 1, gives the following dialog:


Give the radius of a circle: 1
Circumference: 6.28319 , Area: 3.14159

Filename: functions_circumference_area.py.

Exercise 4.5: Function for adding vectors


Write a function add_vectors that takes 2 vectors (arrays with one
index are often called vectors) a and b as input arguments and returns
the vector c, where c = a + b. Place the function in a little program that
calls the function and prints the result. The function should check that
a and b have the same length. If not, None should be returned. Confirm
that your function works by comparing to hand calculations (i.e., just
choose some arrays and test).
Solution. The code reads:
import numpy as np

def add_vectors(a, b):


’’’Vectors a and b are added if equally long’’’
if len(a) == len(b):
c = np.zeros(len(a))
4.3 Exercises 133

for i in range(0, len(a), 1):


c[i] = a[i] + b[i]
return c
else:
return None

x = np.array([1, 3, 5])
y = np.array([2, 4, 6])
z = np.array([2, 4])

print(add_vectors(x, y)) # gives 3., 7. and 11.


print(add_vectors(x, z)) # gives None

Filename: add_vectors.py.

Exercise 4.6: Function for area of a rectangle


Write a program that computes the area A = bc of a rectangle. The
values of b and c should be user input to the program. Also, write the
area computation as a function that takes b and c as input parameters
and returns the computed area. Let the program print the result to
screen along with an appropriate text. Run the program with b = 2 and
c = 3 to confirm correct program behavior.
Solution. The code reads:
def area(s1, s2):
return s1*s2

b = float(input(’Give the one side of the rectangle: ’))


c = float(input(’Give the other side of the rectangle: ’))
print(’\nArea is {:g}’.format(area(b, c)))

Running the program, gives the following dialog:


Give the one side of the rectangle: 2
Give the other side of the rectangle: 3
Area is 6

Filename: function_area_rectangle.py.

Exercise 4.7: Average of integers


Write a program that gets an integer N > 1 from the user and computes
the average of all integers i = 1, . . . , N . The computation should be
done in a function that takes N as input parameter. Print the result to
the screen with an appropriate text. Run the program with N = 5 and
confirm that you get the correct answer.
134 4 Functions and the writing of code

Solution. The code reads:


def average(N):
sum = 0
for i in range(1, N+1): # Note: Must use ‘N+1‘ to get ‘N‘
sum += i
return sum/N

N = int(input(’Give an integer > 1: ’))


average_1_to_N = average(N)
print(’The average of 1,..., {:d} is: {:g}’.format(N, average_1_to_N))

Running the program, using N = 5, gives the following dialog:


Give an integer > 1: 5
The average of 1,..., 5 is: 3

Filename: average_1_to_N.py.

Exercise 4.8: When does Python check function syntax?


You are told that, when Python reads a function definition for the first
time, it does not execute the function, but still checks the syntax.
Now, you come up with some code lines to confirm that this is the
case.
Hint. You may, for example, use a print command and a deliberate syntax
error in a modification of ball_function.py (note that the modified
code is one of those quick “one-time” tests you might make for yourself,
meant to be deleted once you have the answer).
Solution. You might do the following. In a copy of the program, edit g =
9.81 to g = inside the function (i.e., introduce a syntax error) and insert
a printout of, e.g., the word “hello” in main after the function definition.
Then, Python will report syntax error in the function, and stop without
printing hello, showing that syntax is checked when a function definition
is read the first time.
Filename: when_check_function_syntax.py.

Exercise 4.9: Find crossing points of two graphs


Consider two functions f (x) = x and g(x) = x2 on the interval [−4, 4].
Write a program that, by trial and error, finds approximately for which
values of x the two graphs cross, i.e., f (x) = g(x). Do this by considering
4.3 Exercises 135

N equally distributed points on the interval, at each point checking


whether |f (x) − g(x)| < , where  is some small number. Let N and 
be user input to the program and let the result be printed to screen. Run
your program with N = 400 and  = 0.01. Explain the output from the
program. Finally, try also other values of N , keeping the value of  fixed.
Explain your observations.
Solution. The code reads:
import numpy as np

def f(x):
return x

def g(x):
return x**2

N = int(input(’Give the number of check points N: ’))


epsilon = float(input(’Give the error tolerance: ’))
x_values = np.linspace(-4, 4, N)

# Next, we run over all indices in the array ‘x_values‘ and


# check if the difference between function values is smaller than
# the chosen limit

for i in range(N):
if abs(f(x_values[i]) - g(x_values[i])) < epsilon:
print(x_values[i])

Running the program with 400 check-points (i.e. N = 400) and an error
tolerance of 0.01 (i.e. epsilon = 0.01) gives the following dialog:
Give the number of check-points N: 400

Give the error tolerance: 0.01


0.0100250626566
0.992481203008

We note that we do not get exactly 0 and 1 (which we know are


the answers). This owes to the chosen distribution of x-values. This
distribution is decided by N. Trying other combinations of N and epsilon
might give more than 2 "solutions", or fewer, maybe even none. All of
this boils down to whether the if test becomes true or not. For example,
if you let epsilon stay constant while increasing N, you realize that the
difference between f (x) and g(x) will be small for several values of x,
allowing more than one x value to "be a solution". Decreasing N while
epsilon is constant will eventually give no solutions, since the difference
between f (x) and g(x) at the tested x-values gets too large.
136 4 Functions and the writing of code

Is is important here to realize the difference between the numerical


test we do and the exact solution. The numerical test just gives us an
approximation which we may get as "good as we want" by the right
choices of N and epsilon.
Filename: crossing_2_graphs.py.

Exercise 4.10: Linear interpolation (...for the Assignment)


Some measurements yi , i = 0, 1, . . . , N , of a quantity y have been collected
regularly, once every minute, at times ti = i, i = 0, 1, . . . , N . We want
to find the value y in between the measurements, e.g., at t = 3.2 min.
Computing such y values is called interpolation.
Let your program use linear interpolation to compute y between two
consecutive measurements:
1. Find i such that ti ≤ t ≤ ti+1 .
2. Find a mathematical expression for the straight line that goes through
the points (i, yi ) and (i + 1, yi+1 ).
3. Compute the y value by inserting the user’s time value in the expression
for the straight line.

a) Implement the linear interpolation technique in a function


interpolate that takes as input an array with the yi measurements,
the time between them Δt, and some time t, for which the interpolated
value is requested. The function should return the interpolated y value
at time t.
Solution. See the function interpolate in the script below
b) Write another function find_y that finds and prints an interpolated
y value at times requested by the user. Let find_y use a loop in which
the user is asked for a time on the interval [0, N ]. The loop can terminate
when the user gives a negative time.
Solution. See the function find_y in the script below
c) Use the following measurements: 4.4, 2.0, 11.0, 21.5, 7.5, corresponding
to times 0, 1, . . . , 4 (min), and compute interpolated values at t = 2.5
and t = 3.1 min. Perform separate hand calculations to check that the
output from the program is correct.
4.3 Exercises 137

Solution. The code may be written as follows


import numpy as np

def interpolate(y, dt, t):


"""Uses linear interpolation to find intermediate y"""
i = int(t)
# Scheme: y(t) = y_i + delta-y/delta-t * dt
return y[i] + ((y[i+1] - y[i])/dt)*(t-i)

def find_y(y, dt):


"""Repeatedly finds function value at t by interpolation.
y is array with given measurement data, dt is time between these"""
print(’For time t on the interval [0,{:d}]...’.format(N))
t = float(input(’Give your desired t > 0: ’))
while t >= 0:
print(’y(t) = {:g}’.format(interpolate(y, dt, t)))
t = float(input(’Give new time t (to stop, enter t < 0): ’))

# Note: do not need to store the sequence of times


N = 4 # Total number of measurements
dt = 1.0 # Time difference between measurements

y = np.zeros(5)
y[0] = 4.4; y[1] = 2.0; y[2] = 11.0;
y[3] = 21.5; y[4] = 7.5

find_y(y, dt)

Running the program may produce this dialog


For time t on the interval [0,4]...
Give your desired t: 2.5
y(t) = 16.25

Give new time t: 0.5


y(t) = 3.2

Give new time t: -1

Filename: linear_interpolation.py.

Exercise 4.11: Test straight line requirement


Assume the straight line function f (x) = 4x + 1. Write a script that tests
the “point-slope” form for this line as follows. Within a chosen interval
on the x-axis (for example, for x between 0 and 10), randomly pick 100
points on the line and check if the following requirement is fulfilled for
each point:
138 4 Functions and the writing of code

f (xi ) − f (c)
= a, i = 1, 2, . . . , 100 ,
xi − c
where a is the slope of the line and c defines a fixed point (c, f (c)) on
the line. Let c = 2 here.
Solution. The code may be written as follows
"""
For a straight line f(x) = ax + b, and the fixed point (2,f(2)) on
the line, the script tests whether (f(x_i) - f(2)) / (x_i - 2) = a
for randomly chosen x_i, i = 1,...,100.
"""

from random import random

def f(x):
return a*x + b

a = 4.0; b = 1.0
c = 2; f_c = f(c) # Fixed point on the line
epsilon = 1e-6
i = 0
for i in range(100):
x = 10*random() # random() returns number between 0 and 1
numerator = f(x) - f_c
denominator = x - c
if denominator > epsilon: # To avoid zero division
fraction = numerator/denominator
# The following printout should be very close to zero in
# each case if the points are on the line
print(’For x = {:g} : {:g}’.format(x,abs(fraction - a)))

The potential problem of zero division is here simply handled by the if


test, meaning that if the denominator is too close to zero, that particular
x is skipped. A more elegant procedure would be to use a try-except
construction.
Running the program generates a printout of 100 lines that for each x
drawn gives 0 as result from the test. The two last lines of the printout
read:
For x = 2.67588 : 0
For x = 9.75893 : 0

Note that since the x values are (pseudo-)random in nature, a second


run gives different values for x (but still 0 for each test!).
Filename: test_straight_line.py.
4.3 Exercises 139

Exercise 4.12: Fit straight line to data (...for the Assignment)


Assume some measurements yi , i = 1, 2, . . . , 5 have been collected, once
every second. Your task is to write a program that fits a straight line to
those data.
a) Make a function that, for given measurements and parameter values
a and b, computes the error between the straight line f (x) = ax + b and
the measurements:
5

e= (axi + b − yi )2 .
i=1

Solution. See the function find_error in the script below.


b) Make a function that, in a loop, asks the user to give a and b for the
line. The corresponding value of e should then be computed and printed
to screen, and a plot of the straight line f (x) = ax + b, together with
the discrete measurements, should be produced.
Solution. See the function interactive_line_fit in the script below.
c) Given the measurements 0.5, 2.0, 1.0, 1.5, 7.5, at times 0, 1, 2, 3, 4, use
the function in b) to interactively search for a and b such that e is
minimized.
Solution. The code may be written as follows
import numpy as np
import matplotlib.pyplot as plt

def f(t,a,b):
return a*t + b

def find_error(a, b):


E = 0
for i in range(len(time)):
E += (f(time[i],a,b) - data[i])**2
return E

def interactive_line_fit():
one_more = True
while one_more:
a = float(input(’Give a: ’))
b = float(input(’Give b: ’))
print(’The error is: {:g}’.format(find_error(a, b)))
y = f(time, a, b)
plt.plot(time, y, time, data, ’*’)
plt.xlabel(’Time (s)’)
plt.ylabel(’y (stars) and straight line f(t)’)
4.3 Exercises 141

Fig. 4.2 Straight line fitted to data with second choice of line parameters (a and b).

Remarks. Fitting a straight line to measured data points is a very


common task. The manual search procedure in c) can be automated by
using a mathematical method called the method of least squares.

Exercise 4.13: Fit sines to straight line


A lot of technology, especially most types of digital audio devices for
processing sound, is based on representing a signal of time as a sum of
sine functions. Say the signal is some function f (t) on the interval [−π, π]
(a more general interval [a, b] can easily be treated, but leads to slightly
more complicated formulas). Instead of working with f (t) directly, we
approximate f by the sum
N

SN (t) = bn sin(nt), (4.1)
n=1

where the coefficients bn must be adjusted such that SN (t) is a good


approximation to f (t). We shall in this exercise adjust bn by a trial-and-
error process.
a) Make a function sinesum(t, b) that returns SN (t), given the coeffi-
cients bn in an array b and time coordinates in an array t. Note that if
t is an array, the return value is also an array.
142 4 Functions and the writing of code

Solution. See the script below.


b) Write a function test_sinesum() that calls sinesum(t, b) in a)
and determines if the function computes a test case correctly. As test
case, let t be an array with values −π/2 and π/4, choose N = 2, and
b1 = 4 and b2 = −3. Compute SN (t) by hand to get reference values.
Solution. See the script below. Note that the call to test_sinesum is
commented out, but the function will step into action if the leading # is
removed.
c) Make a function plot_compare(f, N, M) that plots the original
function f (t) together with the sum of sines SN (t), so that the quality
of the approximation SN (t) can be examined visually. The argument f
is a Python function implementing f (t), N is the number of terms in the
sum SN (t), and M is the number of uniformly distributed t coordinates
used to plot f and SN .
Solution. See the script below.
d) Write a function error(b, f, M) that returns a mathematical mea-
sure of the error in SN (t) as an approximation to f (t):

E= (f (ti ) − SN (ti ))2 ,
i

where the ti values are M uniformly distributed coordinates on [−π, π].


The array b holds the coefficients in SN and f is a Python function
implementing the mathematical function f (t).
Solution. See the script below.
e) Make a function trial(f, N) for interactively giving bn values and
getting a plot on the screen where the resulting SN (t) is plotted together
with f (t). The error in the approximation should also be computed as
indicated in d). The argument f is a Python function for f (t) and N is the
number of terms N in the sum SN (t). The trial function can run a loop
where the user is asked for the bn values in each pass of the loop and the
corresponding plot is shown. You must find a way to terminate the loop
when the experiments are over. Use M=500 in the calls to plot_compare
and error.
Solution. See the script below. Note that the call to trial is commented
out, but the function will run if the leading # is removed.
4.3 Exercises 143

f) Choose f (t) to be a straight line f (t) = π1 t on [−π, π]. Call trial(f,


3) and try to find through experimentation some values b1 , b2 , and b3
such that the sum of sines SN (t) is a good approximation to the straight
line.
Solution. See the function trial in the script below.
g) Now we shall try to automate the procedure in f). Write a function
that has three nested loops over values of b1 , b2 , and b3 . Let each loop
cover the interval [−1, 1] in steps of 0.1. For each combination of b1 , b2 ,
and b3 , the error in the approximation SN should be computed. Use this
to find, and print, the smallest error and the corresponding values of
b1 , b2 , and b3 . Let the program also plot f and the approximation SN
corresponding to the smallest error.
Solution. The code may be written as follows
from numpy import zeros, linspace, sin, sqrt, pi, copy, arange
import matplotlib.pyplot as plt

def sinesum(t, b):


"""
Computes S as the sum over n of b_n * sin(n*t).
For each point in time (M) we loop over all b_n to
produce one element S[M], i.e. one element in
S corresponds to one point in time.
"""
S = zeros(len(t))
for M in range(0, len(t), 1):
for n in range(1, len(b)+1, 1):
S[M] += b[n-1]*sin(n*t[M])
return S

def test_sinesum():
t = zeros(2); t[0] = -pi/2; t[1] = pi/4
b = zeros(2); b[0] = 4.0; b[1] = -3
print(sinesum(t, b))

def plot_compare(f, N, M):


time = linspace(left_end, right_end, M)
y = f(time)
S = sinesum(time, b)
plt.plot(time, y, ’b-’, time, S, ’r--’)
plt.xlabel(’Time’)
plt.ylabel(’f (blue) and S (red)’)
plt.show()

def error(b, f, M):


time = linspace(left_end, right_end, M)
y = f(time)
S = sinesum(time, b)
144 4 Functions and the writing of code

E = 0
for i in range(len(time)):
E += sqrt((y[i] - S[i])**2)
return E

def trial(f, N):


M = 500
new_trial = True
while new_trial:
for i in range(N):
text = ’Give b’ + str(i+1) + ’ : ’
b[i] = input(text)
plot_compare(f, N, M)
print(’The error is: {:g}’.format(error(b, f, M)))
answer = input(’Another trial (y/n)? ’)
if answer == ’n’:
new_trial = False

def f(t):
return (1/pi)*t

def automatic_fit(f, N):


"""Search for b-values, - just pick limits and step"""
global b
M = 500
# Produce and store an initially "smallest" error
b[0] = -1; b[1] = -1; b[2] = -1
test_b = copy(b)
smallest_E = error(test_b, f, M)
db = 0.1
for b1 in arange(-1, 1+db, db):
for b2 in arange(-1, 1+db, db):
for b3 in arange(-1, 1+db, db):
test_b[0] = b1; test_b[1] = b2;
test_b[2] = b3
E = error(test_b, f, M)
if E < smallest_E:
b = copy(test_b)
smallest_E = E
plot_compare(f, N, M)
print(’The b coeffiecients:\n’); print(b)
print(’The smallest error found: {:g}’.format(smallest_E))

left_end = -pi; right_end = pi


N = 3
b = zeros(N)
#test_sinesum()
#trial(f, N)
automatic_fit(f, N)

Running the program may produce this dialog


The b coefficients: [ 0.6 -0.2 0.1 ]
The smallest error found: 67.1213886326
4.3 Exercises 145

and the plot seen in Figure 4.3

Fig. 4.3 Straight line fitted to data with first choice of line parameters (a and b).

Filename: fit_sines.py.
Remarks.
1. The function SN (x) is a special case of what is called a Fourier series.
At the beginning of the 19th century, Joseph Fourier (1768-1830)
showed that any function can be approximated analytically by a sum
of cosines and sines. The approximation improves as the number of
terms (N ) is increased. Fourier series are very important throughout
science and engineering today.
a. Finding the coefficients bn is solved much more accurately in
Exercise 6.12, by a procedure that also requires much less human
and computer work!
b. In real applications, f (t) is not known as a continuous function,
but function values of f (t) are provided. For example, in digital
sound applications, music in a CD-quality WAV file is a signal
with 44100 samples of the corresponding analog signal f (t) per
second.

You might also like