Using a series to calculate pi in Python
Question:
pi/2 = 1 + 1/3 + (1*2) / (3*5) + (1*2*3) / (3*5*7) + ...
Okay, let’s try this again.
I need to write a function that takes the max error as a parameter for the value of pi and returns the calculated value of pi and the number of iterations necessary to get to that point. I can’t use a recursive algorithm.
Thus far I have:
def piEuler (x):
count = 0.0
approx = 0.0
approx2 = 1.0
error = math.pi/2 - approx2
while error > x:
count = count + 1
approx3 = approx + approx2
error = math.pi/2 - approx3
#print error
approx = approx + approx2
approx2 = approx2 * count/(count + 2)
#print approx2
final = math.pi - error
return final, count
The issue is that the program is returning a negative value. The error should converge to zero. I need to be able to subtract my error from the accepted value of pi to get an approximate value from the series. What am I doing wrong?
Answers:
This works:
import math
def piEuler(x):
halfpi = math.pi / 2.0
count = 0
approx = 1.0
divisor = 1
numerator = 1
while True:
count += 1
numerator *= count
divisor *= 2*count + 1
approx += float(numerator) / float(divisor)
error = halfpi - approx
if error < x:
return (math.pi - error), count
The basic differences are:
- By switching the terminating condition of the loop to a test/break, I can remove the manual calculation of the second term of the series
- Careful use of int and float datatypes (this may have been your problem)
- Better naming of the variables leads to easier debugging
You should try to rewrite the routine such that the smallest term in the sequence, approx2 in your code, has to be greater than error.
Also you have ‘math.pi’ in your last error calculation. Does it have to be math.pi/2 ?
It seems the nature of the error is oscillating also!
It looks like you were incorrectly implementing the algorithm. Rather than doing pi/2 = 1 + 1/3 + (1*2)/(3*5) + (1*2*3)/(3*5*7) + ...
, it looks like your code is doing pi/2 = 1 + 1/3 + (1*2)/(3*4) + (1*2*3)/(3*4*5) + ...
.
Since the denominators will end up being smaller, you’ll be increasing the sum by a greater amount, resulting no doubt in an overshoot and consequently a negative error.
The problem lies in this line:
approx2 = approx2 * count/(count + 2)
As you can see, when count
is even, count + 2
will be even. An easy fix would be to change this to:
approx2 = approx2 * count/(2 * count + 1)
Here is a example algorithm that works, but uses the relative error between terms rather than the absolute error (wouldn’t want to give everything away 😉 ):
from __future__ import division
def half_pi(max_err=10**-6, max_iter=10000):
partial_sum = cur_term = 1
n = 1
while abs(t) > max_err and n < max_iter:
cur_term = cur_term * (n / (2 * n + 1))
partial_sum += cur_term
n += 1
return partial_sum, n
Why not use Python’s generator expression to implement Leibniz formula for π (one liner :)) :
4*sum(pow(-1, k)/(2*k + 1) for k in range (10000))
pi/2 = 1 + 1/3 + (1*2) / (3*5) + (1*2*3) / (3*5*7) + ...
Okay, let’s try this again.
I need to write a function that takes the max error as a parameter for the value of pi and returns the calculated value of pi and the number of iterations necessary to get to that point. I can’t use a recursive algorithm.
Thus far I have:
def piEuler (x):
count = 0.0
approx = 0.0
approx2 = 1.0
error = math.pi/2 - approx2
while error > x:
count = count + 1
approx3 = approx + approx2
error = math.pi/2 - approx3
#print error
approx = approx + approx2
approx2 = approx2 * count/(count + 2)
#print approx2
final = math.pi - error
return final, count
The issue is that the program is returning a negative value. The error should converge to zero. I need to be able to subtract my error from the accepted value of pi to get an approximate value from the series. What am I doing wrong?
This works:
import math
def piEuler(x):
halfpi = math.pi / 2.0
count = 0
approx = 1.0
divisor = 1
numerator = 1
while True:
count += 1
numerator *= count
divisor *= 2*count + 1
approx += float(numerator) / float(divisor)
error = halfpi - approx
if error < x:
return (math.pi - error), count
The basic differences are:
- By switching the terminating condition of the loop to a test/break, I can remove the manual calculation of the second term of the series
- Careful use of int and float datatypes (this may have been your problem)
- Better naming of the variables leads to easier debugging
You should try to rewrite the routine such that the smallest term in the sequence, approx2 in your code, has to be greater than error.
Also you have ‘math.pi’ in your last error calculation. Does it have to be math.pi/2 ?
It seems the nature of the error is oscillating also!
It looks like you were incorrectly implementing the algorithm. Rather than doing pi/2 = 1 + 1/3 + (1*2)/(3*5) + (1*2*3)/(3*5*7) + ...
, it looks like your code is doing pi/2 = 1 + 1/3 + (1*2)/(3*4) + (1*2*3)/(3*4*5) + ...
.
Since the denominators will end up being smaller, you’ll be increasing the sum by a greater amount, resulting no doubt in an overshoot and consequently a negative error.
The problem lies in this line:
approx2 = approx2 * count/(count + 2)
As you can see, when count
is even, count + 2
will be even. An easy fix would be to change this to:
approx2 = approx2 * count/(2 * count + 1)
Here is a example algorithm that works, but uses the relative error between terms rather than the absolute error (wouldn’t want to give everything away 😉 ):
from __future__ import division
def half_pi(max_err=10**-6, max_iter=10000):
partial_sum = cur_term = 1
n = 1
while abs(t) > max_err and n < max_iter:
cur_term = cur_term * (n / (2 * n + 1))
partial_sum += cur_term
n += 1
return partial_sum, n
Why not use Python’s generator expression to implement Leibniz formula for π (one liner :)) :
4*sum(pow(-1, k)/(2*k + 1) for k in range (10000))