3.1. Chapter 1: Math Fundamentals#

Instructions:

  • Read Chapter 1 (Math Fundementals) in Savov (2020), take notes, compile a list of your questions

  • Complete the Python tutorial on Udemy and review contents of Python Primer

  • Complete this notebook, submit you answer via Gradescope

YOUR NAME AND EMAIL HERE

# You do not need to include import statements in individual functions. Instead, you can put them all here.
import numpy as np
import math
import matplotlib.pyplot as plt

3.1.1. High/Low Guess My Number#

3.1.1.1. Instructions#

Write pseudocode and Python function program to interactively play the following game:

  1. Generate a random integer number between 0 and 100.

  2. Ask the player to guess a number. Capture their input from the keyboard.

  3. Check if the number is valid (between 0 and 100). If not, warn the player and go to Step 2.

  4. If the player’s guess is correct, they win. Print out “Congratulations” and tell them how many guesses it took. Only count valid guesses. Terminate the program.

  5. If the guess is too low, print “Guess is too low.” and go to Step 2.

  6. If the guess is too high, print “Guess is too high.” and go to Step 2.

First write the pseudocode and then implement as a function in Python. Test your program at least 2 times.

3.1.1.2. Your Function#

Python tips:

  • Look up the input command to request keyboard input.

  • You may need to cast/convert the data captured with input. For example, int(2.0) converts 2.0 to an integer. Give int('2') a try.

# function to accept guess from user and determine if guess matches secret 
# number between 0 and 100
def guessing_game(rand_num=None):
    ''' Play the guess my number interactively with keyboard input
    
        Argument:
            rand_num: the random (secret) number. If None (default), the program generates
                a random integer between 0 and 100.
            
        Returns:
            Nothing
            
        Other:
            Asks user for interactive keyboard input that are integers
    
    '''
    
    # Add your solution here

3.1.1.3. Test 1#

# guessing_game()

3.1.1.4. Test 2#

# guessing_game()

3.1.2. Algorithm for High/Low Guessing Game#

In this problem, you will implement and analyze two algorithms for playing the guessing game (see above). In both algorithms, you must keep track of the lower (\(l_i\)) and upper (\(u_i\)) bounds and use these bounds to generate the next guess (\(g_{i+1}\)).

Important: The autograder can get stuck in an infinite loop if your function does not satisfy all of the specifications (see bullet points below).

3.1.2.1. Random Guesses#

Perhaps the simplest strategy, you generate a random number between \(l_i\) and \(u_i\) which becomes guess \(g_{i+1}\). Depending on the feedback, either too low or too high, you adjust the bounds and repeat.

Hint: Start with \(u_0 = 100\) and \(l_0 = 0\). Let \(g_1\) be the first guess. Likewise, let \(l_1\) and \(u_1\) be the updated bounds after the first guess.

Do the following:

  • Write pseudocode. Include both the guessing algorithm (the player) and the game rules (see Tutorial 1). Print out each guess instead of asking for keyboard input. Remember to scan all of your pseudocode for this assignment to a single PDF and upload to Gradescope.

  • Implement in Python below. Create a function named random_guess that takes the secret number as its input and returns the number of guesses. Also add the argument print_flag to your function to toggle on/off the print statements.

  • If the input secret number is not between 0 and 100, your function should return -1.

  • Your function must return an integer.

  • Test your code with 35 as the secret number. Store the number of tries in the variable result.

# Create your function here.
def random_guess(secret_num, print_flag=True):
    '''
    student add description here
    
    argument:
    student add comments here
    
    return:
    student add comments here
    '''
    
    # You need to fill in the comments above and write Python code to finish this function
    
    # Add your solution here

In the cell below, test your function a few times. Specifically:

  • Does your function return an integer number of guesses?

  • Does your function behave correctly with an invalid secret number?

  • Does your function work with print_flag set to either True or False?

  • Does your function update the bounds correctly?

