Pi Squared... Literally

For the past several years, in celebration of Pi Day, I’ve been creating simulations of various experiments and techniques to estimate the value of ππ. In the past, I’ve done experiments such as the Buffon’s Needle problem and Archimedes’ polygonal estimation. You can find these in my lab. 2024 is no different. This time around, I’m going back to the basics. That is, I will be estimating ππ using basic geometry and probability.

You can check out the final result here or read the source code.

The Mathematical Proof

Suppose you have a square that looks like this:

It is divided down the exact middle vertically and horizontally. If you pick a completely random point in the square, what is the probability that it lands to the top-left quadrant? From this visual, it’s pretty easy to see that there’s a 25% chance of landing in the top-left or in any of the other quadrants. The reason this works is because the smaller square region that comprises the top-left quadrant has an area that is 14\frac{1}{4} the area of the entire square. We can even move this smaller square.

It’s a little bit less obvious here, but the probability that a point chosen at random lands in the green region of the final shape is still 25%. We’ve simply moved the shape, but did not alter it, so its area remains the same. This is a principle that will help us in determining the value of ππ:

The probability that a point chosen at random within a shape lands within a specific region of the shape is equal to the proportion between their areas.

Now let’s suppose that we have a circle that goes right up to the edges of the square, like this:

2r r

The area of a circle is πr2πr^2, where rr is its radius. If we encase the circle exactly inside of a square, the square would have an edge length or 2r2r. The area of a square is l2l^2, where l is its length; therefore, the area of a square that perfectly encloses a circle of radius rr is (2r)2(2r)^2. This expression can be expanded into 22×r22^2 × r^2, or simply 4r24r^2. With this information, we can conclude that the ratio between a circle and its circumscribed square is πr24r2\frac{πr^2}{4r^2}. We can simplify this fraction by dividing r2r^2 from both expressions, which leaves us with a ratio of π4\frac{π}{4}. That is the next principle:

The proportion between the areas of a circle and a square that perfectly encloses it is π4\frac{π}{4}.

In conjuction with the first statement, we can conclude that π4\frac{π}{4} is also the probability that a random point in the square also resides in the circle. That’s great and all, but that would require us to know what the value of ππ is, which defeats the purpose of an experiment to estimate it. So let’s assume we don’t know the value of ππ. How do we use what we know to deduce its value?

You may remember learning in elementary school the idea of “theoretical probability” and “experimental probability.” Theoretical probability is the calculated probability that an event occurs based on the possible outcomes. For example, a fair coin has two sides — heads and tails. Each side has an equal chance of landing up. The theoretical probability of the coin landing heads up is, therefore, 12\frac{1}{2}.

Experimental probability, on the other hand, is the calculated probability of an event based on actual trials. Going back to the coin example, we can determine the probability that it lands on heads by physically flipping a coin. Our first flip lands on heads, which means that the probability that flipping a fair coin lands on heads is 11\frac{1}{1} or 100%. Obviously, this is a ludicrous statement, but we only performed one trial. After ten trials, we may end up with six heads and assume the probability to be 60%. While that’s not quite the 12\frac{1}{2} we expect to see, it’s much closer. After a total of 100 trials, 47 of which are heads, we arrive at 47%, closer still. And that’s the final principle I want to introduce:

The experimental probability of an event converges to its theoretical probability as more trials are performed.

Using the principles I have outlined, we now should be able to devise an experiment to deduce the value of ππ. In a way, we will be playing virtual darts on a board similar to the diagram above, with a circle inside of a square. If we throw random points, count the number of points that land inside the circle, and divide that by the total number of points selected, we can come up with an experimental probability of hitting the circle. The probability converges to our theoretical probability of π4\frac{π}{4}. Therefore ππ can be derived from multiplying that ratio by 4.

Technical Challenges

Taking on a project like this requires an understanding of geometry. Drawing shapes on a canvas is easy, and so is selecting points with a random xx and a random yy value that’s constrained within the boundaries of a square. But how do we know if the point is inside of the circle?

To figure out whether or not a point intersects with a circle, we need to calculate its distance to the center of the circle. As long as the distance is less than the radius of the circle, the point is inside of the circle. We can do this using the good old Pythagorean theorem, which states that a2+b2=c2a^2 + b^2 = c^2.

Essentially, we’ll be treating the board as a cartesian grid, with the center being the origin (0, 0). The coordinates that represent any given point can then be used as the sides of a right triangle, or aa and bb. We can use this value to calculate cc, which forms the hypotenuse of two right triangles. Given the values of aa and bb, we can rearrange the Pythagorean theorem to calculate the value of cc as c=a2+b2c =\sqrt{a^2 + b^2}. This gives us the distance from the center of the circle to the chosen point. As long as this distance is less than the radius of our circle, the point is inside of it. You can drag the point in the diagram below to see this formula at work.

Coordinates: (30, 61)Distance: 67.98Radius: 120In Circle: Yes

This is what the code ends up looking like.

  const isInCircle = (x: number, y: number) => {
    const adjustedX = Math.abs(x - radius);
    const adjustedY = Math.abs(y - radius);

    const distance = Math.sqrt(adjustedX ** 2 + adjustedY ** 2);

    return distance <= radius;
  };

Right off the bat, I knew that performance was going to be an issue. I want to be able to generate as many points as possible as quickly as possible, since more trials will get us closer to ππ. So I knew that storing the individual points in memory was not going to be a good idea. Instead, I just have two variables to track the total number of points and the number that lands inside the circle. Each time a point is added, the coordinates are generated, and then those two variables are incremented accordingly, and finally the point is added to the canvas.

  const addPoint = () => {
    const x = Math.floor(Math.random() * width);
    const y = Math.floor(Math.random() * width);

    const inCircle = isInCircle(x, y);

    canvas.addPoint(x, y, inCircle);

    if (inCircle) circlePoints++;
    totalPoints++;
  };

Initially, I used SVG to draw the visuals for this experiment. However, as you can imagine, when we are generating tens of millions of points, adding that many elements to the DOM can make a real mess of things. Ultimately, I ended switching to HTML canvas, which sped it up significantly. While canvas isn’t nearly as flexible as SVG, it’s comparable to working with a raster image. Whenever we add a point on a canvas, we’re directly manipulating the pixels within it. This takes up much less memory than adding a separate element for every point.

Geometry fascinates me because everything lines up so perfectly. We saw this with Buffon’s Needle, and in how the angle of a toothpick forms a hidden circle that conceals ππ within it. We’re seeing it again now in an imaginary game of darts. Some of these even feel like supernatural coincidences that shouldn’t be possible. If math has taught me anything, it’s that there’s an explanation for everything.