Simulation Lets Randomness Move

LESSON

Probability, Random Processes, and Statistical Thinking

005 25 min beginner

Simulation Lets Randomness Move

By the end of this lesson, you will be able to...

  • Turn a probability model into a sequence of random trials with explicit local rules.

  • Trace one simulated request and compare empirical frequencies with theoretical probabilities.

  • Change one model parameter, predict the direction of the effect, and name a limitation of the simulation.

Idea in one sentence: A simulation repeatedly executes a probability model so that variation, dependence, and trade-offs become visible rather than remaining symbols on a page.

Core Insight

Consider the notification client with one retry. Lesson 004 added a shared service state:

In the healthy state, one attempt times out with probability \(0.05\). In the degraded state, it times out with probability \(0.50\). Attempts are independent after the state is chosen, but both attempts share that state.

This model predicts:

\[ P(A)=0.86,\qquad P(TA)=0.088,\qquad P(TT)=0.052 \]

It also predicts an average of \(1.14\) attempts per request, because the first attempt times out with probability \(0.14\) and only those requests retry.

Those numbers are useful, but a team may still ask:

What will a small batch look like? How much can the observed failure rate move by chance? What happens if degraded periods become more common?

A formula can answer a probability question. A simulation lets us watch many individual runs unfold under the same rules. It does not replace the model. It executes the model.

The Model Becomes Local Rules

To simulate one request, make every source of randomness explicit.

  1. Draw the service state.
  2. Choose the timeout probability from that state.
  3. Draw the first attempt.
  4. If the first attempt succeeds, record A and stop.
  5. If it times out, draw the retry.
  6. Record TA if the retry succeeds or TT if it times out again.

The simulation has three kinds of objects:

One common way to generate a yes/no event is to draw a number uniformly between 0 and 1. If the draw is less than the event probability, the event happens.

For example, with timeout probability \(0.05\):

The rule is simple enough to inspect. The changing result comes from the draws, not from changing the rule halfway through the experiment.

A Worked Trace: One Request

Use these draws for one request:

Step Draw Rule Result
State 0.10 \(0.10<0.20\) choose degraded \(D\)
First attempt 0.20 \(0.20<0.50\) timeout
Retry 0.90 \(0.90\geq0.50\) acknowledgement

The simulated history is TA. It uses two attempts and eventually succeeds.

Now trace a second request:

Step Draw Rule Result
State 0.70 \(0.70\geq0.20\) choose healthy \(H\)
First attempt 0.01 \(0.01<0.05\) timeout
Retry 0.03 \(0.03<0.05\) timeout

The history is TT. It fails after two attempts.

The state draw changes which timeout threshold we use. That is the mechanism that creates dependence. In the degraded state, both attempts use the larger threshold \(0.50\), so two timeouts are easier to obtain than they would be in the healthy state.

Pseudocode for Repeated Trials

The following pseudocode describes the simulation without hiding the rules inside a library call. A real implementation can use a seeded pseudorandom generator so that a test run can be repeated exactly.

  counts = {A: 0, TA: 0, TT: 0}
  total_attempts = 0

  repeat N times:
      state_draw = random_number()
      if state_draw < 0.20:
          timeout_rate = 0.50
      else:
          timeout_rate = 0.05

      first_draw = random_number()
      total_attempts += 1

      if first_draw >= timeout_rate:
          counts[A] += 1
          continue

      retry_draw = random_number()
      total_attempts += 1

      if retry_draw >= timeout_rate:
          counts[TA] += 1
      else:
          counts[TT] += 1

  empirical_rate_TT = counts[TT] / N
  empirical_mean_attempts = total_attempts / N

The variable \(N\) is the number of simulated requests. The loop does not “discover” the probabilities. It samples outcomes from the rules we supplied and reports what happened in this finite run.

A Small Batch Is Noisy

Here is a deliberately chosen eight-request trace. The draws are selected only to make the intermediate states visible:

Request State First attempt Retry Outcome
1 \(D\) timeout acknowledgement TA
2 \(H\) acknowledgement A
3 \(H\) timeout acknowledgement TA
4 \(H\) acknowledgement A
5 \(D\) acknowledgement A
6 \(D\) timeout timeout TT
7 \(H\) timeout acknowledgement TA
8 \(H\) acknowledgement A

The empirical counts are:

\[ \widehat{P}(A)=4/8=0.50,\qquad \widehat{P}(TA)=3/8=0.375,\qquad \widehat{P}(TT)=1/8=0.125 \]

The theoretical probabilities were \(0.86\), \(0.088\), and \(0.052\). The small batch is far from those values, especially for A. That does not automatically mean the model is wrong. Eight trials can land in an unusual combination of states and draws.

The simulated mean attempt count is:

\[ \frac{4(1)+4(2)}{8}=1.5 \]

The theoretical mean is \(1.14\). Again, the gap is plausible for a small sample. A simulation makes this variation emotionally and operationally visible: a team can observe one batch with a 12.5% complete-failure rate even when the model's long-run rate is 5.2%.

More Trials Stabilize Frequencies

As \(N\) grows, empirical frequencies usually move closer to the model probabilities. They do not move in a smooth line, and they do not become exactly equal after a fixed number of trials. Randomness remains in every run.

For \(N=1{,}000\), the model suggests approximate counts:

Outcome Theoretical rate Approximate count
A 0.860 860
TA 0.088 88
TT 0.052 52

