LambdaLabTM
Computer Science · Class 11 · Python Modules
Modulesrandom⏱️ 15 min read

The random Module

Three functions, and the whole difficulty is that two of them look almost identical and cover different numbers. randint(1, 6) can give you a 6. randrange(1, 6) never will. Nothing in the code says so, which is why examiners like it and why dice programs are so often quietly wrong.

1random() — a decimal between 0 and 1

random_basic.py
import random

print(random.random())
print(random.random())
print(random.random())
Output
0.09257646839864098
0.6501665598866379
0.5743197615487686
Note
Your numbers will be different, and that is the point. Every output on this page is one real run. Run the same program again and you get different numbers — so when you check your work against these, check the shape of the answer, never the digits.

random() takes no arguments and always gives a float from 0 up to (but never reaching) 1. On its own that is rarely what you want — it is a building block. Multiply it to stretch it, add to shift it:

scaled.py
# 0-1 stretched into other ranges

import random

print(random.random() * 10)
print(random.random() * 5 + 20)
Output
4.51715653708381
20.246290398300456

The first line lands somewhere in 0 to 10, the second somewhere in 20 to 25. For whole numbers there is a better tool, which is the next one.

2randint(a, b) — a whole number, both ends included

randint.py
# six rolls of a die

import random

for i in range(6):
    print(random.randint(1, 6), end=' ')
Output
5 3 2 3 4 2 
Key Takeaway
randint(1, 6) can give 1, 2, 3, 4, 5 or 6. Both ends are included. This makes it the one to reach for whenever the range is described the way a human describes it — “a number from 1 to 6”, “a page between 1 and 200”.

That is unusual for Python. range(1, 6) stops at 5, and so does slicing, and so does almost everything else in the language. randint is the odd one out — which is exactly why it needs remembering.

3randrange() — the same shape as range()

randrange() takes the same arguments as range() and obeys the same rule: the stop value is not included.

randrange_forms.py
import random

print(random.randrange(5))          # 0, 1, 2, 3 or 4
print(random.randrange(1, 6))       # 1, 2, 3, 4 or 5 -- never 6
print(random.randrange(0, 101, 10)) # 0, 10, 20, ... 100
Output
1
4
30

The third form takes a step, which randint cannot do at all. It is how you pick a random multiple of ten, or a random even number, without any arithmetic of your own.

4Watch the 6 go missing

The gap is easy to agree with and hard to believe until you see it. Draw from both a few dozen times:

🎲 Where does the 6 go?

Both calls name 1 and 6. Draw a few dozen and watch one column keep a gap that never fills.

random.randint(1, 6)
both ends included — 1 to 6
1
0
2
0
3
0
4
0
5
0
6
0
random.randrange(1, 6)
stop excluded — 1 to 5, never 6
1
0
2
0
3
0
4
0
5
0
6
0
0 draws each

The right-hand column never fills its last bar, however long you keep going. That is the entire difference between the two functions, and it is the reason a dice program written with randrange(1, 6) looks fine, runs fine, and rolls a six roughly never.

CallPossible answersType
random.random()0.0 up to but not including 1.0float
random.randint(1, 6)1, 2, 3, 4, 5, 6int
random.randrange(5)0, 1, 2, 3, 4int
random.randrange(1, 6)1, 2, 3, 4, 5int
random.randrange(0, 101, 10)0, 10, 20 … 100int

5Program 1 — two dice

two_dice.py
import random

a = random.randint(1, 6)
b = random.randint(1, 6)

print('Dice:', a, 'and', b)
print('Total:', a + b)
Output
Dice: 5 and 3
Total: 8
Watch Out
Two calls, because two dice are thrown. Writing a = b = random.randint(1, 6) rolls once and reports the same face twice — every single time. Each throw needs its own call.

6Program 2 — ten coin tosses

coin.py
# 1 is heads, 0 is tails

import random

heads = 0
tails = 0

for i in range(10):
    if random.randint(0, 1) == 1:
        heads = heads + 1
    else:
        tails = tails + 1

print('Heads:', heads)
print('Tails:', tails)
Output
Heads: 8
Tails: 2

Eight heads out of ten, on this run. That is not a bug and not a bias — it is what ten tosses of a fair coin look like sometimes. Raise the 10 to 1000 and the two counts come out close to each other, which is the honest way to check that a random program is fair.

