TranslateProject/sources/tech/20201026 Pi from High School Maths.md
DarkSun 4d4fb56c13 选题[tech]: 20201026 Pi from High School Maths
sources/tech/20201026 Pi from High School Maths.md
2020-10-27 05:07:51 +08:00

11 KiB
Raw Blame History

Pi from High School Maths

Warning: I dont think the stuff in this post has any direct practical application by itself (unless youre a nuclear war survivor and need to reconstruct maths from scratch or something). Sometimes I like to go back to basics, though. Heres a look at (\pi) and areas of curved shapes without any calculus or transcendental functions.

A simple algorithm for calculating (\pi)

This algorithm starts with simple number theoretic musing. Some whole numbers form neat Pythagorean triples ((x,y,z)) where (x^{2} + y^{2} = z^{2}). E.g., (3^{2} + 4^{2} = 5^{2}). Its easy to find all the solutions to (x^{2} + y^{2} = 5^{2}) through brute-force search because we know that (x) and (y) cant be bigger than (5). Here they are:

[\begin{matrix} {0^{2} + 5^{2}} & {= 5^{2}} \ {3^{2} + 4^{2}} & {= 5^{2}} \ {4^{2} + 3^{2}} & {= 5^{2}} \ {5^{2} + 0^{2}} & {= 5^{2}} \ \end{matrix}]

(Plus all the negative-number combinations, but lets stick with non-negative integers and just count 4 solutions.) If we relax the equation, and count solutions to (x^{2} + y^{2} \leq 5^{2}), the answer turns out to be 26. Why care? Well, if (t) is the total number of solutions to (x^{2} + y^{2} \leq n^{2}), then

[\lim\limits_{n\rightarrow\infty}\frac{4t}{(n + 1)^{2}} = \pi]

Or, in code, heres a simple program that estimates (\pi), getting more accurate for bigger values of the n variable:

import std;

ulong sq(ulong x) pure
{
    return x * x;
}

void main(string[] args)
{
    const n = args.length > 1 ? args[1].to!ulong : 20;

    ulong total;
    foreach (x; 0..n+1)
    {
        foreach (y; 0..n+1)
        {
            if (sq(x) + sq(y) <= sq(n)) total++;
        }
    }

    /*
    // Alternatively, for functional programming fans:
    const total =
        cartesianProduct(iota(n+1), iota(n+1))
        .filter!(p => sq(p[0]) + sq(p[1]) <= sq(n))
        .walkLength;
    */

    writef("%.12f\n", 4.0 * total / sq(n+1));
}

$ ./pi_calc
3.038548752834
$ ./pi_calc 10000
3.141362256135

Okay, thats a little bit more accurate than (\frac{22}{7}). Unlike most formulae for (\pi), though, theres a simple diagram that shows how it works. Imagine we lay out the ((x,y)) integer pairs (where (x) and (y) range from (0) to (n)) on a 2D grid the obvious way. The figure below shows an example for (n = 10), with the arrow (r) pointing from the origin to ((6,8)). (r) and the (x) and (y) components make a right-angled triangle, so Pythagorass theorem says that (x^{2} + y^{2} = r^{2}). For ((6,8)), (r = 10 = n), so ((6,8)) is on the boundary as a solution to (x^{2} + y^{2} \leq 10^{2}). That boundary (the set of real-valued points a constant distance (n = 10) from the origin) makes a quarter circle.

A circle is a simple, convex shape, and the grid points are evenly spaced, so the number of points inside the quarter circle will be roughly proportional to the area. More specifically, the fraction of all the grid points inside the quarter circle will be roughly the area of the quarter circle divided by the area of square around all points. The quarter circle area is (\pi r^{2} \div 4), inside the square of area (r^{2}) (remember, (n = r)), so (\frac{\pi}{4}) of all points represent solutions. The (x) and (y) values count from (0) to (n), so there are ((n + 1)^{2}) grid points. Rearrange the equations and you get a formula for estimating (\pi) from a solution count. The grid points keep drawing an arbitrarily more accurate circle as (n) gets bigger (just like a higher-resolution computer monitor does) so the estimate is exact in the limit.

A faster implementation

The code above is simple but slow because it brute-force scans over all ((n + 1) \times (n + 1))possible (x) and (y) values. But we obviously dont need to scan all values. If we know that (x^{2} + y^{2} \leq n^{2}), then making (x) or (y) smaller will only give us another solution. We dont need to keep testing smaller values after we find a solution. Ultimately, we only need to find the integral points around the boundary. Heres a faster algorithm based on that idea.

Imagine we scan along the integral (x) values and find the maximum integral (y) value that still gives us a solution. This gives us a border line marked in red in the figure below. If (y = 8) for a given (x) value, we instantly know there are (8 + 1 = 9) solutions with that given (x) value ((+ 1) to count the (y = 0) solution).

