Estimating Pi with a Monte Carlo Simulation
A small C++ project that uses random points to approximate pi and shows how noisy simulations slowly converge.
Maruf Hossain4 min read
I built a small C++ program to estimate pi using a Monte Carlo simulation.
The idea is simple: throw random points at a square that contains a circle, count how many land inside the circle, and use that ratio to approximate pi.
It is not the most efficient way to calculate pi. That is not the point. The interesting part is watching randomness slowly settle into something predictable.
The Idea
Imagine a square from -1 to 1 on both axes. Inside it is a unit circle centered at the origin.
The square has area 4. The circle has area pi. If points are scattered uniformly across the square, the fraction that land inside the circle should approach pi / 4.
So:
pi ≈ 4 × points inside circle / total points
To test whether a point is inside the circle, the program checks:
x² + y² ≤ 1
The Core Loop
#include <cstdint>
#include <random>
std::random_device seed;
std::mt19937 generator(seed());
std::uniform_real_distribution<double> coordinate(-1.0, 1.0);
std::uint64_t pointsInCircle = 0;
for (std::uint64_t i = 0; i < iterations; ++i) {
const double x= coordinate(generator);
const double y= coordinate(generator);
if (x * x + y * y <= 1.0) {
++pointsInCircle;
}
}
const double estimate=
4.0 * static_cast<double>(pointsInCircle) / iterations;This is the whole simulation. Generate a point, test it, count it, repeat.
I used the C++ <random> library rather than scaling rand() by hand. The distribution expresses the interval directly, and the Mersenne Twister engine gives the simulation a much longer period and more predictable statistical behavior. For debugging or benchmarks, I can replace seed() with a fixed seed so the same run is reproducible.
What I Noticed
The first few estimates jump around a lot. With a small number of samples, the output can look wildly wrong. But as the sample size grows, the estimate starts hovering closer to 3.14159.
That was the useful part of the project. I could see convergence instead of only reading about it.
The rough pattern looked like this, although any individual run can be luckier or worse:
- 1,000 samples: quick, but unstable
- 100,000 samples: usually close enough to recognize pi
- 1,000,000 samples: much steadier
- 10,000,000+ samples: better, but with diminishing returns
Monte Carlo methods are powerful, but they trade precision for sampling. To get much more accuracy, you need many more samples.
Why Convergence Is Slow
The random error decreases in proportion to 1 / sqrt(N), where N is the number of samples. That means getting roughly ten times less sampling error takes about one hundred times as many points.
This explains the diminishing returns. Ten million samples feel enormous compared with one million, but statistically they only reduce the typical random error by a factor of about sqrt(10). Monte Carlo methods become attractive when the underlying problem is too complicated for a neat formula, not because they are the fastest way to calculate pi.
It also means a single final estimate can be misleading. Running the program several times, recording absolute error, and plotting intermediate estimates gives a better picture of the method than showing only the best result.
Turning It into a Small Tool
The first version only printed an estimate. I later added command-line options for sample count and progress, timing and error measurements, and CSV output for plotting convergence.
Those features did not change the formula, but they made the experiment repeatable. I could run the same sample size under different compiler settings, save intermediate estimates, and compare how quickly the error narrowed instead of relying on one terminal printout.

What I Learned
- Randomness can be useful when exact calculation is difficult or expensive.
- More samples improve the estimate, but not linearly.
- Reproducible seeds make randomized programs easier to test and compare.
- Simple simulations are a good way to build intuition for probability.
- Seeing the output change over time made the math feel less abstract.
Source
Small projects like this are easy to dismiss, but they are useful because the feedback loop is so clear. You write a few lines, run the program, and watch a mathematical idea become visible.
— Maruf