Concept units

Random draws: chance you can repeat

Basic

Before you start

You need nothing else first. This unit starts from scratch.

The idea in one paragraph

A random draw picks one value by chance, like rolling a die. You cannot predict one roll, but many rolls follow a pattern: each face comes up about a sixth of the time. Computers make draws with a fixed recipe that only looks random. The recipe starts from a number called the seed. Start from the same seed and you get the same draws again, which lets you repeat an experiment exactly.

One draw is unpredictable; many draws follow a pattern

A random draw is one value picked by chance from a set of possible values. Rolling a fair die is a draw from {1, 2, 3, 4, 5, 6}, and each face has the same chance, 1 in 6. The rule that says how likely each value is called the distribution of the draw. When every value is equally likely, the distribution is uniform.

Roll 60 times and you expect each face about 10 times, but rarely exactly 10. Roll 6000 times and each face's share lands close to one sixth, even though its count can miss 1000 by 30 or more. The more draws you make, the closer the shares come to the distribution's chances.

More draws bring each face's share closer to one in six
First ten draws:
Try this. The dashed line is the expected count, one sixth of the draws. With 60 draws, try seeds 1 to 5: how far do the bars stray from the line? Predict what happens at 6000 draws, then check. Finally, go back to an earlier seed: are the first ten draws the same as before?

A computer's draws come from a recipe that starts at the seed

A computer follows instructions exactly, so it cannot roll a real die. It uses a pseudo-random number generator instead: a rule that turns one number into the next, chosen so the results look random. The first number is the seed. Same seed, same sequence, every time.

Worked example

A toy generator: multiply the current number by 5, add 3, and keep the remainder after dividing by 16. So the next number is (5 × current + 3) mod 16, where "mod 16" means "the remainder after dividing by 16."

  1. Seed 7: 5 × 7 + 3 = 38, and 38 mod 16 = 6.
  2. From 6: 5 × 6 + 3 = 33, and 33 mod 16 = 1.
  3. From 1: 5 × 1 + 3 = 8.
  4. From 8: 43 mod 16 = 11. Then 10, 5, 12 and so on.

The sequence 6, 1, 8, 11, 10, 5, 12 looks jumbled, but anyone who starts at 7 gets exactly these numbers.

Your turn. Run the same generator from seed 2 for three numbers.

Answer

5 × 2 + 3 = 13; 5 × 13 + 3 = 68, and 68 mod 16 = 4; 5 × 4 + 3 = 23, and 23 mod 16 = 7. The next number would be 6, so from here on the sequence repeats the one from seed 7.

Real generators use much larger numbers and cleverer rules, so their sequences pass strict tests of randomness and take an astronomically long time to repeat. The seed still works the same way. Researchers record the seed so that someone else can rerun a computation and get the same numbers.

This is a simplification

The same seed gives the same draws only with the same generator, the same library version and the same order of calls. Several workers that share one generator can take its draws in a different order on each run. The same seed can also give different draws on a CPU and on a graphics card. Some calculations on graphics cards vary from run to run as well, so results can change even when the draws are the same. Pseudo-random numbers are fine for simulations and training, but they are not safe for passwords or keys, which need a generator built for security.

rand.py: NumPy's generator, with a seed

import numpy as np

rng = np.random.default_rng(seed=42)
print(rng.integers(1, 7, size=10))   # ten rolls of a die

rng = np.random.default_rng(seed=42)
print(rng.integers(1, 7, size=10))   # same seed, same rolls

rng = np.random.default_rng(seed=7)
print(rng.integers(1, 7, size=10))   # new seed, new rolls

rolls = np.random.default_rng(seed=0).integers(1, 7, size=6000)
# np.bincount counts each value from 0 up; [1:] drops the count for 0
print(np.bincount(rolls)[1:])        # how often each face came up
[1 5 4 3 3 6 1 5 2 1]
[1 5 4 3 3 6 1 5 2 1]
[6 4 5 6 4 5 6 2 1 2]
[1017  970 1006 1008  969 1030]

integers(1, 7) draws whole numbers from 1 up to but not including 7. The output above came from NumPy 2.5; another version may print different numbers.

Check yourself

  1. You roll a fair die 600 times. About how many sixes do you expect? Would exactly that number surprise you?

    Answer

    About 100. Getting exactly 100 is not likely; a count somewhere between about 80 and 120 is normal.

  2. Two people run the same program with seed 3, and a third person with seed 4. Whose draws match?

    Answer

    The two with seed 3, as long as they use the same generator and library version. The third gets a different sequence.

  3. A program gives different results each run and you want to find a bug in it. What should you do first?

    Answer

    Fix the seed, so that every run makes the same draws and the bug shows up the same way each time.

Go further

Optional background, for readers who want more than this page.

Where this shows up

Units that build on this one