The problem in brief
The notebook first shows a worked example: throw a fair die until an even number appears. The expected number of throws, 2, is computed analytically and then estimated by simulating the experiment many times and averaging. You then write two Monte Carlo programs of your own. Soal A: keep throwing until the running total of the faces reaches at least 12; how many throws does that take on average? Soal B: throw until the first 6; given that this happened on exactly the 5th throw, how many odd faces do you expect to have seen? It is an exercise from the exhibition final of the Indonesian national science olympiad (OSN) in AI.
How you are scored
Soal A is worth 30 points and Soal B 70 points. The deliverable is a working simulation whose estimate is close to the true expectation. We therefore give, for each question, a Monte Carlo estimate with its uncertainty and check it against the exact value derived by hand.
One remark on the worked example: it writes for the geometric distribution. The correct formula is . With both give 2, which hides the slip, but with (waiting for a 6) the answer is 6, not 1.2.
The key idea
A Monte Carlo estimate is just a sample mean of independent runs of the experiment, so it comes with a standard error: if the runs have standard deviation , the mean of runs has error about . This tells you how many runs you need and whether your simulation agrees with a formula.
The two questions test two different skills:
- A is about simulating a stopping rule correctly: stop as soon as the total reaches 12, and count the throw that crossed the threshold.
- B is about simulating a conditional expectation. There are two honest ways to do it. You can simulate the unconditional experiment and keep only the runs that satisfy the condition (rejection sampling). Or you can work out what the condition implies and sample from the conditional law directly.
Soal A: throws until the total reaches 12
Simulation.
def one_run_A(target=12):
total, throws = 0, 0
while total < target:
total += random.randint(1, 6)
throws += 1
return throws
runs = [one_run_A() for _ in range(200_000)]
print(np.mean(runs), 1.96 * np.std(runs) / np.sqrt(len(runs)))
With 200 000 runs we get (95 % interval).
Exact check. Let be the expected number of additional throws when the current total is . Then for , and conditioning on the next throw gives
Computing backwards from with exact fractions gives
inside the Monte Carlo interval.
A sanity check worth knowing. A naive guess is throws, since each throw adds 3.5 on average. It is too low because the last throw overshoots: when you stop, the total is between 12 and 17. Wald's identity makes this exact: , so the average final total is , i.e. an average overshoot of 1.67 above 12.

Soal B: odd faces, given that the first 6 came on throw 5
Simulation by rejection. Run the experiment "throw until a 6" many times and keep only the runs of length exactly 5:
kept = []
while len(kept) < 50_000:
faces = []
while True:
f = random.randint(1, 6); faces.append(f)
if f == 6: break
if len(faces) == 5: # the condition of the question
kept.append(sum(f % 2 for f in faces))
print(np.mean(kept))
We get from 50 000 accepted runs. Only about 8 % of the runs are accepted, since . Rejection sampling is simple and always correct, but it wastes most of the work when the condition is rare.
Exact value. The condition says: throws 1 to 4 are not 6, and throw 5 is a 6. Since the throws are independent, conditioning on "not a 6" makes each of the first four throws uniform on . Three of those five faces are odd, so each of the first four throws is odd with probability . The fifth throw is a 6, which is even. By linearity of expectation,
The same reasoning gives a much faster simulation, with no rejection at all: draw four faces uniformly from 1 to 5 and count the odd ones. It returns 2.4021.
The classic trap is to ignore the conditioning: "4 throws before the 6, each odd with probability 1/2", which gives 2. Knowing that a throw was not a 6 removes one even face, so odd faces become more likely (). Conditioning changes the distribution of the other throws, and a simulation that does not enforce the condition answers a different question.
Results
| question | Monte Carlo (95 % interval) | exact |
|---|---|---|
| worked example (throws until even) | about 2 | |
| Soal A | (200 000 runs) | |
| Soal B, rejection sampling | (50 000 accepted runs) | |
| Soal B, direct sampling of the conditional law | (200 000 runs) | |
| Soal B, naive (no conditioning) | (wrong) |
What to remember
- Always report a Monte Carlo estimate with its standard error ; it tells you how many runs you need and whether the simulation agrees with theory.
- Simulate the stopping rule exactly: the throw that crosses the threshold counts, and the overshoot is why the answer exceeds .
- For a conditional expectation, either reject the runs that violate the condition or sample from the conditional law directly; never ignore the condition.
- Check simulations against small exact computations (a backwards recursion over states, linearity of expectation). They catch both coding bugs and formula slips like instead of .