- To help you learn some Python essentials and programming skills to enjoy this module.

**How it works:**

- Complete the notebook below, as follows. Click on a question, then from the menu select Insert -> Insert Cell Below. From the dropdown box (next to the "stop" symbol) choose whether you wish to insert 'Code' or 'Markdown'. Save regularly!
- Press Shift-Enter to execute the code in a cell.
- Submit the .ipynb file via https://somas-uploads.shef.ac.uk/mas212 by 23.59pm on Sun 13th Oct 2019.
*If you are off-campus, you will need to use a VPN to submit.* - Your lecturer will mark each question as either 2 (good), 1 (needs revision), 0 (not attempted/wrong).
- This is an open-book test, which means you may consult books, notes and internet resources.
**Do not discuss or share your test with anyone**. Copy-and-pasting is**not permitted**.*Please give answers in your own words.* - Some of these questions are relatively straightforward, and some are harder (e.g. Q20). Please don't spend any longer than a maximum of 4 hours on this test, as it is for a relatively small amount of credit.

**Key Resources:**

- Lecture materials and links on course web page: http://sam-dolan.staff.shef.ac.uk/mas212/

**1. Describe two key differences between the list and set data types.**

Lists are ordered, whereas sets are not. Lists allow duplicates, whereas sets do not.

**2. Describe one way in which a string is similar to a list, and one way in which it is different.**

(i) In Python, function arguments are passed *by value*.

(ii) Function arguments can be modified within functions.

(i) False. Function arguments are passed by reference in Python.

(ii) True. Function arguments of mutable type can be modified within functions. (And functions can also modify variables defined outside of the function, through the use of the global keyword).

*vectorization* in Python, and what benefits can it bring? Give two examples of *Universal Functions* in Python.

Vectorization is the coding practice of replacing the use of `for`

loops and lists with the use of arrays and Universal Functions (ufuncs), wherever possible. Universal Functions act on the whole array with a single statement, and they are typically implemented in C (a compiled language). This has the benefit of increasing the speed and efficiency of the code, as well as of reducing the number of lines of code.

Two examples of universal functions are `np.sin()`

and `np.abs`

. The unary operators (+, *, **, etc) are also universal functions.

*broadcasting* in the context of the numpy module?

What is the shape of A*B, if the arrays A and B have shapes (7,3,1) and (3,7,1,4)?

Broadcasting is a defined way of combining numpy arrays with different shapes so that, subject to certain constraints, the smaller array is “broadcast” across the larger array.

The shape of A*B in the example above is (3,7,3,4).

In [2]:

```
import math
[8+7, math.sqrt(3**3+5**3), math.tan(math.pi/8), (2-3*1j)**3]
```

Out[2]:

**7. Find the sum-of-cubes $1 + 8 + 27 + \ldots + 1000$.**

In [3]:

```
sum([k**3 for k in range(1,11)])
```

Out[3]:

**8. Print to screen the values of the square roots of $1,2, \ldots, 10$ to eight decimal places**.

In [4]:

```
for i in range(1,11):
print("The square root of %i is %.8f" % (i, math.sqrt(i)))
```

[N.B. The Fibonacci sequence is defined by $f_{k+1} = f_k + f_{k-1}$ with $f_{0} = f_{1} = 1$. The Golden Ratio is $\frac{1}{2}(\sqrt{5} + 1)$.]

In [6]:

```
f = [1,1]
nmax = 50
for i in range(2,nmax+1):
f.append(f[-1]+f[-2])
print("The ratio of successive Fibonacci numbers f[%i]/f[%i] is %.10f" % (nmax,nmax-1,f[-1]/f[-2]))
print("The Golden Ratio is %.10f" % (1/2*(math.sqrt(5)+1)))
```

`expfn(x)`

to calculate $e^x$ by truncating the series expansion
$$
e^x = \sum^{}_{n=0} \frac{x^n}{n!}
$$ at $n_{max} = 10$.

Test your function by calculating $e^{i \pi}$. How accurate is the result?

In [9]:

```
def expfn(x):
nmax=10
return sum([x**k/math.factorial(k) for k in range(0, nmax+1)])
y = expfn(1j*math.pi)
print("The true value of exp(i pi) is -1.")
print("The truncated series gives ", y)
print("The absolute error is ", (y + 1.0))
```

**11. Write a function to compute and display the first 10 rows of Pascal's triangle.**

In [13]:

