← Discrete-Time Markov Chains

DTMC · Lesson 4

Uniform random numbers and probability intervals

Turn one random number into one categorical event. This is the bridge between calculated transition probabilities and a simulated epidemic path.

Learning purpose

In the previous lesson, we calculated the probabilities of infection, recovery and no change. Here we learn how a computer uses those probabilities to select one event at random.

This page does not yet update the values of \(S\), \(I\) and \(R\). Producing the next epidemic state is the separate purpose of Lesson 5.

Scenario: what happens during the next time step?

Suppose the current epidemic state and time step give:

Possible eventProbabilityPercentage
Infection0.29729.7%
Recovery0.10010.0%
No change0.60360.3%

The probabilities describe uncertainty. They do not say which event will actually occur in this particular step. The computer therefore needs a random selection rule.

Step 1: draw a uniform random number

A uniform random number \(U\) on \([0,1)\) can take any value from 0 up to, but not including, 1. Equal-length subintervals are equally likely.

\[ U\sim \operatorname{Uniform}(0,1). \]

For example, an interval of length 0.20 has probability 0.20. This makes the unit interval a natural measuring line for probabilities.

Important: “Uniform” does not mean the generated numbers will be evenly spaced. Individual values are unpredictable. It means that, over many independent draws, equal-length intervals are selected with approximately equal frequency.

Step 2: place the event probabilities on the unit interval

Start at 0 and place the probabilities beside one another. The interval length assigned to each event equals its probability.

\[ \begin{aligned} \text{infection: }&[0,0.297),\\ \text{recovery: }&[0.297,0.397),\\ \text{no change: }&[0.397,1). \end{aligned} \]
IntervalLengthSelected event
\(0\le U<0.297\)0.297Infection
\(0.297\le U<0.397\)0.100Recovery
\(0.397\le U<1\)0.603No change

The second boundary is cumulative:

\[ 0.397=p_{\mathrm{infection}}+p_{\mathrm{recovery}} =0.297+0.100. \]

The last interval automatically receives the remaining probability.

Why use cumulative boundaries?

The first boundary is the infection probability. The second boundary is the first probability plus the recovery probability:

\[ c_1=p_{\mathrm{infection}},\qquad c_2=p_{\mathrm{infection}}+p_{\mathrm{recovery}}. \]

These cumulative values divide one continuous line into three non-overlapping regions. Because the probabilities sum to 1, the regions cover the whole unit interval without gaps.

Step 3: locate the random number

Example A: If \(U=0.18\), then \(0.18<0.297\), so infection is selected.

Example B: If \(U=0.35\), then \(0.297\le0.35<0.397\), so recovery is selected.

Example C: If \(U=0.82\), then \(0.82\ge0.397\), so no change is selected.

Exactly one event is selected because every possible \(U\) belongs to exactly one interval.

What happens exactly at a boundary?

We must use one consistent convention. Here, each interval includes its left endpoint and excludes its right endpoint:

\[ [0,0.297),\quad[0.297,0.397),\quad[0.397,1). \]

Therefore \(U=0.297\) selects recovery, and \(U=0.397\) selects no change. This convention prevents overlap. For a continuous uniform distribution, hitting one exact boundary has probability zero, but precise programming conditions are still necessary.

Translate the interval rule into Python

if u < p_infection:
    event = "infection"
elif u < p_infection + p_recovery:
    event = "recovery"
else:
    event = "no change"

if

Tests the first interval. If true, Python selects infection and skips the remaining branches.

elif

Means “else if”. It is checked only when the infection condition was false.

else

Catches every remaining value. These values belong to the no-change interval.

The recovery condition does not need to repeat u >= p_infection. Reaching elif already means the first condition was false.

Interactive Python laboratory

The code draws 12 visible random numbers, maps each one to an event, and then uses 10,000 additional draws to check that the long-run event frequencies are close to the specified probabilities.

Interactive PythonUniform interval event selector

Output

Run the code to see the result.

Understand every new programming idea

CodeMeaning
np.random.default_rng(7)Creates a modern NumPy random-number generator with seed 7.
rng.random(12)Produces 12 values from the uniform distribution on \([0,1)\).
def choose_event(u):Defines a reusable function that accepts one number and applies the interval rule.
return "infection"Ends the function and sends the selected event name back to the caller.
[choose_event(u) for u in u_values]Applies the same selection rule to every random number in the array.
many_events.count(name)Counts how many simulated draws produced a particular event.
count / number_of_drawsConverts an event count into an observed proportion.

What does the random seed mean?

A random seed is a starting value for a pseudo-random number generator. Using the same seed and the same code produces the same sequence of numbers.

Reproducibility is useful in teaching, testing and scientific reporting. Another researcher can repeat the exact simulated example.

A seed does not make the model deterministic in meaning and does not change the intended probabilities. It only fixes which pseudo-random sequence is used for that run. Changing the seed gives another valid realisation.

Why observed proportions are not exactly equal to probabilities

In 10,000 draws, the observed infection proportion will usually be close to 0.297, but it need not equal 0.297 exactly. Random sampling creates variation.

As the number of independent draws increases, the observed proportions generally become closer to their theoretical probabilities. This is the practical idea behind the law of large numbers. A later lesson will study many complete epidemic trajectories and Monte Carlo uncertainty properly.

Try these experiments

  1. Change seed = 7 to seed = 25. Which first 12 events change?
  2. Change number_of_draws from 10000 to 100. Are the observed proportions less stable?
  3. Use p_infection = 0.15, p_recovery = 0.25 and p_no_change = 0.60. Update the interval boundaries by running the code.
  4. Test choose_event(0.297) and choose_event(0.397) to confirm the boundary convention.

Biological interpretation

A uniform draw is not itself an infection level, a recovery time or a biological measurement. It is a computational device for selecting among biological events according to their probabilities.

For one step, the result is one event. Across many repeated selections made under the same state and probabilities, infection, recovery and no change appear in approximately the required proportions.

What this lesson has—and has not—done

You can now:

  • interpret \(U\sim\operatorname{Uniform}(0,1)\);
  • convert event probabilities into cumulative intervals;
  • use if, elif and else to select exactly one event;
  • use a seed for reproducible random selections; and
  • compare theoretical probabilities with observed proportions.

Not yet: we have returned only an event name. We have not changed \(S\), \(I\) or \(R\). Lesson 5 combines the selected event with the current state to program one complete DTMC transition.