7Program 3 — pick one item at random

📋 The problem

Pick a random colour out of a tuple of colours.

The trick is to pick a random position rather than a random item — and the last valid position is len(colours) - 1:

pick.py
import random

colours = ('red', 'green', 'blue', 'yellow')

i = random.randint(0, len(colours) - 1)

print('Picked:', colours[i])
Output
Picked: green
Watch Out
The - 1 is not optional. The tuple has four items at positions 0, 1, 2, 3. randint(0, 4) would sometimes return 4 and colours[4] raises IndexError: tuple index out of range — a crash that only happens on some runs, which is the worst kind of bug to hunt.
random.randrange(len(colours)) avoids the trap entirely, because excluding the stop is exactly right here.

8Program 4 — a six-digit OTP

otp.py
# build the code one digit at a time, as a string

import random

otp = ''

for i in range(6):
    otp = otp + str(random.randint(0, 9))

print('Your OTP is', otp)
Output
Your OTP is 920537

Built as a string, not a number, so that a leading zero survives — as a number, 075129 would print as 75129 and be five digits long. This is why PINs, phone numbers and PIN codes are stored as text in real systems.

otp.py

9Program 5 — three different lucky numbers

📋 The problem

Pick three different numbers between 1 and 20 — no repeats.

Three calls to randint can easily give the same number twice. The fix is a while loop that keeps drawing until it has enough, and throws away anything already picked:

lottery.py
# keep drawing until three different numbers are in

import random

picked = ()

while len(picked) < 3:
    n = random.randint(1, 20)

    if n not in picked:
        picked = picked + (n,)

print('Lucky numbers:', picked)
Output
Lucky numbers: (1, 6, 11)

while len(picked) < 3: rather than for i in range(3): — because a round that draws a repeat makes no progress, and a for loop would count it anyway and finish with two numbers.

10Program 6 — guess the number

guess.py
# the computer thinks of a number and gives hints

import random

secret = random.randint(1, 100)
tries = 0

while True:
    guess = int(input('Your guess: '))
    tries = tries + 1

    if guess == secret:
        print('Correct! You took', tries, 'tries.')
        break
    elif guess < secret:
        print('Too low')
    else:
        print('Too high')
Output
Your guess: 50
Too high
Your guess: 25
Too low
Your guess: 37
Too low
Your guess: 43
Too high
Your guess: 40
Too low
Your guess: 41
Too low
Your guess: 42
Correct! You took 7 tries.

while True: with a break, because nobody knows in advance how many guesses it will take. Seven is not luck — halving the range each time finds any number from 1 to 100 in at most seven tries.

11Recap

randint(a, b) includes b

The only common Python function that does. randint(1, 6) is a fair die.

randrange excludes the stop

Like range() and like slicing. randrange(1, 6) never gives 6 — and takes a step, which randint cannot.

random() is a float from 0 up to 1

Never quite 1, and no arguments. Multiply and add to stretch it into another range.

Picking from a sequence means picking a position

randint(0, len(t) - 1), or randrange(len(t)), which cannot be off by one.

Every run is different

So test the shape of the answer, not the digits — and check fairness over a thousand draws, not ten.

✍️ Now write these yourself
  1. 1

    Roll a die 20 times and count how many sixes you get.

    Hint · A counter, and randint(1, 6) — not randrange(1, 6), or the count is always 0.

  2. 2

    Roll two dice 100 times and tally the totals with a dictionary.

    Hint · freq[a + b] = freq.get(a + b, 0) + 1. Walk sorted(freq) to print it — 7 should be the tallest.

  3. 3

    Generate a random 4-digit PIN that may start with 0.

    Hint · Build it as a string, exactly like the OTP. randrange(10) works as well as randint(0, 9).

  4. 4

    Pick a random even number between 10 and 50.

    Hint · randrange(10, 51, 2). The 51 is because the stop is excluded and 50 must be reachable.

  5. 5

    Play the guessing game the other way round: you think of a number, the computer guesses.

    Hint · Keep low and high, guess the middle, and move whichever end the answer rules out.

Quick Check

Which call is right for a dice roll?

Quick Check

colours has 4 items. Why is random.randint(0, len(colours)) wrong?

Quick Check

Why does the lottery program use while rather than for?