A document from MCS 275 Spring 2024, instructor Emily Dumas. You can also get the notebook file.

MCS 275 Spring 2024 Homework 4 Sample Solution

Emily Dumas

The format of this set of solutions is different from the usual ones, as the assignment explicitly requested a notebook with specific formatting.

Problem 2

Preliminary imports

In [1]:
import time

The context manager requested in the problem

In [2]:
class TimedBlock:
    """
    Context manager that tracks and prints total time 
    spent in the with-block
    """
    def __init__(self,title):
        "Initialize a named, timed block context manager"
        self.title = title
    def __enter__(self):
        "Display message and begin timing"
        print("Starting timed block '{}'".format(self.title))
        self.t0 = time.time()
    def __exit__(self,a,b,c):
        "Compute and print elapsed time"
        self.t1 = time.time()
        print("Exited timed block '{}' after {:.4f}s".format(self.title,self.t1-self.t0))

Running the given sample code with this context manager to show it works as intended.

In [3]:
# Sample usage to time a bunch of calculations
with TimedBlock("Gigantic integer arithmetic"):
    digits = 0
    for k in range(5000):
        x = 3**k - 2**k
        digits += len(str(x))
    print("Total of {} digits".format(digits))
Starting timed block 'Gigantic integer arithmetic'
Total of 5965321 digits
Exited timed block 'Gigantic integer arithmetic' after 0.1078s
In [5]:
# Sample usage showing we still get timing data if the with-block
# raises an exception
with TimedBlock("Float arithmetic that eventually fails"):
    for k in range(5000):
        x = 1 / (3**(4000-k) - 2**(4000-k))
Starting timed block 'Float arithmetic that eventually fails'
Exited timed block 'Float arithmetic that eventually fails' after 0.0106s
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
/tmp/ipykernel_1525377/973717916.py in <module>
      3 with TimedBlock("Float arithmetic that eventually fails"):
      4     for k in range(5000):
----> 5         x = 1 / (3**(4000-k) - 2**(4000-k))

ZeroDivisionError: division by zero
In [6]:
# Sample usage showing output is only printed when we enter the with-block
# and NOT when the class is instantiated
B = TimedBlock("Notice the constructor doesn't print anything")
In [7]:
# Sample usage showing that timing begins when the with-block begins
# and NOT whent he TimedBlock constructor is called.
B = TimedBlock("Should take about 0.5 seconds")
# The next delay isn't part of the timing
time.sleep(1.5)
with B:
    # This delay IS part of the timing
    time.sleep(0.5)
Starting timed block 'Should take about 0.5 seconds'
Exited timed block 'Should take about 0.5 seconds' after 0.5026s

Problem 4A

In [8]:
def DRB(n):
    """
    Recursive implementation of 'digital recursion breath' sequence
    """
    if n<=2:
        return 1
    d = len(str(n))
    x = 0
    for i in range(d+2):
        x += DRB(n-1-i)
    return x

Quick test

In [9]:
[DRB(n) for n in range(15)]
Out[9]:
[1, 1, 1, 3, 5, 9, 17, 31, 57, 105, 210, 403, 775, 1493, 2881]

Problem 4B

In [10]:
def DRB_iterative(n):
    """
    Iterative implementation of 'digital recursion breath' sequence
    """
    if n<=2:
        return 1
    # Store d+2 recent terms in `context`
    context = [1,1,1]
    # At the end of each iteration of this loop,
    # the last element of `context` will be DRB(k)
    for k in range(3,n+1):
        # make sure `context` has the right length
        if len(context) > len(str(k))+2:
            context = context[1:]
        # Note: sum(L) returns sum of list items, which is useful
        # (But we could just as well use a for loop)
        context.append(sum(context))
    return context[-1]
In [11]:
[DRB_iterative(n) for n in range(15)]
Out[11]:
[1, 1, 1, 3, 5, 9, 17, 31, 57, 105, 210, 403, 775, 1493, 2881]

Problem 4C

In [12]:
agree = True
print("{:>3} {:>5} {:>5}".format("n","rec","iter"))
for n in range(20):
    xrec = DRB(n)
    xiter = DRB_iterative(n)
    print("{:3d} {:5d} {:5d}".format(n,xrec,xiter))
    assert xrec==xiter

print("\nAll computed values agree.")
  n   rec  iter
  0     1     1
  1     1     1
  2     1     1
  3     3     3
  4     5     5
  5     9     9
  6    17    17
  7    31    31
  8    57    57
  9   105   105
 10   210   210
 11   403   403
 12   775   775
 13  1493  1493
 14  2881  2881
 15  5552  5552
 16 10701 10701
 17 20627 20627
 18 39761 39761
 19 76641 76641

All computed values agree.

Revision history

  • 2024-02-09 Initial publication