An actual simulation might produce 848, 96, and 56 instead. The useful check is whether the differences are consistent with ordinary sampling variation, not whether the counts equal the expected values exactly.

This gives simulation two jobs:

  1. Sanity check: Does the implementation produce frequencies that resemble the stated model?
  2. Variation study: How much can a finite batch move around the long-run expectation?

If a simulation produces 300 TT outcomes out of 1,000 under this model, inspect the implementation, the random generator, and the assumptions. The result is not impossible in a logical sense, but it is surprising enough to demand investigation.

Change One Parameter: More Degraded Time

Now change only the probability of the degraded state from \(0.20\) to \(0.40\). Keep the healthy and degraded timeout rates unchanged.

The new theoretical probabilities are:

\[ \begin{aligned} P(A)&=0.60(0.95)+0.40(0.50)=0.77\\ P(TA)&=0.60(0.05)(0.95)+0.40(0.50)(0.50)=0.1285\\ P(TT)&=0.60(0.05)(0.05)+0.40(0.50)(0.50)=0.1015 \end{aligned} \]

The complete-failure rate rises from \(5.2\%\) to \(10.15\%\). The expected attempt count rises from \(1.14\) to \(1.23\), because the first timeout rate rises from \(14\%\) to \(23\%\).

Before simulating, predict the direction:

Then run the changed model and check whether the output supports those predictions. This predict-then-run habit is important. If we only run code and inspect a chart afterward, it is easy to narrate any result as if we expected it.

Compare Policies, Not Just Numbers

The same simulation can compare two client policies.

Policy 0: no retry

Policy 1: one retry

Under this model, one retry reduces complete failures from 14% to 5.2%, but increases average attempt work by 14%. That is a real trade-off, not a universal recommendation. If a retry overloads the degraded service, or if duplicate side effects are unsafe, the model needs more states and outcomes before a policy decision is trustworthy.

Simulation can show the distribution of total work across batches, not only its average. A capacity planner may care about the 95th percentile of attempts in a batch, while a reliability engineer may care about the number of TT outcomes. The right output follows the decision.

What Simulation Cannot Fix

Simulation is only as credible as the model and implementation behind it.

Use a seed while debugging so that a failing sequence can be replayed. Change the seed when studying variation. A fixed seed is a debugging tool, not evidence that the process itself is deterministic.

Common Confusions

Confusion: A simulation is proof that the model is true

Why it is tempting: a large table of outputs looks empirical.

Better model: simulation answers “what does this model produce?” It does not establish that the model matches the real system. Compare output with independent observations and inspect the assumptions.

Confusion: More trials remove randomness

Why it is tempting: large samples usually look more stable.

Better model: more trials reduce typical sampling noise in summaries, but every finite run still varies. A simulation estimates a distribution of possible results, not one guaranteed result.

Confusion: A random seed makes a simulation realistic

Why it is tempting: the same seed makes results reproducible.

Better model: a seed controls repeatability. Realism comes from the state space, probabilities, dependencies, and observations used to construct the model.

Confusion: A retry policy can be evaluated from success rate alone

Why it is tempting: success is an easy headline metric.

Better model: include attempt work, delay, duplicate effects, and correlated failure. Improving one metric can worsen another.

Check Your Understanding

Check: In the worked eight-request batch, why is \(\widehat{P}(TT)=0.125\) not evidence by itself that the long-run failure probability is 12.5%?

Think first, then reveal.

Answer: The batch has only eight requests. Its empirical frequency can move substantially because of ordinary sampling variation. The model's long-run value is 0.052, and more trials are needed to compare the two fairly.

Check: If the degraded-state probability increases while all other rules stay fixed, what should happen to the retry rate?

Think first, then reveal.

Answer: It should increase, because a degraded request has a higher timeout probability. More first timeouts create more retries.

Check: What does a seeded random generator help you inspect?

Think first, then reveal.

Answer: It makes the same sequence of draws reproducible, which helps debug a branch or compare one code change. It does not validate the model or remove uncertainty.

Practice: Build a Simulation Plan

Design a small simulation for a batch job with these outcomes:

Answer:

  1. What random draw rule would choose the three outcomes?
  2. What would you record for each run?
  3. Which summary would estimate the average attention cost if the costs are 1, 2, and 5 units?
  4. Change one parameter and predict its effect before running the simulation.
  5. Name one real-world feature the three-outcome model omits.

Model answer

Draw a number \(u\) between 0 and 1:

Record the outcome, completion time or bucket, attention cost, and any state or batch identifier needed to detect dependence. The average cost estimates:

\[ 1(0.75)+2(0.15)+5(0.10)=1.55 \]

Changing the failure probability from 0.10 to 0.15 should increase the expected cost, assuming the other probabilities are adjusted and the costs stay fixed. The model omits details such as how late a late job is, whether failures cluster during a shared outage, and whether recovery work changes later jobs. Those omissions define what the simulation can and cannot answer.

Connections

Lesson 004 showed that shared state makes observations dependent. This lab executes that dependency repeatedly so the learner can see the difference between a marginal rate and a batch of correlated outcomes. Lesson 006 will review the full model layer before the track turns to sampling variation and estimation.

Simulation is a bridge between probability and statistical practice: it can test an implementation, expose model consequences, and generate reference distributions. It cannot choose the boundary or guarantee that the assumed process matches reality.

Resources

Key Takeaways

PREVIOUS Dependence Changes the Meaning of Evidence NEXT Review: From Sample Spaces to Conditional Models