Solutions#

Question 1#

1. Write a function that generates \(n!\).

There are two approaches to do this, the first is to use recursion:

def generate_n_factorial(n):
    """
    A function to give n! using recursion.

    Parameters
    ----------
    n: int
        The index of the number wanted.

    Returns
    -------
    int
        The value of n!.
    """
    if n == 0:
        return 1
    return n * generate_n_factorial(n - 1)

We can use this:

generate_n_factorial(5)
120

Here is another approach, to use iteration and making use of the actual expression for \(n!\):

\[ n! = \prod_{i=1}^ni \]
def generate_n_factorial_using_iteration(n):
    """
    A function to give n! using iteration.

    Parameters
    ----------
    n: int
        The index of the number wanted.

    Returns
    -------
    int
        The value of n!.
    """
    cumulative_product = 1
    for i in range(1, n + 1):
        cumulative_product *= i
    return cumulative_product

We can use this:

generate_n_factorial_using_iteration(5)
120

Question 2#

2. Write a function that generates the \(n\)th triangular numbers defined by: \( T_n = \frac{n(n+1)}{2} \)

def generate_triangular_numbers(n):
    """
    A function to give the nth triangular number defined by:

    $ T_n = \frac{n(n+1)}{2} $

    Parameters
    ----------
    n: int
        The index of the number wanted.

    Returns
    -------
    int
        The value of triangular number
    """
    return n * (n + 1) / 2

Question 3#

3. Verify the following that the following identify holds for positive integer values \(n\leq 500\): \( \sum\_{i=0}^n T_i = \frac{n(n+1)(n+2)}{6} \)

We will write a function to check the equality:

def check_equality(n):
    """
    Returns a boolean variable whether or not the equality is true for a given n

    Parameters
    ----------
    n: int
        The value for which the equality will be checked.

    Returns
    -------
    boolean
        Whether or not the equality holds.
    """
    lhs = sum(generate_triangular_numbers(i) for i in range(n + 1))
    rhs = n * (n + 1) * (n + 2) / 6
    return lhs == rhs

Using this we can check all the values of \(n\) as required:

all([check_equality(n) for n in range(1, 501)])
True

Note that we can also use all directly without creating the list first (this is similar to sum):

all(check_equality(n) for n in range(1, 501))
True

Question 4#

4. Consider the Monty Hall problem: 1. Write a function that simulates the play of the game when you ‘stick’ with the initial choice.

This corresponds to sampling from a list of three choices:

import random


def play_monty_hall_with_stick():
    """
    Instead of picking a random element we will shuffle it and return the first
    element.

    We make use of `random.shuffle` to shuffle and `pop()` to remove the first
    element of a list (and return it).

    Returns
    -------
    str
        Either "goat" or "car"
    """
    doors = ["goat", "goat", "car"]
    random.shuffle(doors)
    return doors.pop()

We can check that this gives expected behaviour:

random.seed(0)

play_monty_hall_with_stick()
'goat'
play_monty_hall_with_stick()
'goat'
play_monty_hall_with_stick()
'car'

2. Write a function that simulates the play of the game when you ‘change’ your choice.

This corresponds to sampling from a list of three choices, removing that choice and then sampling again:

def play_monty_hall_with_switch():
    """
    Instead of picking a random element we will shuffle it and return the first
    element.

    We make use of `random.shuffle` to shuffle and `pop()` to remove the first
    element of a list. We then use `remove()` to remove an element from a list.
    We then pop the first
    element of the remaining list (and return it).

    Returns
    -------
    str
        Either "goat" or "car"
    """
    doors = ["goat", "goat", "car"]
    random.shuffle(doors)
    doors.pop()
    doors.remove("goat")
    return doors.pop()
random.seed(0)

play_monty_hall_with_switch()
'car'
play_monty_hall_with_switch()
'car'
play_monty_hall_with_switch()
'goat'

3. Repeat the play of the game using both those functions and compare the probability of winning.

We can calculate the probability of winning the car when we stick:

repetitions = 100000
count_of_winning_with_stick = sum(
    play_monty_hall_with_stick() == "car" for repetition in range(repetitions)
)
probability_of_winning_with_stick = count_of_winning_with_stick / repetitions
probability_of_winning_with_stick
0.33317

We can calculate the probability of winning the car when we switch:

count_of_winning_with_switch = sum(
    play_monty_hall_with_switch() == "car" for repetition in range(repetitions)
)
probability_of_winning_with_switch = count_of_winning_with_switch / repetitions
probability_of_winning_with_switch
0.66627