- To help you learn some Python essentials and programming skills to enjoy the remainder of this course.

**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 http://somas-uploads.shef.ac.uk/mas212 by 23.59pm on Sun 8th Oct 2017.
*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.*

**Key Resources:**

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

**1. Describe the three numeric data types in Python 3. **

`int`

, `float`

and `complex`

. `int`

is an integer, a whole number, that is, any finite member of the set $\mathbb{Z}$. `float`

is a floating-point number: a representation of a member of the reals $\mathbb{R}$ with a finite precision for its mantissa and exponent. `complex`

is a representation of a complex number in $\mathbb{C}$: it can be thought of as a pair of `float`

s, representing its real and imaginary parts.

`list`

and `ndarray`

types).

Some key differences between lists and arrays include the following:

- Arrays are homogeneous, whereas lists are heterogeneous. In other words, all elements of an array must have the same data type, whereas elements of lists may have different data types.
- Arrays are typically fixed in size, whereas lists can be lengthened or shortened.
- Arrays are typically contiguous chunks of memory, whereas lists are not.
- Arrays may be operated on with universal functions (ufuncs, see below), which operate on a whole array in an efficient manner. This enables vectorizaton. Universal functions are not available for lists.
- Arrays can be multi-dimensional, whereas lists are one-dimensional (though lists-of-lists can be used to mimic multi-dimensional structures).

**3. What does 'mutable' mean? Give three examples of data types that are mutable.** (N.B. Common data types include

`int, list, tuple, dict, set, string, float`

).</b>`list`

, `dict`

and `set`

are mutable, whereas int, tuple, string and float are not.

**4. What is a Universal Function? (in the context of the numpy module)**

`numpy`

includes many `ufunc`

s, which, under the hood, are typically implemented in C (a low-level compiled language) to improve their speed and efficiency.
A example `ufunc`

(`np.log()`

) is shown below:

In [1]:

```
import numpy as np
A = np.linspace(1.0, 10.0, 20)
np.log(A)
```

Out[1]:

`numpy`

module).

(b) What is the shape of `A*B`

, if the arrays `A`

and `B`

have shapes `(1,3,4)`

and `(2,5,3,1)`

?

(Please answer the following questions in "code" cells).

**6. Python as a calculator: Calculate $6 - 2$, $(3^2 + 5^2)^{1/3}$, $\tan(\pi/8)$ and $(3 + 5i)^{3}$.**

In [2]:

```
import math
[6-2, (3**2+5**2)**(1/3.0), math.tan(math.pi/8.0), complex(3,5)**3]
```

Out[2]:

In [3]:

```
sum([1.0/k**2 for k in range(1,11)])
```

Out[3]:

**8. Print the value of $\ln(2)$ to 9 significant figures. **

In [4]:

```
print("%.9f" % math.log(2.0))
```

**9. Using the formula below, calculate Euler's number $e$ to an accuracy of 10 significant figures.**
$$
e = \sum_{n=0}^{\infty} \frac{1}{n!}
$$

In [5]:

```
tot = 1 # the running total for the sum
fac = 1 # the running total for the factorial
for k in range(1,100):
fac = fac*k
if fac > 10**10: # if the next term in the series is smaller than 1e-10 we may stop the loop
break
tot += 1.0/float(fac)
print("e = %.9f" % tot)
```

**10. Use the decimal package to display $e$ to 50 decimal places.**

In [6]:

```
from decimal import Decimal, getcontext
getcontext().prec = 51
Decimal(1).exp()
```

Out[6]:

`sumdigits(N)`

to compute the sum of the digits of a number. For example, `sumdigits(1234)`

would return `10`

(as 1+2+3+4=10).

Use the function to compute the sum of the digits in your registration number.

In [7]:

```
def sumdigits(N):
l = [int(s) for s in str(N)]
return sum(l)
sumdigits(123456789) # supposing my reg number were 123456789
```

Out[7]:

In [8]:

```
def NR_iterate(x):
"""Carry out one step of NR method for this polynomial"""
return x - (x**3 - 5*x - 1) / (3.0*x**2 - 5.0)
xs = [3.0] # initial guess
for k in range(100):
x = NR_iterate(xs[-1])
if abs(x - xs[-1]) < 1.0e-11: # Check whether accuracy criterion is satisfied; if so, break out of loop
print("after %d iterations:" % k)
break
xs.append(x)
print("%.10f" % xs[-1])
```