Now run your function with 35 as the secret number. Store the answer in result. Then verify your answer makes sense. In the best case, it should take 1 guess. In the worst case, it will take 100 guesses. Tip: If the autograder says you have too few or too many guesses, also check that you are storing your answer in result correctly.

# Test your function code with 35 as secret number. Store answer in `result`
### BEGIN SOLUTION
result = random_guess(35)
## END SOLUTION
print("\nNumber of guesses = ",result)

3.1.2.2. Bisection#

A more intuitive strategy is the bisection algorithm. It cuts the search space in half each iteration using the guess \(g_{i+1} = (l_i + u_i)/2\), where \(l_i\) and \(u_i\) are the (updated) lower and upper bounds, respectively, after guess \(i\).

Do the following:

  • Write pseudocode. Include both the guessing algorithm (the player) and the game rules (see Tutorial 1). Print out each guess instead of asking for keyboard input.

  • Implement in Python. Create a function named bisection_guess that takes the secret number as its input and returns the number of guesses. Also add the argument print_flag to your function to toggle on/off the print statements.

  • If the input secret number is not between 0 and 100, your function should return -1.

  • Your function must return an integer.

  • Test your code with 35 as the secret number and store answer in variable named results2.

# Create your function here.
def bisection_guess(secret_num, print_flag=True):
    '''
    student add description here
    
    argument:
    student add comments here
    
    return:
    student add comments here
    '''
    
    # You need to fill in the comments above and write Python code to finish this function
    
    # Add your solution here
    

In the cell below, test your function a few times. Specifically:

  • Does your function return an integer number of guesses?

  • Does your function behave correctly with an invalid secret number?

  • Does your function work with print_flag set to either True or False?

  • Are the guesses printed out each iteration (with print_flag=True) integer? If not, you need to use integer division // or cast as an interger int( ).

  • Does your function update the bounds correctly? For example, if your function learns 24 is too low, it should not guess 24 again. The new lower bound should be 25.

  • Is your function deterministic, i.e., do you get the same answer when you run it multiple times? The bisection algorithm should not use any random numbers.

Now test your function with 35 as the secret number. Store the answer in results2 then print it.

# Test code with 35 as secret number, store number of guesses in `results2`
# Add your solution here
print("\nNumber of guesses =",results2)

3.1.2.3. Analysis#

Use your two functions, compare both strategies for the following secret numbers: 1, 43, 76, 89. For each secret number value:

  • Play the game 100 times using the random strategy.

  • Calculate the average number of guesses required for the random strategy.

  • Print the average number of guesses for the random strategy versus the number of guesses for the bisection strategy. Why are we not taking an average for the bisection strategy?

Do the following:

  • Write pseudocode for the algorithm described above. Think about how do you plan on calculating the average? Will you store the results in a list or do you prefer to calculate the average as you go? Both approaches are valid.

  • Implement your pseudocode by finishing the Python function below.

  • Use your function to compare the algorithms for the secret numbers 1, 43, 76, and 89.

  • Hints: Your function compare should call random_guess and bisection_guess. (The main idea with a function is to write and test code once, and then reuse whenever possible.) But you’ll want to set print_flag = False when calling random_guess and bisection_guess. Notice that it is okay for compare, random_guess, and bisection_guess to all have an arugment called print_flag.

# finish this function
def compare(secret_number, nsim=100, print_level=2):
    ''' Compare two algorithms for the high/low guessing game. This code requires the functions
    random_guess and bisection_guess are properly defined.
    
    Arguments:
        secret_number: the secret number our algorithms are trying to guess
        nsim: number of simulations (trials) for the random strategy
        print_level: 2 = print all, 1 = print only summary, 0 = print nothing
        
    Returns:
        bisection_result: number of guesses for the bisection algorithm to find the secret number
        random_result: average number of guesses for the random algorithm to find the secret number
    
    '''
    
    # Finish implementing this function
    
    # Add your solution here
    
    if print_level > 0:
        print("Bisection algorithm converged to secret number ",secret_number," with ",bisection_result," guesses.")
        print("Random algorithm converged to secret number ",secret_number," with ",random_result," guesses (average).\n")
    
    return bisection_result, random_result

