Simulation Lets Randomness Move
LESSON
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:
- Healthy \(H\) with probability \(0.80\).
- Degraded \(D\) with probability \(0.20\).
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:
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.
- Draw the service state.
- Choose the timeout probability from that state.
- Draw the first attempt.
- If the first attempt succeeds, record A and stop.
- If it times out, draw the retry.
- Record TA if the retry succeeds or TT if it times out again.
The simulation has three kinds of objects:
- State: healthy or degraded.
- Random draws: numbers used to choose a state and attempt outcomes.
- Recorded output: outcome label, attempt count, and success or failure.
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\):
- draw \(0.03\): timeout, because \(0.03<0.05\);
- draw \(0.40\): acknowledgement, because \(0.40\geq0.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:
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:
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:
- Sanity check: Does the implementation produce frequencies that resemble the stated model?
- 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:
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:
- More degraded requests should create more timeouts.
- More first timeouts should create more retries.
- The retry improves eventual success compared with no retry, but it also adds work.
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
- Attempts per request: always 1.
- Failure probability: first-timeout probability \(0.14\).
Policy 1: one retry
- Expected attempts: \(1.14\).
- Failure probability: \(P(TT)=0.052\).
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.
- If the degraded-state probability is stale, the output is precise but irrelevant.
- If the simulation makes attempts independent while the real service has bursts, it understates clusters of failure.
- If the model omits duplicate side effects, a retry may look safer than it is.
- If the random generator or branch logic is wrong, repeated runs reproduce the wrong mechanism.
- If the sample is tiny, an extreme result may be ordinary variation rather than a new system regime.
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:
- 75% finish before the deadline.
- 15% finish late.
- 10% fail without finishing.
Answer:
- What random draw rule would choose the three outcomes?
- What would you record for each run?
- Which summary would estimate the average attention cost if the costs are 1, 2, and 5 units?
- Change one parameter and predict its effect before running the simulation.
- Name one real-world feature the three-outcome model omits.
Model answer
Draw a number \(u\) between 0 and 1:
- \(u<0.75\): record F.
- \(0.75\leq u<0.90\): record L.
- \(u\geq0.90\): record X.
Record the outcome, completion time or bucket, attention cost, and any state or batch identifier needed to detect dependence. The average cost estimates:
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
- [COURSE] MIT OpenCourseWare: Introduction to Probability - Focus: Connect probability rules to repeated trials and empirical frequencies.
- [REFERENCE] MIT 18.05: Probability Terminology and Examples - Focus: Review conditional models and the interpretation of simulated samples.
- [COURSE] MIT OpenCourseWare: Fundamentals of Probability - Focus: Use the deeper probability lectures as a formal companion to simulation work.
Key Takeaways
- A simulation turns a probability model into explicit local rules and repeated random trials.
- A small empirical frequency can differ greatly from a theoretical probability; larger samples usually make the comparison more stable.
- Changing one parameter and predicting first makes simulation a reasoning tool rather than a chart generator.
- Seeds support reproducible debugging, while realism comes from the model's states, probabilities, dependencies, and boundaries.
- Compare the metric that matches the decision: reliability, work, delay, tail behavior, or side effects.
← Back to Probability, Random Processes, and Statistical Thinking