```
def pascaltriangle(rows):
pasc = [1]
n = 100
for k in range(rows):
s2 = " ".join([repr(i) for i in pasc])
pad = " "*((n-len(s2))//2)
print(pad + s2 + pad) # add padding to centre this row
pasc = [1] + [pasc[k]+pasc[k+1] for k in range(len(pasc)-1)] + [1]
return
pascaltriangle(10)
```

`"me.txt"`

.

In [15]:

```
f = open("me.txt", "w")
f.write("Sam Dolan\n")
f.write("012345679")
f.close()
```

In [16]:

```
s = """Alice was beginning to get very tired of sitting by her sister on the bank, and of having nothing to do: once or twice she had peeped into the book her sister was reading, but it had no pictures or conversations in it, `and what is the use of a book,' thought Alice `without pictures or conversation?'
So she was considering in her own mind (as well as she could, for the hot day made her feel very sleepy and stupid), whether the pleasure of making a daisy-chain would be worth the trouble of getting up and picking the daisies, when suddenly a White Rabbit with pink eyes ran close by her."""
```

**13. How many times does the first letter of the alphabet appear in the string s?** (please count both uppercase 'A' and lowercase 'a').

In [17]:

```
s.count('a') + s.count('A')
```

Out[17]:

**14. How many words are there in the string? How many distinct words?**

In [21]:

```
# First I will remove the punctuation
letters = "abcdefghijklmnopqrstuvwxyz"
alphabet = letters + letters.upper() + " "
s1 = ""
for c in s:
if c in alphabet:
s1 += c
print(s1 + "\n")
# Now split into words using the space character.
words = s1.split(" ")
print("There are %i words." % len(words))
print("There are %i distinct words." % len(set(words)))
```

**15. What is the longest word in s, and how many characters does it have?**

In [29]:

```
longwords = [w for w in words if len(w) > 12]
longword = longwords[0]
print("The longest words is '%s' and it has %i characters." % (longword, len(longword)))
```

`numpy`

In [30]:

```
import numpy as np
```

In [31]:

```
A = np.arange(1,21).reshape(4,5)
print(A)
```

In [32]:

```
n = 12
row = np.arange(1,n+1).reshape((1,n))
col = np.arange(1,n+1).reshape((n,1))
A = row*col
print(A)
```

**18.** A square matrix $N$ is said to be **nilpotent** if $N^k = 0$ for some $k > 1$ (where e.g. $N^2 = N N$). **Demonstrate that the following matrix is nilpotent:**
$$
A = \begin{bmatrix} 2 & 2 & -2 \\ 5 & 1 & -3 \\ 1 & 5 & -3 \end{bmatrix}
$$

In [39]:

```
N = np.matrix("2 2 -2 ; 5 1 -3 ; 1 5 -3")
print(N*N*N)
# The matrix is nilpotent, as the cube of the matrix is zero (k=3).
```

`np.linalg`

, find the eigenvalues of the real symmetric matrix
$$
A = \begin{bmatrix} 1 & 2 & 3 \\ 2 & 4 & 1 \\ 3 & 1 & -5 \end{bmatrix} .
$$
Verify that the sum of the eigenvalues is zero.

In [44]:

```
A = np.matrix("1 2 3 ; 2 4 1 ; 3 1 -5")
eigs = np.linalg.eig(A)[0]
print("Eigenvalues : ", eigs)
print("The sum of the eigenvalues is %.10f" % sum(eigs))
```

`ciphertext`

using `keytext`

, to recover the melody as a string of letters, A to G.

From what piece of music is this melody taken? (*this last part not assessed*)

In [45]:

```
ciphertext = "SYFOMNHALYDMKVKJPUYUOBZDIJKSSGSVYRQHWNNERLPGPPWVYWDMHNJETVVZIM"
keytext = "MGASUSXDREASUITVPLIMRDEZUUULKWLIGLNXJRPZNUPXNOKJGJDUYRUYJIJEUQ"
```

In [55]:

```
s = ""
i0 = ord('A')
for k in range(0, len(ciphertext)):
i1 = ord(ciphertext[k]) - i0
i2 = ord(keytext[k]) - i0
i3 = (i1 + i2) % 26
s += chr(i3 + i0)
print("The list of notes is: ", s)
print("This is the melody from Ludwig van Beethoven's 'Ode to Joy' (9th symphony, movement 4).")
```

In [ ]:

```
```