The recipe, and two real projects
Part 1 gave you the engine and its error bar. Now the assembly instructions: how to manufacture any distribution from uniform noise, the five steps every simulation follows, and two complete builds - a valuation rebuilt as a distribution of value, and a sample-size analysis that survives contact with reality.
From uniform to anything
Deep down a random generator produces exactly one thing: numbers spread uniformly between 0 and 1. Yet real models need normal returns, lognormal prices, exponential waiting times, Poisson arrival counts. Every one of those is manufactured from uniforms by a transformation. In practice you call the library and let it do the manufacturing - but the two classic techniques underneath are worth owning, because one day you'll need a distribution the library doesn't stock, or one that exists only as data.
The inverse transform is the universal adapter, and the idea is easier than the name. Every distribution has a CDF - a curve climbing from 0 to 1 - and that curve is a translator between two languages: the horizontal axis speaks values, the vertical axis speaks percentiles. Saying F(120) = 0.85 is saying “120 is the 85th percentile”. So: draw a uniform number and read it as a percentile request. Then translate it back into a value with the inverse CDF. That's it.
Why it must work, in one sentence of intuition: a uniform draw picks every percentile equally often, and the inverse CDF automatically converts equal percentile-traffic into correct value-traffic - crowding draws where the curve is steep (values packed together, which is what high density means) and thinning them where it's flat.
The proof in one line, and the interview favourite worked by hand
The proof is short enough to memorise. The probability that X is at most x equals the probability that the inverse CDF of U is at most x, which equals the probability that U is at most F(x) - and for a uniform, the probability of being below u is just u. So that probability is F(x), which is exactly what it means for X to have distribution F.
Now the classic interview question: generate an exponential from a uniform. The exponential CDF is one minus e to the minus lambda x. Set that equal to u and solve for x, and you get x = -ln(1 - u)/lambda. Two lines of code and you have built a distribution with your bare hands. (Since 1 - U is also uniform, -ln(u)/lambda works too - a small elegance interviewers enjoy.)
The method is fully general - it works for any one-dimensional distribution, including empirical ones: sort your historical data and the sorted array is a discretised inverse CDF, which is how you simulate draws that look like your own dataset. Its only weakness is that some inverse CDFs have no closed form. The normal is the famous offender, which is why it gets bespoke algorithms - Box-Muller being the one a human can rederive at a whiteboard: take two uniforms, use one for a random angle and the other for a random radius, convert to Cartesian coordinates, and out come two exact standard normals. Polar coordinates do all the work.
The other classic is accept-reject, and it is Part 1's dartboard reused. Suppose you can evaluate a density but can't invert its CDF. Draw the density curve inside a box, throw uniform darts at the box, keep the ones that land under the curve and discard the rest: the survivors' x-coordinates have exactly that density, because tall regions of the curve catch proportionally more darts - which is what density means. The catch is efficiency: if the curve fills little of the box, you throw away almost everything, and in high dimensions that becomes hopeless. That inefficiency is precisely the gap MCMC was invented to fill.
Which distribution should you use?
Mechanics answered, the real question remains, and it is judgment rather than code. The working shortlist is mercifully small:
- Normal - sums of many small independent effects: measurement error, aggregate demand, log-returns over short horizons. Symmetric, and it allows negative values - which disqualifies it for prices, quantities and durations.
- Lognormal - products of many small effects, meaning anything that grows multiplicatively: asset prices, firm sizes, incomes, project durations. Always positive, right-skewed. Rule of thumb: if the natural sentence is “it could go up or down 20%” - percent, not dollars - you're in lognormal country. In valuation you will live here.
- Triangular - the expert-elicitation workhorse: minimum, most likely, maximum, straight from an interview with whoever knows the business. Not deep theory; extremely honest about the shape of what you actually know.
- Exponential and Poisson - the memoryless pair: exponential for waiting times between independent random events, Poisson for the count of such events per period.
- Uniform - genuine “all I know is the range”. Rarer than beginners think: usually you know at least where the middle is, and triangular represents that knowledge better.
The universal recipe
Every Monte Carlo simulation ever built - pi, neutron transport, option pricing, clinical trial design - is the same five steps. Learn the skeleton once and you can read any simulation, and structure your own, instantly.
1Model
Identify the uncertain inputs and give each one a distribution. Decide the dependencies between them - at entry level, independence, stated out loud as an assumption rather than assumed in silence.
2Generate
Draw N scenarios of the inputs from a seeded generator. One scenario is one complete joint draw of every input: one possible world.
3Propagate
Push each scenario through the deterministic business logic - the same arithmetic you'd do for a single case, vectorised over all N worlds at once. All your domain knowledge lives in this step; the other four are boilerplate.
4Aggregate
Collapse N outcomes into answers: mean, spread, percentiles, the probability of landing below a threshold, a histogram. Notice the shift from a number to a distribution - that is the whole cultural payoff. A deterministic model answers “what is the NPV?”; this one answers “what might the NPV be, with what probabilities?”, which is the question the decision actually needs.
5Quantify the simulation error
Standard errors on everything you report, plus the thirty-second ritual: rerun with another seed - conclusions should survive - and double N, where results should move less than your reported error bar. Cheap, and it separates professionals from dart-throwers.
One structural note before the projects: everything here is static Monte Carlo - draw the inputs once, compute an outcome. Many famous applications are dynamic, where the random thing is a path evolving in time: a stock price day by day, a disease progressing, a queue filling and draining. Nothing conceptually new - a path is just a high-dimensional scenario generated one step at a time, and the square-root law's blindness to dimension is exactly why simulation handles paths so calmly - but path generation has its own techniques and its own bugs, so it opens Part 3.
Project 1: from a point-estimate DCF to a distribution of value
You're valuing a small subscription-software business over five years. The classical spreadsheet: pick a revenue growth rate - say 12% - an EBITDA margin of 30%, a discount rate, a terminal multiple; grind the arithmetic; announce that the business is worth $40M.
Every number in that sentence is a guess dressed as a fact. Growth around 12% - could be 5%, could be 20%. Margin around 30% - depends on churn and hiring. The point-estimate DCF takes the midpoint of every uncertainty, multiplies them together, and produces one number with no honest sense of how wrong it might be.
Monte Carlo's proposal: stop feeding the model midpoints. Feed it distributions, and let it tell you the distribution of value. Step 1 of the recipe - the inputs, their distributions, and the reason for each - is the heart of any simulation writeup, and the part a reviewer will actually challenge:
| Input | Distribution | Why |
|---|---|---|
| Revenue growth | A persistent level drawn once per world, around 12% give or take 4%, plus annual noise of about 3% | Businesses have a quality level that persists - good ones keep being good - with year-to-year noise on top. The persistence fattens the tails and creates the genuine compounding convexity behind Jensen's gap. |
| EBITDA margin | Triangular: worst 22%, most likely 30%, best 35% | Straight from the management interview. The skew is real: there are more ways to disappoint than to delight. |
| Discount rate | Fixed at 11% | A choice, not a random state of the world - deliberately not simulated. |
| Exit multiple | Triangular: 6, most likely 8, up to 11 | Market-driven, wide, mildly right-skewed. |
One entry-level simplification, stated out loud: the inputs are independent. In reality growth and exit multiple are probably correlated - good businesses exit rich - and ignoring that understates the spread of value. We accept the bias knowingly and flag it; fixing it is Part 3. Notice the professional posture: not “my model is right” but “here are my assumptions, and here is which direction each one biases the answer”.
Steps 2 and 3 in nine lines, and the idiom to internalise
N, years, wacc, rev0 = 100_000, 5, 0.11, 10.0
rng = np.random.default_rng(2026)
g_bar = rng.normal(0.12, 0.04, size=(N, 1)) # persistent growth, one per world
g = g_bar + rng.normal(0, 0.03, size=(N, years)) # plus annual noise
rev = rev0 * np.cumprod(1 + g, axis=1) # compounded revenue paths
m = rng.triangular(0.22, 0.30, 0.35, size=(N,1))
ebitda = rev * m
disc = (1 + wacc) ** -np.arange(1, years + 1)
mult = rng.triangular(6, 8, 11, size=N)
value = ebitda @ disc + mult * ebitda[:, -1] * disc[-1]The idiom to internalise is the shape: rows are worlds, columns are time. Compounding runs along each row, and the matrix-vector product with the discount factors performs 100,000 complete DCFs in one line. This is Part 1's vectorisation habit paying rent - and it is also your first taste of a dynamic simulation, because each row of the revenue array is a path. Squint and you can see geometric Brownian motion from here: make the log-growth normal per step and let the number of steps grow. That is literally Part 3's first model.
Value the business
Twenty thousand possible five-year futures, one histogram. The dashed line is the deterministic base case - the single number the spreadsheet would have announced. Move the growth assumption and its uncertainty, then set an asking price and read the sentence a point estimate physically cannot say.
Notice three things. The spread is the message: at the default assumptions the value runs from roughly $28M to $55M - uncertain by a factor of two inside our own assumptions, which is the reason the method exists. The shape is right-skewed, because multiplicative arithmetic manufactures lognormal-ish outputs out of symmetric inputs. And the mean sits slightly above the dashed base case: that gap is Jensen's inequality, live - set the uncertainty slider to zero and watch it close.
How an analyst reads this, and the two judgment calls that teach the craft
Percentiles are the deliverable, not the mean. P5 / P25 / P50 / P75 / P95 is how simulation results are actually communicated, because “a P5 of $28.5M” is a stress case with a probability attached - categorically more useful than an arbitrary “bear case”. And tail probabilities come for free: if the asking price is $40M - exactly the base case - the chance of overpaying is about a coin flip, with the consolation that the upside tail is longer than the downside one. Try saying that with a point estimate.
Why not simulate the discount rate? Because it isn't a random state of the world on par with growth - it's the price of risk, and in this design it is precisely the mechanism by which risk gets converted into value. Sprinkling a distribution on it double-counts uncertainty and mixes “what might happen” with “how we price what might happen”. The transferable lesson: not every number in the model should become a distribution. Simulate states of the world; choose policies and prices.
Which input matters most? Freeze the inputs at their central values one at a time and watch the output spread shrink; rank them by how much spread they own. Here the persistent growth assumption dominates - it owns roughly two-thirds of the output variance - with margin and exit multiple splitting the rest. The practical consequence is a work plan: analyst hours belong on stress-testing the growth thesis, churn data and cohort analysis, not on polishing the margin assumption. The simulation just told you where diligence has the highest return. Not bad for forty lines.
Project 2: statistical power, without a formula
Different domain, identical skeleton - which is the point of doing two. You're planning a study: a clinical trial, an A/B test, same maths. A new treatment against control, an effect you expect, a t-test at the 5% level. The question every study must answer before collecting data: how many subjects?
Too few and the study is a coin flip: a real effect exists but your test probably won't see it, and you've spent the budget - or exposed patients - to learn nothing. Too many wastes money and time. The quantity that formalises this is power: the probability your test detects the effect, given that the effect is real. Convention wants at least 80%.
For the textbook case - two groups, normal outcomes, known effect size - power has a closed formula, and you should absolutely use it. But nudge the design one inch toward reality and the formula dies: dropout, unequal groups, skewed outcomes, a nonparametric test, repeated measures, an interim analysis. No formula, or a formula resting on assumptions you know are false.
And once you see it that way the code writes itself, because power is a probability - so estimate it as a frequency, exactly like Part 1's gambler:
def one_study(n_per_group, effect, sd, rng):
a = rng.normal(0, sd, n_per_group) # control
b = rng.normal(effect, sd, n_per_group) # treatment
return ttest_ind(a, b).pvalue < 0.05 # did we detect it?
power = np.mean([one_study(64, 0.5, 1.0, rng) for _ in range(5000)])Note the shape: one_study simulates one complete study, analysis included, and returns a single bit. Power is the mean of those bits. And because power is a proportion, its own standard error is the familiar square root of p times one minus p over the number of simulated studies - with 5,000 studies that's about ±0.6 percentage points, plenty for a design decision.
Size the study
The power curve is the actual deliverable of a sample-size analysis. Each point is 600 complete simulated studies - generate two groups, run the t-test, count the detections. Set the effect you expect, then break the textbook with dropout and watch the closed form stop being able to help you.
With no dropout the simulation should land on the closed form - that's the certification, and it is printed under the number. Now add 15% dropout: the formula has no opinion, and the curve simply moves. That is the moment the method stops being a demo. If you can code the mess, you can power the mess.
The same machinery, pointed at Type I error - a lie detector for methods
Power asks whether the test fires when it should. The mirror question - does it stay quiet when it shouldn't? - is checked by the same code with one character changed: set the effect to zero, and the rejection rate should come out at exactly 5%. That turns simulation into a lie detector for statistical methods. Point it at a t-test on heavily skewed data with fifteen per group and the real Type I error is about 4%, not 5 - conservative, which sounds virtuous until you realise it means quietly underpowered. Other assumption violations push the error the other way.
Now point it at a genuinely bad practice: peek at the p-value every 20 subjects and stop as soon as it's significant. The false-positive rate balloons past 20%, and you have just derived, with your own hands, why sequential peeking without correction is a methodological crime. An enormous fraction of methodological research in biostatistics is exactly this loop - simulate under known truth, audit the method's error and coverage - and you now own the entire template. It fits in a dozen lines.
That last experiment is one we have already run on this site from the other end: the multiple-comparisons case shows the same inflation from scanning many contexts rather than many looks - and fixes it by simulating the null and taking the quantile of the maximum. Part 4 turns that pattern into calibrated sequential designs.
The seven deadly sins
Collected from real code reviews. Each has appeared above; here they are as a pre-flight checklist - the referee's version of this guide.
- 1. Reporting a number without an error bar. The simulation grades its own homework; not asking for the grade is malpractice. And tail percentiles are far noisier than the mean - the P1 of 10,000 scenarios rests on just 100 points, so if the deliverable is a tail, size N for the tail.
- 2. Confusing the spread of the world with the spread of your estimate. “The NPV is $52M ± $30M” - is that the project's risk or your simulation's noise? They differ by a factor of the square root of N, and confusing them misprices either the project or your own competence.
- 3. No seed, or seed chaos. Unreproducible analysis, undebuggable failures, A-versus-B comparisons polluted by luck. One explicit seeded generator per experiment, passed as an argument and recorded in the report - and spawned streams for parallelism, never hand-seeded workers.
- 4. Independence by default, silently. Correlated realities simulated as independent inputs - growth and exit multiples, component failures with a common cause, assets in the same market. The tails come out badly wrong, usually optimistically. Entry-level licence: assume independence out loud, with a sentence on which way it biases the result.
- 5. Trusting the tails of a convenient distribution. Normal tails are thin; reality's usually are not. If the question is about extremes, the distribution choice is the analysis, and no N will rescue a thin-tailed model of a fat-tailed world.
- 6. Judging convergence by eyeball. “The number stopped moving, ship it.” Skewed quantities plateau deceptively between rare huge draws. Convergence is declared by the standard error being small enough for the decision - a number, not a vibe.
- 7. Simulating what should be computed. The square-root law makes simulation the expensive route to easy problems. And when a closed form exists it isn't the enemy - it's the validation case your simulator gets certified against before facing the problems that have no formula.
Now go and build them
Both projects live in the companion notebook, complete and ready to run in your browser. Section 6 builds distributions from uniforms by hand, Section 8 is the full DCF including the Jensen check and the tornado analysis, and Section 9 is the power study with the validation case, the curve, dropout, skew and the peeking experiment. Change one line and it becomes your model.
What's next
- Part 3 - paths and options. What changes when time enters: random walks, geometric Brownian motion, and the option-pricing workflow certified against Black-Scholes. Then correlated inputs via Cholesky - the fix for sin 4 above - and the dependence-modelling error that helped detonate 2008.
Missed the engine and its error bar? Part 1 builds both from scratch, and all four parts are listed on the series hub.
Frequently asked
How do you turn a uniform random number into any distribution?
Use the inverse transform: a uniform draw is a random percentile, and the inverse CDF translates a percentile back into a value, so X = F-inverse(U) has exactly the distribution F. For the exponential it inverts by hand to minus the log of one minus U, divided by the rate. The normal has no closed-form inverse CDF, which is why it gets bespoke algorithms such as Box-Muller.
How do you calculate statistical power by simulation?
Power is the probability your test detects a real effect, so estimate it as a frequency. Write a function that simulates one complete study including its analysis and returns whether it reached significance; run it thousands of times at the effect you care about; take the mean. Validate against the closed form on the textbook case first, then add dropout, skew, or any other realism the formula cannot handle.
Why is a Monte Carlo DCF better than a point-estimate DCF?
A point estimate feeds the midpoint of every uncertainty into the model and returns one number with no sense of how wrong it might be. It is also quietly biased: valuation arithmetic is nonlinear, so the value at the average inputs is not the average value. Simulation feeds distributions instead and returns a distribution of value - which supports the sentences a decision actually needs, such as the probability that the value falls below the asking price.
What are the five steps of a Monte Carlo simulation?
Model the uncertain inputs by giving each a distribution and stating the dependencies. Generate N scenarios from a seeded generator. Propagate each scenario through the deterministic business logic. Aggregate the outcomes into a mean, percentiles and tail probabilities. Quantify the simulation error, then re-run with another seed and double N as a sanity check.
Power, dropout and the traps behind them are exam material long before they are job material. Stat Exam Pro drills the questions, with worked answers.
Get it on iPhone