Saturday, December 17, 2022

The circumference of an ellipse

Looking around mathematics this time, I found a 'tube on the circumference of an ellipse - also called perimeter. There's no equation for it! Instead I found a power-series.

The story is an ancient one - differentiation is easy, integration suuucks. Famously there's nothing for the Euler–Poisson integral, the area under the Bell Curve. We want $\int_0^{2\pi}\sqrt {a^2\cos^2\theta+b^2\sin^2\theta}d\theta$. Well it turns out that Johnny Kepler wanted this too and didn't get it. Although, yes; where there's an algebraic powerseries there's an integration for that powerseries. Approximations therefore exist, all multiples of π. Mathematicians enjoy Ramanujan's.

I wondered if C# libraries had anything on ellipses, you know, to save us some time. We do have DrawEllipse... in the Graphics library (Common dll), and Ellipse in Shapes (PresentationFramework dll). Both are visual; neither is Math. Also I'm unsure if anyone's given us something down at the CPU.

Looking into the series for the ellipse-circumference, one happens to own a lot of binary exponents. Coincidentally we've just done some.

My starter-class in C# is

    internal class Ellipse {
        public double h { get; private set; }
        public double major { get; private set; }

        private static int[] binCoef = { 1, 1, 1, 25, 49, 441 };
        private static int[] shifts = { 2, 4, 2, 6, 2, 4 };

        public Ellipse(double a, double b) {
            var minor = a - b;
            major = a + b;
            h = minor * minor / (major * major);
        }

        public double Power(int accuracy) {
            double res = 1;
            double multiplier = 1;
            int denominator = 1;
            StringBuilder sanity = new StringBuilder("1");
            for (int i = 0; i < accuracy; i++) {
                multiplier *= h;
                denominator <<= shifts[i];
                sanity.Append($" + ({String.Format("{0:0.###}", multiplier)} * {binCoef[i]}/{denominator})");
                res += multiplier * binCoef[i] / (double)denominator;
                Console.WriteLine(sanity.ToString() + $" = {res}");
            }
            return res * Math.PI * major;
        }
    }

Before I'd ask for RUST, I can already see some areas of slowness. (Besides the debugging-code.) Like: instead of looping through the arrays with that integer, maybe we can pluck the values with a pointer. And that division by the denominator, which denominator has to be turned into a double... ugggh.

Probably better to store binCoef[i] / denominators[i] as majick-number doubles in a single array; to remove the "denominator" and his bitshifts. But then it would be harder to read.

Alternately...

        public double Power(int accuracy) {
            double res = 1;
            double multiplier = 1;
            double multiplierLog = 0;
            var mathLogh = Math.Log(h);
            int denominator = 1;
            for (int i = 0; i < accuracy; i++) {
                multiplier *= h;
                multiplierLog += mathLogh;
                denominator <<= shifts[i];
                var oof = multiplierLog + Math.Log(binCoef[i]) - Math.Log(denominator);
                res += Math.Exp(oof);
                Console.WriteLine($"{ multiplier * binCoef[i] / (double)denominator} or {Math.Exp(oof)}");
            }
            return res * Math.PI * major;
        }
    }

Here I'd magic-up Math.Log(binCoef[i]) - Math.Log(denominator). The remaining pain-points are mathLogh and (more-so) the repeat res += Math.Exp(oof). Hence the oof. But. If that Log's base is two(2), we might get away with the Evil Bit Hack.

Now: do we need fast-calcs of ellipse-perimeter? I somewhat wonder. For a friend.

No comments:

Post a Comment