We now take a brief diversion into a popular type of programming application—simulation and game playing. You can introduce the element of chance via the Python Standard Library’s random
module.
Let’s produce 10 random integers in the range 1–6 to simulate rolling a six-sided die:
In [1]: import random
In [2]: for roll in range(10):
...: print(random.randrange(1, 7), end=' ')
...:
4 2 5 5 4 6 4 6 1 5
First, we import random
so we can use the module’s capabilities. The
randrange
function generates an integer from the first argument value up to, but not including, the second argument value. Let’s use the up arrow key to recall the for
statement, then press Enter to re-execute it. Notice that different values are displayed:
In [3]: for roll in range(10):
...: print(random.randrange(1, 7), end=' ')
...:
4 5 4 5 1 4 1 4 6 5
Sometimes, you may want to guarantee reproducibility of a random sequence—for debugging, for example. At the end of this section, we’ll show how to do this with the random
module’s seed
function.
If randrange
truly produces integers at random, every number in its range has an equal probability (or chance or likelihood) of being returned each time we call it. To show that the die faces 1–6 occur with equal likelihood, the following script simulates 6,000,000 die rolls. When you run the script, each die face should occur approximately 1,000,000 times, as in the sample output.
1 # fig04_01.py 2 """Roll a six-sided die 6,000,000 times.""" 3 import random 4 5 # face frequency counters 6 frequency1 = 0 7 frequency2 = 0 8 frequency3 = 0 9 frequency4 = 0 10 frequency5 = 0 11 frequency6 = 0 12 13 # 6,000,000 die rolls 14 for roll in range(6_000_000): # note underscore separators 15 face = random.randrange(1, 7) 16 17 # increment appropriate face counter 18 if face == 1: 19 frequency1 += 1 20 elif face == 2: 21 frequency2 += 1 22 elif face == 3: 23 frequency3 += 1 24 elif face == 4: 25 frequency4 += 1 26 elif face == 5: 27 frequency5 += 1 28 elif face == 6: 29 frequency6 += 1 30 31 print(f'Face{"Frequency":>13}') 32 print(f'{1:>4}{frequency1:>13}') 33 print(f'{2:>4}{frequency2:>13}') 34 print(f'{3:>4}{frequency3:>13}') 35 print(f'{4:>4}{frequency4:>13}') 36 print(f'{5:>4}{frequency5:>13}') 37 print(f'{6:>4}{frequency6:>13}')
Face Frequency 1 998686 2 1001481 3 999900 4 1000453 5 999953 6 999527
The script uses nested control statements (an if
…elif
statement nested in the for
statement) to determine the number of times each die face appears. The for
statement iterates 6,000,000 times. We used Python’s underscore (_
) digit separator to make the value 6000000
more readable. The expression range(6,000,000)
would be incorrect. Commas separate arguments in function calls, so Python would treat range(6,000,000)
as a call to range
with the three arguments 6
, 0
and 0
.
For each die roll, the script adds 1
to the appropriate counter variable. Run the program, and observe the results. This program might take a few seconds to complete execution. As you’ll see, each execution produces different results.
Note that we did not provide an else
clause in the if
…elif
statement. Exercise 4.1 asks you to comment on the possible consequences of this.
Function randrange
actually generates pseudorandom numbers, based on an internal calculation that begins with a numeric value known as a seed. Repeatedly calling randrange
produces a sequence of numbers that appear to be random, because each time you start a new interactive session or execute a script that uses the random
module’s functions, Python internally uses a different seed value.1 When you’re debugging logic errors in programs that use randomly generated data, it can be helpful to use the same sequence of random numbers until you’ve eliminated the logic errors, before testing the program with other values. To do this, you can use the random module’s
seed
function to seed the random-number generator yourself—this forces randrange
to begin calculating its pseudorandom number sequence from the seed you specify. In the following session, snippets [5]
and [8]
produce the same results, because snippets [4]
and [7]
use the same seed (32
):
In [4]: random.seed(32)
In [5]: for roll in range(10):
...: print(random.randrange(1, 7), end=' ')
...:
1 2 2 3 6 2 4 1 6 1
In [6]: for roll in range(10):
...: print(random.randrange(1, 7), end=' ')
...:
1 3 5 3 1 5 6 4 3 5
In [7]: random.seed(32)
In [8]: for roll in range(10):
...: print(random.randrange(1, 7), end=' ')
...:
1 2 2 3 6 2 4 1 6 1
Snippet [6]
generates different values because it simply continues the pseudorandom number sequence that began in snippet [5]
.
(Fill-In) The element of chance can be introduced into computer applications using module .
Answer:
random
.
(Fill-In) The random
module’s function enables reproducibility of random sequences.
Answer:
seed
.
(IPython Session) Requirements statement: Use a for
statement, randrange
and a conditional expression (introduced in the preceding chapter) to simulate 20 coin flips, displaying H
for heads and T
for tails all on the same line, each separated by a space.
Answer:
In [1]: import random
In [2]: for i in range(20):
...: print('H' if random.randrange(2) == 0 else 'T', end=' ')
...:
T H T T H T T T T H T H H T H T H H H H
In snippet [2]
’s output, an equal number of T
s and H
s appeared—that will not always be the case with random-number generation.