Search code examples
c#pi

Specify a starting index for continuation of calculating Pi


This C# code will calculate Pi to whatever length I specify. I want to be able to start at a given index without recalculating to that point. Precision is not a great concern as this is a puzzle project but I do need this code to reproduce the same results over and over. It works fine as is but I haven't been able to figure out how to modify for a starting point.

//Looking to pass BigInteger to specify a starting index for continuation of calculating Pi

    public static BigInteger GetPi(int digits, int iterations)
    {
        return 16 * ArcTan1OverX(5, digits).ElementAt(iterations)
            - 4 * ArcTan1OverX(239, digits).ElementAt(iterations);
    }

    public static IEnumerable<BigInteger> ArcTan1OverX(int x, int digits)
    {
        var mag = BigInteger.Pow(10, digits);
        var sum = BigInteger.Zero;
        bool sign = true;
        for (int i = 1; true; i += 2)
        {
            var cur = mag / (BigInteger.Pow(x, i) * i);
            if (sign)
            {
                sum += cur;
            }
            else
            {
                sum -= cur;
            }
            yield return sum;
            sign = !sign;
        }
    }

Solution

  • You are using the Machin formula with the Taylor serie expansion for Arctan. It should give you about 1.4 digits of precision for each "cycle" (see here). You can't "shortcut" the calculation of the Taylor serie. You can speed-up a little the program removing the IEnumerable<BigInteger> part and simply returning the nth iteration (the yield instruction has a cost) and by changing the BigInteger.Pow with a fixed multiplication. But the calculation will still be made iteratively. There is no known way for calculating PI with a precision of n digits in O(1) time.

    Note that there are algorithms (see the wiki) that converge in a smaller number of cycles, but I'm not sure if they converge in a smaller number of operations (their cycles are much more complex).

    An optimized version of the code:

    public static BigInteger GetPi2(int digits, int iterations)
    {
        return 16 * ArcTan1OverX2(5, digits, iterations)
            - 4 * ArcTan1OverX2(239, digits, iterations);
    }
    
    public static BigInteger ArcTan1OverX2(int x, int digits, int iterations)
    {
        var mag = BigInteger.Pow(10, digits);
        var sum = BigInteger.Zero;
        bool sign = true;
    
        int imax = 1 + (2 * iterations);
    
        int xsquared = x * x;
        BigInteger pow = x;
    
        for (int i = 1; i <= imax; i += 2)
        {
            if (i != 1)
            {
                pow *= xsquared;
            }
    
            var cur = mag / (pow * i);
    
            if (sign)
            {
                sum += cur;
            }
            else
            {
                sum -= cur;
            }
    
            sign = !sign;
        }
    
        return sum;
    }