This notebook covers the simplest of the four types of simulation optimization problems: , so we only have to decide which of two scenarios is better. We compare them with a confidence interval for the difference, and show how common random numbers give a narrower interval for the same number of runs. Common random numbers return in all following notebooks: Ranking and Selection, Local Search and Gradient Methods. The last two also show a downside: less noise also means less exploration, which can keep a search in a local optimum.
Learning outcomes
On completion of this notebook, you will be able to:
compare two scenarios with a confidence interval for the difference of their expected performances
explain why positively correlated matched pairs reduce the variance of the difference
use common random numbers in a simulation, and quantify how many runs they save
Independent Samples¶
With , one scenario gives a random performance and the other a random performance , for example the throughput of two layouts of a factory. The question is whether one scenario is better than the other, that is, whether .
The most direct approach simulates both scenarios times, independently: runs of the first scenario and of the second. As in Variability (Recap), we write for the (unknown) variance of , and and for the sample average and sample variance of the runs, which estimate and ; the same for . To have one notation for this section and the next, let and , the difference of the -th runs. Then estimates , and because the runs of the two scenarios are independent,
so the sample variance of the differences is approximately . A 95% CI for is
The test for two independent samples of comes down to this CI: at significance level 5% (two-sided), we reject exactly when 0 does not lie in the CI. In that case we have evidence that one scenario is better than the other. Writing the test as a CI has an advantage: the width of the CI, , shows directly how much a smarter design of the simulation gains. If the design makes smaller, the CI gets narrower for the same number of runs .
Matched Pairs and Common Random Numbers¶
We can often do better by comparing the two scenarios in a similar, “fair” test setting. We take matched pairs of samples , and make sure that the two samples of each pair are positively correlated by simulating them under similar conditions. Then we compute the differences and make the CI for from them exactly as above, as in the test for matched pairs. If , the difference is significant.
The pairing gives more statistical power because it makes the CI narrower. For and that are not independent,
The covariance measures how and move together. If it is positive, a large sample of tends to come with a large sample of , so their difference is relatively small. So a positive covariance reduces compared to independent samples, where the covariance is 0. A smaller gives a narrower CI for the same , and as a result, if , the CI excludes 0 after fewer runs: we see earlier that one scenario is better.
In practice, we get this positive correlation with common random numbers (CRN): we use the same underlying random draws for both scenarios (the same simulated demands, the same arrival moments, or in general the same seed), instead of drawing fresh randomness for each scenario.
Example: The Effect of Common Random Numbers¶
A shop decides between ordering 9 or 10 units of a product. Demand is Poisson with mean 10, a unit is sold for €5 and bought for €3, and unsold units are worthless. We simulate the difference in profit between the two order quantities 1000 times, once with independent demand for the two scenarios and once with CRN, where the same demand feeds both:
import numpy as np
rng = np.random.default_rng(1)
n_runs = 1000
price, cost = 5, 3
order_a, order_b = 9, 10
def profit(order, demand):
return price * np.minimum(demand, order) - cost * order
# independent sampling: a separate demand stream per scenario
profit_a_indep = profit(order_a, rng.poisson(10, n_runs))
profit_b_indep = profit(order_b, rng.poisson(10, n_runs))
diff_indep = profit_b_indep - profit_a_indep
# common random numbers: one shared demand stream for both scenarios
demand_shared = rng.poisson(10, n_runs)
profit_a_crn = profit(order_a, demand_shared)
profit_b_crn = profit(order_b, demand_shared)
diff_crn = profit_b_crn - profit_a_crn
for name, diff in [("independent", diff_indep), ("CRN", diff_crn)]:
mean, sd = diff.mean(), diff.std(ddof=1)
half_width = 1.96 * sd / np.sqrt(n_runs)
print(
f"{name:>11}: s_Y = {sd:5.2f}, 95% CI for EY: "
f"[{mean - half_width:.2f}, {mean + half_width:.2f}]"
)independent: s_Y = 11.31, 95% CI for EY: [-1.13, 0.28]
CRN: s_Y = 2.50, 95% CI for EY: [-0.49, -0.18]
With independent sampling, the CI contains 0: after 1000 runs we cannot tell which order quantity is better. With CRN, the CI lies entirely below 0, so ordering 10 units gives a lower expected profit than ordering 9. Figure 1 and Figure 2 show why: with CRN, a day with high demand raises the profit of both order quantities, and a day with low demand lowers both, so the two profits move together and their difference varies much less.
Figure 1:Profit of ordering 9 and 10 units in the first 40 runs, with independent demand for the two order quantities.
Figure 2:Profit of ordering 9 and 10 units in the first 40 runs, with common random numbers: both order quantities face the same demand in each run.
How many runs does CRN save here? The CI has width , where is the sample standard deviation of the differences. Because of the , halving the width takes 4 times as many runs, and in general, making the width times smaller takes times as many runs. CRN makes , and thus the width, times smaller without extra runs, with
To get the CI of CRN with independent sampling, we would need times as many runs:
sd_ratio = diff_indep.std(ddof=1) / diff_crn.std(ddof=1)
print(f"f = {sd_ratio:.1f}, f^2 = {sd_ratio**2:.0f}")
n_equivalent = sd_ratio**2 * n_runs
print(f"independent runs per scenario for the same width: {n_equivalent:.0f}")f = 4.5, f^2 = 21
independent runs per scenario for the same width: 20553
CRN makes the CI about 4.5 times narrower, which with independent sampling would take about 20 times as many runs: about 20,500 runs per scenario instead of 1000. And CRN costs nothing extra: we only reuse the demands we drew. The two order quantities are close, so their profits are strongly correlated under CRN; for scenarios that differ more, the savings are smaller.
References¶
Koole, G. (2019). An Introduction to Business Analytics. §8.1.