Now compare the number of guesses required for secret numbers 1, 42, 76, and 89.

# use your function to test the following secret numbers
my_numbers = [1, 43, 76, 89]

# Add your solution here

3.1.2.4. Discussion#

In a few sentences, interpret the results from both. Which strategy is better? Offer justification for why your results are not surprising.

Your Answer:

3.1.3. Data Visualization Best Practices#

Data visualization is an essential part of effective scientific communication. Bad figures can easily tank a paper or presentation.

Complete the following (in MS Word or similar):

  1. Read P. Kamat, G. V. Hartland, and G. C. Schatz (2014). Graphical Excellence, J. Phys. Chem. Lett., 5(12), 2118–2120. Reflect on the most important thing you learned in the short article in a few sentences.

  2. Sign up for a New York Times or Wall Street Journal digital subscriptions for free via the ND library. These two popular news organizations routinely include high-quality data visualizations in their articles.

  3. Find one article from either the NYT or WSJ on a topic of your choice. The only requirement is that the article includes a quantitive figure. Summarize the critical takeaway message(s) from the article in a few sentences or a bulleted list. What is the central thesis or argument of the article? Include a full citations to the article (article name, newspaper/journal name, authors, date, URL if applicable).

  4. Paste a copy of one of the quantitative figures from the article. (A screenshot from your web browser or phone may be helpful.) How did this figure help support the takeaway message of the article? Could you guess the main thesis of the article from just the figure?

  5. Finally, in a brief paragraph, reflect on how graphical excellence for scientific publishing (see Kamat et al.) translates to effective communication in popular media, e.g., NYT or WSJ.

  6. Save your response as PDF and upload to Gradescope. Tip: Answer these questions in a MS Word or similar document. While you can answer these questions by editing the notebook, successfully uploading this notebook with an embedded image to Gradescope is challenging.

3.1.4. Compounding Penalties#

X, formerly known as Twitter, was recently fined $350k for being three days late complying with a court order. The penalty was $50k for the first day, doubling every subsequent day of tardiness.

Complete the following:

  1. Develop a mathematical formula to compute the penalty on the Nth day of being late. This is the penalty incurred that day only, not the cumulative penalty. (Typeset in this notebook.)

  2. Verify your formula by showing the cumulative penalty for being 3 days late is $350k. (Complete on paper and typeset in this notebook or use Python.)

  3. In Python, create a semi-log plot showing the daily and cumulative penalties with blue and red lines, respectively, as a function of the number of days late. The penalties should be on a logarithmic scale vertical axis. You must follow the best practices for publication-quality figures for full credit. (Calculate in this notebook.)

  4. Based on the plot, when does the cumulative penalty exceed $1M and $1B? Describe the shape of the curves on your graph. (Are one or both of the curves linear?) Explain these shapes using the formula you developed. (Typseset in this notebook.)

3.1.4.1. Compounding Formula#

Edit this box to show your work

3.1.4.2. Verification Calculation#

# Either enter your verification calculations as a markdown cell or do them in Python

# Add your solution here
Penalty for day 1 = 50.0 k$
Penalty for day 2 = 100.0 k$
Penalty for day 3 = 200.0 k$
Cumulative penalty for the first 3 days = 350.0 k$

3.1.4.3. Plotting in Python#

# Add your solution here

3.1.4.4. Analysis and Discussion#

3.1.5. Submission#

Please submit two files for this assignment:

  1. Submit your pseudocode and written answers to Gradescope (one assignment)

  2. Download this notebook as a .ipynb file and submit to to Gradescope (second assignment)

Reminders:

  • Be sure to upload your pseudocode and answers to Gradescope as a single PDF for grading.

  • Review the homework formatting guidelines in the syllabus.

  • Review the commenting and pseudocode guidlines.