Note that as (x) scans from (0) to (n), (y) starts at (n) and decreases to (0). Importantly, it only decreases — its monotonic. So if we scan (x) from (0) to (n), we can find the next boundary (y) point by starting from the previous boundary point and searching downwards. Heres some code:

ulong y = n, total;
foreach (x; 0..n+1)
{
    while (sq(x) + sq(y) > sq(n)) y--;
    total += y + 1;
}

This version still has nested loops, so it might look like its still (O(n^{2})). However, the inner while loop executes a varying number of times for each (x) value. Often the y-- doesnt trigger at all. In fact, because y starts from n and monotonically decreases to 0, we know the y-- will be executed exactly n times in total. Theres no instruction in that code that executes more than (O(n)) times, total, so the whole algorithm is (O(n)).

With 64b ulong integers, the largest value of n that works before overflow is 4294967294:

$ ./pi_calc 4294967294
3.141592653058

There are ways to get faster convergence using numerical integration tricks, but I like the way this algorithm only uses integer arithmetic (up until the final division), and can be understood directly from simple diagrams.

Area of a circle without calculus

Perhaps you feel a bit cheated because that algorithm assumes the (\pi r^{2}) formula for the area of a circle. Sure, thats arguably included in “high school maths”, but its something students just get told to remember, unless they study integral calculus and derive it that way. But if were going to assume (\pi r^{2}), why not assume the theory of trigonometric functions as well, and just use (\pi = 4\operatorname{atan}(1))?

The great ancient Greek mathematician Archimedes figured out the circle area over two thousand years ago without integral calculus (or trigonometric functions for that matter). He started with an elegant insight about regular (i.e., equal-sided) polygons.

The famous “half base times height” formula for the area of a triangle already had a well-known proof in the first book of Euclids Elements of Geometry (easily derived from a theorem about parallelograms). Conveniently, any regular polygon can be split into equal triangles joined to the centre. For example, a regular hexagon splits into six triangles, as in the figure below. We can take any one of the triangles (theyre all the same) and call the “base” the side thats also a side of the polygon. Then the “height” is the line from the centre of the base to the centre of the polygon.

Now heres Archimedess neat insight: The ratio of the triangle area to the base is (\frac{h}{2}). If you add up all the areas, you get the area of the polygon. Likewise, if you add up all the bases, you get the perimeter of the polygon. Because the triangle area/base ratio is a constant (\frac{h}{2}) for all triangles, the area/perimeter ratio for the whole polygon is the same (\frac{h}{2}). As a formula, the area of any regular polygon is (P \times \frac{h}{2}) (where (P) is the perimeter).

If you think of a circle as a regular polygon with infinitely many sides (so that (h) becomes the radius of the circle), and use the circle circumference ((2\pi r)) as your basic definition of (\pi), then that implies the area of a circle is (2\pi r \times \frac{r}{2} = \pi r^{2}).

Of course, Archimedes was a respected mathematician who couldnt get away with just assuming that anything true of a polygon is true of a circle (counterexample: all polygons have bumpy corners, but circles dont). He used the kind of geometric proof by contradiction that was popular in his day. (He even took it further and analysed spheres, cylinders, parabolas and other curved objects, almost inventing something like modern real analysis a couple of millenia early.) Sadly, not all of his mathemetical work has survived, but the key part of his Measurement of a Circle has.

Heres the high-level version. Archimedes claimed that the area of a circle is (2\pi r \times \frac{r}{2}). Suppose you think his value is too small, and the circle is really bigger than (2\pi r \times \frac{r}{2}). That means theres enough room inside the circle to fit a regular polygon thats also bigger than (2\pi r \times \frac{r}{2}). But Archimedes said thats contradictory because for any such polygon, (h < r), and (P < 2\pi r) (because each side of the polygon is a straight line thats shorter than the circle arc that connects the same points), so the area (A = P \times \frac{h}{2} < 2\pi r \times \frac{r}{2}). The polygons area cant be both bigger and smaller than (2\pi r \times \frac{r}{2}).

Archimedes argued that theres a similar contradiction if you think (2\pi r \times \frac{r}{2}) is too big, and the circle area is smaller than that. In that case he could make a polygon thats also smaller than (2\pi r \times \frac{r}{2}), yet still wraps around the circle. For this polygon, (h = r), but he said the perimeter of the polygon must be greater than (2\pi r)1, so that the polygons area must be bigger than (2\pi r \times \frac{r}{2}), even though its also meant to be smaller.

If both of those cases lead to contradiction, were left with the only alternative that the circle area is (\pi r^{2}).

  1. I dont actually know how he argued this. ↩︎

via: https://theartofmachinery.com/2020/10/26/pi_from_high_school_maths.html

作者:Simon Arneaud 选题:lujun9972 译者:译者ID 校对:校对者ID

本文由 LCTT 原创编译,Linux中国 荣誉推出