Skip to content
December 4, 2007 / wj32

Ways to calculate Pi (in Python)

Enjoy (requires gmpy)!

UPDATE: More Taylor series approximations (I love them), polygon method now works.

"""
    Calculate pi.
"""

import math, cmath, gmpy
import time

# Euler
machin_euler_set = ((5, 7), (2, gmpy.mpf(79, 1024) / 3))
# F. C. W. Stormer
machin_1896_set = ((44, 57), (7, 239), (-12, 682), (24, 12943))
# Kikuo Takano
machin_1982_set = ((12, 49), (32, 57), (-5, 239), (12, 110443))
# Hwang Chien-Lih
machin_2003_set = ((183, 239), (32, 1023), (-68, 5832), (12, 113021), (-100, 6826318), (-12, 33366019650), (12, 43599522992503626068))

def factorial(n):
    if (n == 0):
        return 1
    
    if (n <= 1):
        return n
    
    return n * factorial(n - 1)

def double_factorial(n):
    if (n == 0) or (n == -1) or (n == 1):
        return 1
    
    return n * double_factorial(n - 2)

def taylor_sin(x, precision, n = 100):
    """ Return the taylor series approximation of sine. """
    gmpy.set_minprec(precision)
    sum = gmpy.mpf(0)
    
    for i in xrange(n):
        sum += pow(-1, i) * (gmpy.mpf(x) ** ((2 * i) + 1)) / factorial((2 * i) + 1)
    
    return sum

def taylor_cos(x, precision, n = 100):
    """ Return the taylor series approximation of cosine. """
    gmpy.set_minprec(precision)
    sum = gmpy.mpf(0)
    
    for i in xrange(n):
        sum += pow(-1, i) * (gmpy.mpf(x) ** (2 * i)) / factorial(2 * i)
    
    return sum

def taylor_atan(x, precision, n = 100):
    """ Return the taylor series approximation of arctan. """
    gmpy.set_minprec(precision)
    sum = gmpy.mpf(0)
    
    for i in xrange(n):
        sum += pow(-1, i) * (gmpy.mpf(x) ** ((2 * i) + 1)) / ((2 * i) + 1)
    
    return sum

def calcpi_polygon(sides, precision):
    """ The classic polygon method. """
    gmpy.set_minprec(precision)
    tmp = gmpy.mpf(2.0) - (2.0 * taylor_cos(((gmpy.mpf(360) / sides) * (gmpy.pi(precision) / 180.0)), precision, precision / 4))
    
    return gmpy.mpf(sides) / (gmpy.mpf(2) * gmpy.fsqrt(gmpy.mpf(1) / tmp))

def calcpi_leibniz(n, precision):
    """ The Leibniz method. Converges very slowly. """
    gmpy.set_minprec(precision)
    sum = gmpy.mpf(0)
    
    for i in xrange(n):
        sum += pow(-1, i) * gmpy.mpf(1) / gmpy.mpf((2 * i) + 1)
    
    return sum * 4

def calcpi_newton(n, precision):
    """ The Newton method. """
    gmpy.set_minprec(precision)
    sum = gmpy.mpf(0)
    
    for i in xrange(n):
        sum += gmpy.mpf(factorial(i)) / gmpy.mpf(double_factorial((2 * i) + 1))
    
    return sum * 2

def calcpi_madhava(n, precision):
    """ The Madhava method. """
    gmpy.set_minprec(precision)
    sum = gmpy.mpf(0)
    
    for i in xrange(n):
        sum += pow(-1, i) * (gmpy.mpf(1) / (((2 * i) + 1) * pow(3, i)))
    
    return sum * gmpy.fsqrt(12)

def calcpi_machin(n, precision, set):
    """ The Machin method. Can use multiple sets """
    gmpy.set_minprec(precision)
    sum = gmpy.mpf(0)
    
    for a, b in set:
        sum += gmpy.mpf(a) * taylor_atan(gmpy.mpf(1) / gmpy.mpf(b), precision, precision / 4)
    
    return sum * 4

