Note:
In this Notebook, I used besides Code cells also Markdown cells for formatted text - like this one. This is a great way to write down your thoughts, concepts or documentation as you work with data - or like here, mix instructions with code.
You can edit these Markdown cells yourself: Simply double-click to open the Edit mode. When you're done, you can "execute" a Markdown cell to show it as nicely formatted text.
For more, see the link to Markdown Help in the Help menu.
This exercise makes use of the "random" package, included in Python. Take a look at the documentation for more information:
https://docs.python.org/3/library/random.html
The following cell with an import
statement has to be executed once.
# The following "import" line makes functions of the "random" package available.
import random
# Standard deviation, for exercise 4:
from statistics import stdev
Another note:
Jupyter notebooks can show you the built-in help/documentation of Python functions (and classes, modules, ...) in a nice fashion at the bottom of the screen. All you have to do is write the name of the function and append a question mark ?
. Try it below!
You can use two question marks ??
to get even more help, sometimes the complete source code of the function.
random.randint?
Create two random integers, a
and b
, in the range of [1, 100].
Print the initial values of a
and b
.
Write a while loop that:
a
and b
are more than 5.abs()
function. It's always available. See:a
or b
.a
and b
at the end of each loop iteration.Let your program run a couple of times to see how it works.
# An example of using a function of the "random" package,
# creates a random integer in the range [10,20]:
r = random.randint(10,20)
print("The randomly chosen value between 10 and 20 is: {}".format(r))
# You may as well delete this example or comment it out.
# Create initial random integers:
a = random.randint(1, 100)
b = random.randint(1, 100)
print(f'Initial: a = {a:3}, b = {b:3}')
# use abs() to get the absolute distance without sign:
while abs(a - b) > 5:
# Compare the two values and generate one anew:
if a < b:
a = random.randint(1, 100)
else:
b = random.randint(1, 100)
# Print the actual state at the end of this loop:
print(f'Updated: a = {a:3}, b = {b:3}')
# Additionally: Print final state (equal to last loop iteration)
print(f'Final: a = {a:3}, b = {b:3}')
Change your code to count, how many iterations were used to complete the loop.
You can modify your code above or make a copy of it in a new cell and work in there. Look at the Edit menu or the 3rd to 5th menu button in the toolbar to copy&paste entire cells.
a = random.randint(1, 100)
b = random.randint(1, 100)
# Initialize counter with value 0:
counter = 0
print(f'Initial: a = {a:3}, b = {b:3}')
while abs(a - b) > 5:
if a < b:
a = random.randint(1, 100)
else:
b = random.randint(1, 100)
# Increment counter, equal to `counter = counter + 1`
counter += 1
print(f'Updated: a = {a:3}, b = {b:3} (iteration {counter})')
print(f'Final: a = {a:3}, b = {b:3} after {counter} iterations.')
list
.To see, how a for
loop can be made to run 100 times, take a look at
"The range()
Function" in the Python tutorial:
https://docs.python.org/3/tutorial/controlflow.html#the-range-function
In short, it should look something like this:
for i in range(100):
#... your pre-existing code...
At the end, print the minimum, maximum and average loop counts.
HINT: Use the builtin min()
, max()
and sum()
functions.
stdev()
function from the statistics module (we imported that above).# Create an empty list to be filled:
loop_counts = []
# Run the inner code 100 times:
for i in range(100):
a = random.randint(1, 100)
b = random.randint(1, 100)
counter = 0
while abs(a - b) > 5:
if a < b:
a = random.randint(1, 100)
else:
b = random.randint(1, 100)
counter += 1
# append the current counter value to the loop_counts list:
loop_counts.append(counter)
# Commented out, so we see the aggregated results only.
# Uncomment to see the final state and iteration count of each loop:
print(f'Final: a = {a:3}, b = {b:3} after {counter} iterations.')
# An empty line to separate outputs:
print()
# Print "statistics" values
# (I'm lazy, so I didn't save them in variables first)
print(" Minimum:", min(loop_counts))
print(" Maximum:", max(loop_counts))
# The mean is simply the sum divided by the number of values,
# here the length the list
# Note: You COULD just divide by 100, but that will bite you
# later whenever you change the number of iterations.
print(" Mean:", sum(loop_counts) / len(loop_counts))
# Also interesting:
print("Standard Deviation:", stdev(loop_counts))
It might seem silly, but what you probably did above - copying and pasting your own code - is a perfectly normal, yet error prone practice. Often enough, you will seemingly have a quicker result by just doing that.
However, this is also a good example, where a function would have been a good solution.
Take your code from the above Exercise 2 (copying it one last time) and put it inside a new function approaching_numbers()
.
This might look like:
def approaching_numbers():
"""Two random numbers approaching each other"""
a = random.randint(1, 100)
b = ...
loop_count = 0
while ...
return
the loop count.Test your function by calling it, saving the return value in a variable and printing its value.
Roughly like this:
c = approaching_numbers()
print("The function returned:", c)
Once your function is working, you might want to remove or "comment out" some of the earlier print()
lines - we don't really need them anymore, except for debugging if necessary.
Expand your approaching_numbers()
function by making the lower and upper limit of the random.randint()
function parameters that you can pass on.
It should look like this:
def approaching_numbers(lower, upper):
"""Two random numbers approaching each other"""
a = random.randint(lower, upper)
b = ...
And then you can call your function like this, e.g. with an interval from 500 to 1000:
c = approaching_numbers(500, 1000)
Define default values for the lower
and upper
parameters, such that these two lines do the same:
c = approaching_numbers()
c = approaching_numbers(1, 100)
# Version 1, without parameters
def approaching_numbers_no_params():
a = random.randint(1, 100)
b = random.randint(1, 100)
counter = 0
while abs(a - b) > 5:
if a < b:
a = random.randint(1, 100)
else:
b = random.randint(1, 100)
counter += 1
return counter
# call like this, you'll see the return value as output of this cell:
approaching_numbers_no_params()
# Version 2, without mandatory parameters
def approaching_numbers_mandatory_params(lower, upper):
a = random.randint(lower, upper)
b = random.randint(lower, upper)
counter = 0
while abs(a - b) > 5:
if a < b:
a = random.randint(lower, upper)
else:
b = random.randint(lower, upper)
counter += 1
return counter
# call like this (examples):
out100 = approaching_numbers_mandatory_params(1, 100)
out1k = approaching_numbers_mandatory_params(1, 1000)
out10k = approaching_numbers_mandatory_params(1, 10000)
print("Range 1 to 100 needed", out100, "iterations")
print("Range 1 to 1000 needed", out1k, "iterations")
print("Range 1 to 10000 needed", out10k, "iterations")
def approaching_numbers(lower = 1, upper = 100):
"""Two random integers approach each other
Two initially randomly drawn integers are updated until
their absolute difference is less than or equal to five.
Only the smaller number is updated in every iteration.
Parameters
----------
lower, upper : int, optional
Lower and upper bounds (inclusive) for random numbers.
Default range is 1 to 100.
Returns
-------
int
Number of loop iterations until the two integers were
close enough to each other.
Notes
-----
Every good function has a so call docstring. That is the
first string inside the (indented) function body, not saved
into any variable. For simple functions, the description
might be longer than the code itself.
This docstring follows the style guide that is used for
numpy [1] which I think is one of the clearest ways to do
it. The most important thing about a docstring, though, is
that it explains what a function does and how to use it.
References
----------
.. [1] https://numpydoc.readthedocs.io/en/latest/format.html
"""
a = random.randint(lower, upper)
b = random.randint(lower, upper)
counter = 0
while abs(a - b) > 5:
if a < b:
a = random.randint(lower, upper)
else:
b = random.randint(lower, upper)
counter += 1
return counter
# These two lines should work when you're done (uncomment to use):
print(approaching_numbers())
print(approaching_numbers(500, 1000))
# And since we have written a beautiful docstring, we can get help:
approaching_numbers?
Use your function for more:
approaching_numbers()
function. Collect the returned values in a list and print minimum, maximum, mean and standard deviation.lower
and upper
bounds. Important: You should not need to change the approaching_numbers()
function for this.loop_counts = []
for i in range(100):
# still any doubts why it's so nice to have a function?
loop_counts.append(approaching_numbers())
print(" Minimum:", min(loop_counts))
print(" Maximum:", max(loop_counts))
print(" Mean:", sum(loop_counts) / len(loop_counts))
print("Standard Deviation:", stdev(loop_counts))
(super optional) Write a function, using your function:
evaluate_approach()
.lower
and upper
bounds parameters of that function as well and pass them through when you call approaching_numbers()
.n
for the number of iterations of your for
-loop.def evaluate_approach(n = 100, lower = 1, upper = 100):
"""Evaluate the output of approaching_numbers()
Parameters
----------
n : int
Number of executions before evaluating the results.
lower, upper : int, optional
Lower and upper bounds (inclusive) for random numbers
in approaching_numbers(). Default range is 1 to 100.
See Also
--------
approaching_numbers :
The inner function doing the actual work.
"""
loop_counts = []
for i in range(n):
# Important: Don't forget to pass through `lower` and `upper`
loop_counts.append(approaching_numbers(lower, upper))
print(f"After {n} iterations, loop counts are as follows:")
print(" Minimum:", min(loop_counts))
print(" Maximum:", max(loop_counts))
print(" Mean:", sum(loop_counts) / len(loop_counts))
print("Standard Deviation:", stdev(loop_counts))
# Just saying... you can also get help like this:
help(evaluate_approach)
evaluate_approach()
evaluate_approach(100)
evaluate_approach(10000)
# THIS MAY RUN A FEW SECONDS:
evaluate_approach(n = 1000, lower = -20000, upper = +20000)