In [9]:

```
s = "It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of light, it was the season of darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way. In short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only."
print(s)
```

In [17]:

```
s1 = s.lower().replace(",", "").replace(".", "") # remove punctuation
words = s1.split(" ")
words.count("it")
```

Out[17]:

**14. How many words are in this paragraph? How many different words?**

In [18]:

```
s1 = s.lower().replace(",", "").replace(".","") # Remove punctuation
words = s1.split(" ") # list of words
print(len(words)) # number of words
print(len(set(words))) # number of distinct words
```

In [22]:

```
alphabet = "abcdefghijklmnopqrstuvwxyz"
counts = [s.lower().count(c) for c in alphabet]
for i in range(len(alphabet)):
print("%s appears %d times" % (alphabet[i], counts[i]))
```

So 't' is the next most frequently-occurring letter. It appears 48 times.

In [23]:

```
import numpy as np
```

In [24]:

```
# Here I make this from a list-of-lists. Other ways are possible
A = np.array([[j+k for j in range(1,11)] for k in range(0,5)])
print(A)
```

`A B`

where
$$
A = \begin{bmatrix} 5 & 6 & 2 \\ 1 & -1 & \frac{3}{4} \end{bmatrix} , \quad \quad
B = \begin{bmatrix} 1 & 2 \\ 3 & -4 \\ 5 & 0 \end{bmatrix}
$$

In [25]:

```
A = np.matrix("5,6,2 ; 1,-1,0.75") # an easy way to define a matrix
B = np.matrix("1,2 ; 3,-4 ; 5,0")
C=A*B
print(C)
```

In [26]:

```
A = np.matrix("-1,2,3 ; 2,4,1 ; 3,1,-3")
eigs = np.linalg.eig(A)[0]
print("The eigenvalues are %.6f, %.6f and %.6f" % tuple(eigs))
print("The sum of eigenvalues is %.4e, which is zero to within machine precision." % sum(eigs))
```

In [27]:

```
def scramble(s, keyword="aardvark"):
"""Scrambles a string."""
n = len(keyword)
s2 = ""
for k in range(len(s)):
c = s[k]
cnew = c
if c in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ':
k = (ord(c) + ord(keyword.upper()[k % n]) - 2*65) % 26
cnew = chr(65 + k)
elif c in 'abcdefghijklmnopqrstuvwxyz':
k = (ord(c) + ord(keyword.lower()[k % n]) - 2*97) % 26
cnew = chr(97 + k)
s2 = s2 + cnew
return s2
scramble("Hello everyone, blah blah blah.")
```

Out[27]:

**19. Describe in your own words what the scramble function does.**

The `scramble`

function encrypts a message using a keyword. It is a version of a Vigenère cipher, which is a method of encrypting alphabetic text by using a series of different Caesar ciphers based on the letters of a keyword (https://en.wikipedia.org/wiki/Vigenère_cipher).

To see how this works, we can line up the characters in the plaintext with the characters in the keyword and the encrypted message, as below :

```
Hello everyone, blah blah blah.
aardvarkaardvarkaardvarkaardvar
Hecoj vferprie, blrk bckh sovh.
```

The ciphertext is generated from the plaintext by using the characters in the keyword to move letters "around the letter wheel". Note that 'a' in the keyword leads to no change, whereas 'r' moves on by 17 (as 'r' is the 18th letter of the alphabet). Punctuation is not affected.

**20. Write a function unscramble to reverse its effect.**

Use your function to decipher the following quote by a famous literary detective. From which novel is it taken?</b>

In [30]:

```
txt = "Hon jfkon ydqe S srly ky yfx tykt nkzn iou kvvv elzpdnrded wce smpfvnisve, zcakovei medkinj, hfgevvu idzrosdwlv, mlvo so tyh tieth?"
```

In [31]:

```
# One way would be to copy-and-paste the scramble function and change some signs in the right places.
# An alternative way is to deduce a new keyword that generates the inverse transformation.
def unscramble(s, keyword="aardvark"):
reversekey = "".join([chr(((-(ord(c) - 97)) % 26) + 97) for c in keyword])
return scramble(s, reversekey)
unscramble(txt)
```

Out[31]:

This is a line spoken by Sherlock Holmes in "The Sign of the Four" (1890) by Arthur Conan Doyle.

In [ ]:

```
```