Leibniz_Pi.py

Bengt Richter bokr at oz.net
Sat Oct 12 09:14:45 CEST 2002

On Fri, 11 Oct 2002 10:14:38 -0700, "Cousin Stanley" <CousinStanley at HotMail.com> wrote:

>Yesterday in the news group  comp.lang.java.help
>I found a Java example of the  Leibniz Pi  approximation
>and converted the Java code to Python ...=20
>
>The numerical results from either version=20
>using a  LARGE  number of terms as a program argument
>provide approximations for the number  Pi   that seems correct ...=20
>
>Using  1 million  terms as an input argument,
>the Java version is a bit faster, which I think
>is expected ...=20
>
>   On a 250 MHz Win98 machine ...=20
>  =20
>     Java SDK 1.4 .......  ~ 3-4   seconds
>    Python 2.2.1 .........  ~ 9-10 seconds
>
>Since I am fairly new to  BOTH  Java  AND  Python,
>I'm wondering if there are any ...=20
>
>  obvious oversights        ???
>  Python improvements ???
>
>Python code follows ...
>
>Cousin Stanley
>
>----------------------------------------------------------------
>
>'''=20
>    Leibniz_Pi.py
>
>    Pi =3D 4 * ( 1 - 1/3 + 1/5 - 1/7 + 1/9 - 1/11 + 1/13 - 1/15 ... )
>
>    Using one million terms provides a good approximation ...
>
>    Posted to the NewsGroup
>    comp.lang.java.help           2002-10-10=20
>    By Jonas Lindstr=F6m
>
>    Converted to Python
>    By Stanley C. Kitching
>'''
>
>import sys
>print '\n    Approximate  Pi  via Leibniz Sequence '
>
>def calculate_pi( nTerms ) :
>
>    limit =3D ( 2 * nTerms ) - 1=20
>
>    if ( ( limit - 1 ) % 4 ) =3D=3D 0 : =20
>           sign =3D +1.0=20
>    else :=20
>           sign =3D -1.0
>
>    sum =3D 0.0
>
>    for i in range( limit , 0 , -2 ) :=20
>        sum +=3D  sign / i
>        sign =3D -sign
>
>    return 4.0 * sum=20
>
>nTerms =3D int( sys.argv[ 1 ] )=20
>
>Pi =3D calculate_pi( nTerms  )
>
>print '\n    Terms : ' , nTerms
>print '\n       Pi : ' , Pi
>
>

[ 0:01] C:\pywk\pi>pisigint.py
(Type Ctrl-C to stop generating pi)
50  4085.12 dig/sec  31415926535897932384626433832795028841971693993751
100  3292.99 dig/sec  05820974944592307816406286208998628034825342117067
150  2422.61 dig/sec  98214808651328230664709384460955058223172535940812
200  1851.27 dig/sec  84811174502841027019385211055596446229489549303819
250  1473.79 dig/sec  64428810975665933446128475648233786783165271201909
300  1277.74 dig/sec  14564856692346034861045432664821339360726024914127
350  1087.28 dig/sec  37245870066063155881748815209209628292540917153643
400  1003.15 dig/sec  67892590360011330530548820466521384146951941511609
450   852.50 dig/sec  43305727036575959195309218611738193261179310511854
500   742.55 dig/sec  80744623799627495673518857527248912279381830119491
550   666.12 dig/sec  29833673362440656643086021394946395224737190702179
600   624.75 dig/sec  86094370277053921717629317675238467481846766940513
650   595.61 dig/sec  20005681271452635608277857713427577896091736371787
700   535.77 dig/sec  21468440901224953430146549585371050792279689258923
750   501.08 dig/sec  54201995611212902196086403441815981362977477130996
800   470.28 dig/sec  05187072113499999983729780499510597317328160963185
850   436.58 dig/sec  95024459455346908302642522308253344685035261931188
900   333.36 dig/sec  17101000313783875288658753320838142061717766914730
950   363.52 dig/sec  35982534904287554687311595628638823537875937519577
818577805321712
965 Total digits

(965 total digits)

(I hit Ctrl-C fairly shortly).

You can redirect the pi digits, and watch the left part on a single line
being rewritten, which looks like so:

[ 0:10] C:\pywk\pi>python pisigint.py >zz.txt
(Type Ctrl-C to stop generating pi)
2455 Total digits

[ 0:10] C:\pywk\pi>

----< pisigint.py >-------------------------
#!/usr/bin/python

# Print digits of pi forever, unless you hit Ctrl-C
# The output is split between stderr and stdout so that you can
# redirect the pi digits to a file, like so
#   python pisiginit.py > pidigits.txt
# and still see how many and how fast digits are being output. The latter
# info is rewritten without \n, so it does not scroll. If you don't
# redirect you see the latter to the left and the pi digits 50 at a time
# to the right. This is a modified version of the algorithm as found
# somewhere on the Ruby site.
#
# The algorithm, using Python's 'long' integers ("bignums"), works
# with continued fractions, and was conceived by Lambert Meertens.
#
# See also the ABC Programmer's Handbook, by Geurts, Meertens & Pemberton,

import sys, signal
from time import clock
class SigHandler:
def __init__(self):
self.signaled = 0
def __call__(self, sn, sf):
self.signaled += 1

sh = SigHandler()
oldHandler = signal.signal(signal.SIGINT,sh)

k, a, b, a1, b1 = 2L, 4L, 1L, 12L, 4L

digits=0 # count to show progress
lines = 0
digline = []
start=clock()
sys.__stderr__.write('\r(Type Ctrl-C to stop generating pi)\n')

while not sh.signaled:
# Next approximation
p, q, k = k*k, 2*k+1, k+1
a, b, a1, b1 = a1, b1, p*a+q*a1, p*b+q*b1
# Print common digits
d = a / b
d1 = a1 / b1
while d == d1:
digits += 1
#sys.__stderr__.write('\r%5d' % (digits,))
digline.append(d)
if len(digline)==50:
sys.__stderr__.write('\r%5d %8.2f dig/sec  ' % (digits, 50.0/(clock()-start)))
lines += 1
print ''.join(map(lambda x: chr(x+48),digline))
digline = []
start=clock()
a, a1 = 10*(a%b), 10*(a1%b1)
d, d1 = a/b, a1/b1
sys.__stderr__.write('\r'+' '*24)
if digline: print ''.join(map(lambda x: chr(x+48),digline))
sys.__stderr__.write('\r%5d Total digits                   \n'% (lines*50+len(digline)))
print '\n(%d total digits)' % (lines*50+len(digline))
signal.signal(signal.SIGINT,oldHandler)
--------------------------------------------

Regards,
Bengt Richter