The Limber Lambda

“Sharpen Up” Series: Episode 5

Posted in Fun by Eric Smith on November 20, 2008

This one got me thinking for a long time, mostly because I subconsciously eliminated “cheat” solutions.  The problem is to calculate an estimate of π by working out the perimeter of an n-sided circle-inscribing polygon.  Let’s start with some pictures:

Now, the obvious solution (focus on Figure A) is to use a trigonmetric function to solve the problem.  Using a convenient r = 0.5 (implies that the perimeter of the circle is π) one side of our polygon (d) can be determined as follows:

d = 2rsin θ

And since the perimeter of the polygon = p = nd, and r = 0.5, and θ = 2π/2n, then:

p = nsin (π/n)

All too easy, and well … we’re using π to find an approximation of π.  Somehow that doesn’t sit well with me.  Which is the reason why I took two days to come up with a solution that didn’t involve trigonometric functions, or π.  Enter Figure B.

We apply a method of virtual construction–let’s assume a cartesian plane, our circle having centre (0,0), and radius r = 0.5.  The method is iterative, so we’ll need to start with a guess of the length of a side of the polygon, Rguess =Pguess / n, where Pguess is our “perimeter guess”.  Pguess can’t be any larger than π, so let’s make the upper bound = 3.2.  Rguess then forms the radius of a “construction circle”, with centre (x,y), starting at (x0,y0) in the diagram.  The intersection of said construction circle and our original circle will determine a polygon corner; of course, since there are two intersection points, we eliminate the one that has already been “visited” (or occurs where y is negative in the case of the first attempt).  We progressively “construct” our way around the circle n times, each time using the previously determined “corner” as the centre of the next construction circle, finally arriving at the final intersection point: (xn, yn).  If Rguess was correct, then (xn, yn) would equal (x0,y0).  Adjusting Rguess for the next iteration then becomes a case of increasing Pguess if yn turned out negative and decreasing Pguess if yn turned out positive.  A simple zero-by-halving (aka Bisection) technique is employed to get a value for the perimeter that satisfies the accuracy constraint (1e-9).

The source code for my π-less solution is below:


using System;

class Archimedes
{
    public double approximatePi(int numSides)
    {
        double upperPi = 3.5;
        double lowerPi = 0.0;
        while (Math.Abs(upperPi - lowerPi) > 1e-10)
        {
            bool first = true;
            double cx = 0.5;
            double cy = 0.0;
            double piGuess = (upperPi + lowerPi) / 2;
            double lastCx = 0.0, lastCy = 0.0;
            for (int i = 0; i < numSides; i++)
            {
                double[][] intersections = GetCirclesIntersection(cx, cy, piGuess, numSides);
                if (intersections[0][0] == Double.NaN ||
                    intersections[0][1] == Double.NaN ||
                    intersections[1][0] == Double.NaN ||
                    intersections[1][1] == Double.NaN)
                {
                    cy = 1;
                    break;
                }
                if (first)
                {
                    if (intersections[0][1] > 0)
                    {
                        lastCx = cx;
                        lastCy = cy;
                        cx = intersections[0][0];
                        cy = intersections[0][1];
                    }
                    else
                    {
                        lastCx = cx;
                        lastCy = cy;
                        cx = intersections[1][0];
                        cy = intersections[1][1];
                    }
                    first = false;
                }
                else
                {
                    if (Math.Abs(intersections[0][0] - lastCx) < 1e-10 &&
                        Math.Abs(intersections[0][1] - lastCy) < 1e-10)
                    {
                        lastCx = cx;
                        lastCy = cy;
                        cx = intersections[1][0];
                        cy = intersections[1][1];
                    }
                    else
                    {
                        lastCx = cx;
                        lastCy = cy;
                        cx = intersections[0][0];
                        cy = intersections[0][1];
                    }
                }
            }
            if (cy < 0.0)
            {
                lowerPi = piGuess;
            }
            else
            {
                upperPi = piGuess;
            }
        }
        return upperPi;
    }

    private double[][] GetCirclesIntersection(double xo, double yo, double piGuess, int n)
    {
        double e = xo;
        double f = yo;
        double p = Math.Sqrt(e*e + f*f);
        double k = (p * p + 0.25 - (piGuess / n) * (piGuess / n)) / (2 * p);
        return new double[2][] {
            new double[] {
                (e*k)/p + (f/p) * Math.Sqrt(0.25-k*k),
                (f*k)/p - (e/p) * Math.Sqrt(0.25-k*k) },
            new double[] {
                (e*k)/p - (f/p) * Math.Sqrt(0.25-k*k),
                (f*k)/p + (e/p) * Math.Sqrt(0.25-k*k)
            }
        };
    }
}

Tagged with:

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.