def calcpi_chudnovsky(n, precision):
    """ The Chudnovsky method. Converges so fast you can't believe it. """
    gmpy.set_minprec(precision)
    sum = gmpy.mpf(0)
    
    for i in xrange(n):
        # (-1)i
        a0 = pow(-1, i)
        # (6i)!
        a1 = factorial(6 * i)
        # 13591409 + 545140134i
        a2 = (545140134 * i) + 13591409
        # product of As.
        a = a0 * a1 * a2
        # (3i)!
        b0 = factorial(3 * i)
        # (i!) ^ 3
        b1 = pow(factorial(i), 3)
        # 640320 ^ (3i + 3/2)
        c = gmpy.mpf(640320)
        b2 = gmpy.fsqrt(c ** ((6 * i) + 3))
        # product of Bs
        b = gmpy.mpf(b0) * gmpy.mpf(b1) * b2
        
        div = gmpy.mpf(a) / b
        sum += div
    
    sum *= 12
    
    return gmpy.mpf(1) / sum

def test_calcpi(func, machin_set = None):
    c = 2
    ls = ""
    t = time.clock()
    final_time = 0
    
    print "Bits:"
    n = sys.stdin.readline()
    
    while True:
        ts = time.clock()
        
        if machin_set is None:
            s = str(func(c, int(n)))
        else:
            s = str(func(c, int(n), machin_set))
        
        final_time = time.clock() - ts
        
        if ls == s:
            break
        
        ls = s
        
        sys.stdout.write("\ri: " + str(c) + (" " * (8 - len(str(c)))) + " pi: " + s)
        c += 1
    
    print "\ntime: " + str(time.clock() - t) + " seconds"
    print "iterations (until no more change): " + str(c - 1)
    print "final time: " + str(final_time) + " seconds"

if __name__ == "__main__":
    import sys
    print "  ----  ----\nTesting Polygon method..."
    print "  ---- HISTORY ----\nI used to use math.cos() for this but it sucks so I wrote my own Taylor series based version of cosine. And it finally works (it's very slow)!"
    test_calcpi(calcpi_polygon)
    print "  ----  ----\nTesting Newton method..."
    print "  ---- HISTORY ----\nThis the first method I implemented. Works pretty slowly (but it's way faster than the Polygon method)."
    test_calcpi(calcpi_newton)
    print "  ----  ----\nTesting Madhava method..."
    print "  ---- HISTORY ----\nThis method is so fast (and old)!"
    test_calcpi(calcpi_madhava)
    print "  ----  ----\nTesting Machin (Euler) method..."
    print "  ---- HISTORY ----\nThese Machin methods' number sets were initially hardcoded, but then I decided to use one function and multiple \"sets\"."
    test_calcpi(calcpi_machin, machin_euler_set)
    print "  ----  ----\nTesting Machin (1896) method..."
    test_calcpi(calcpi_machin, machin_1896_set)
    print "  ----  ----\nTesting Machin (1982) method..."
    test_calcpi(calcpi_machin, machin_1982_set)
    print "  ----  ----\nTesting Machin (2003) method..."
    test_calcpi(calcpi_machin, machin_2003_set)
    print "  ----  ----\nTesting Chudnovsky method..."
    print "  ---- HISTORY ----\nI wanted to try this out because it's used by a pi program on the GMP website. It is SO FAST!"
    test_calcpi(calcpi_chudnovsky)
    print "  ----  ----\nTesting Leibniz method..."
    print "  ---- HISTORY ----\nHit Control+C now, this will take forever..."
    test_calcpi(calcpi_leibniz)

10 Comments

Leave a Comment
  1. paddy3118 / Dec 5 2007 6:15 am

    print 22/7
    :-)

  2. edgarsr / Dec 8 2007 10:16 am

    22/7 is a rational number. Pi is not a rational number ;)

  3. wj32 / Dec 8 2007 10:52 am

    yes that’s right!

  4. David Webb (Photographer) / Apr 8 2008 5:23 pm

    Print 22/7/0???? ;)

  5. subscriber123 / May 24 2008 1:01 pm

    import math
    print math.pi

  6. wj32 / May 24 2008 8:35 pm

    subscriber123, that’s not the point. print math.pi produces low-precision output.

  7. Highlord / Jul 23 2009 3:56 am

    I don’t get the connection between N and precision… anywhere I could read about it or someone who could explain?

  8. aronzak / Aug 20 2009 10:57 pm

    There are other solutions that don’t need gmpy. But thanks for all these.

  9. Matthew / Sep 11 2009 9:29 pm

    I wrote a program that solves pi by using random numbers (as a fun project not for efficiency / accuracy ) and posted a link to this post for people who want to calculate it properly!

    Good work.

    calculating-pi-using-random-numbers

Trackbacks

  1. stealthcopter.com » Python: Calculating pi using random numbers

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.