Approximating pi within error - python

To start off, this is the problem.
The mathematical constant π (pi) is an irrational number with value approximately 3.1415928... The precise value of π is equal to the following infinite sum: π = 4/1 - 4/3 + 4/5 - 4/7 + 4/9 - 4/11 + ... We can get a good approximation of π by computing the sum of the first few terms. Write a function approxPi() that takes as a parameter a floating point value error and approximates the constant π within error by computing the above sum, term by term, until the absolute value of the difference between the current sum and the previous sum (with one fewer terms) is no greater than error. Once the function finds that the difference is less than error, it should return the new sum. Please note that this function should not use any functions or constants from the math module. You are supposed to use the described algorithm to approximate π, not use the built-in value in Python.
I'd really appreciate it if someone could help me understand what the problem is asking, since I've read it so many times but still can't fully understand what it's saying. I looked through my textbook and found a similar problem for approximating e using e's infinite sum: 1/0! + 1/1! + 1/2! + 1/3!+...
def approxE(error):
import math
'returns approximation of e within error'
prev = 1 # approximation 0
current = 2 # approximation 1
i = 2 # index of next approximation
while current-prev > error:
#while difference between current and previous
#approximation is too large
#current approximation
prev = current #becomes previous
#compute new approximation
current = prev + 1/math.factorial(i) # based on index i
i += 1 #index of next approximation
return current
I tried to model my program after this, but I don't feel I'm getting any closer to the solution.
def approxPi(error):
'float ==> float, returns approximation of pi within error'
#π = 4/1 - 4/3 + 4/5 - 4/7 + 4/9 - 4/11 + ...
prev = 4 # 4/1 = 4 : approx 0
current = 2.6666 # 4/1 - 4/3 = 2.6666 : approx 1
i = 5 # index of next approx is 5
while current-prev > error:
prev = current
current = prev +- 1/i
i = i +- 2
return current
The successful program should return
approxPi(0.5) = 3.3396825396825403 and approxPi(0.05) = 3.1659792728432157
Again, any help would be appreciated. I'd like to just understand what I'm doing wrong in this.

If you're trying to approximate pi using that series, start by writing out a few terms:
π = 4/1 - 4/3 + 4/5 - 4/7 + 4/9 - 4/11 + ...
0 1 2 3 4 5 ...
And then write a function that returns the nth term of the series:
def nth_term(n):
return 4 / (2.0 * n + 1) * (-1) ** n
From there, the code is pretty generic:
def approximate_pi(error):
prev = nth_term(0) # First term
current = nth_term(0) + nth_term(1) # First + second terms
n = 2 # Starts at third term
while abs(prev - current) > error:
prev = current
current += nth_term(n)
n += 1
return current
It seems to work for me:
>>> approximate_pi(0.000001)
3.1415929035895926

There are several issues:
A) i = i +- 2 does not do what you think, not sure what it is.
The correct code should be something like (there are a lot of ways):
if i < 0:
i = -(i-2)
else:
i = -(i+2)
The same is for:
current = prev +- 1/i
It should be:
current = prev + 4.0/i
Or something, depending on what exactly is stored in i. Beware! In python2, unless you import the new division from the future you have to type the 4.0, not just 4.
Personally I would prefer to have to variables, the absolute value of the divisor and the sign, so that for each iteration:
current = current + sign * 4 / d
d += 2
sign *= -1
That's a lot nicer!
B) The ending of the loop should check the absolute value of the error:
Something like:
while abs(current-prev) > error:
Because the current value jumps over the target value, one value bigger, one smaller, so one error is positive, one is negative.

Here's how I'd do it:
def approxPi(error):
# pi = 4/1 - 4/3 + 4/5 - 4/7 + 4/9 - 4/11 + ...
value = 0.0
term = 1.0e6
i = 1
sign = 1
while fabs(term) > error:
term = sign/i
value += term
sign *= -1
i += 2
return 4.0*value
print approxPi(1.0e-5)

Related

Infinite sum with given precision

I've been trying to solve this infinite sum with a given precision problem.
You can see the description in the picture below
Here's what I tried so far:
import math
def infin_sum(x, eps):
sum = float(0)
prev = ((-1)*(x**2))/2
i = 2
while True:
current = prev + ((-1)**i) * (x**(2*i)) / math.factorial(2*i)
if(abs(current - prev) <= eps):
print(current)
return current
prev = current
i+=1
For the given sample input (0.2 for x and 0.00001 precision) my sum is 6.65777777777778e-05 and according to their tests it doesn't come close enough to the correct answer
You should use math.isclose() instead of abs() to check your convergence (given that it's how the result will be checked). given that each iteration adds or subtract a specific term, the delta between previous and next (Si-1 vs Si) will be equal to the last term added (so you don't need to track a previous value).
That infinite series is almost the one for cosine (it would be if i started at zero) so you can test your result against math.cos(x)-1. Also, I find it strange that the check for expected result is fixed within a precision 0.0001 but the sample input specifies a precision of 0.00001 (I guess more precise will be within 0.0001 but then, the validation is not really checking that the output is correct given the input?)
from math import isclose
def cosMinus1(x,precision=0.00001):
result = 0
numerator = 1
denominator = 1
even = 0
while not isclose(numerator/denominator,0,abs_tol=precision): # reach precision
numerator *= -x*x # +/- for even powers of x
even += 2
denominator *= even * (even-1) # factorial of even numbers
result += numerator / denominator # sum of terms
return result
print(cosMinus1(0.2))
# -0.019933422222222226
import math
expected = math.cos(0.2)-1
print(expected, math.isclose(expected,cosMinus1(0.2),abs_tol=0.0001))
# -0.019933422158758374 True
Since it's not a good idea to shadow sum, you had the right idea in calling it current, but you didn't initialise current to float(0), and forgot to sum it. This is your code with those problems fixed:
def infin_sum(x, eps):
current = float(0)
prev = ((-1) * (x ** 2)) / 2
i = 2
while True:
current = current + (((-1) ** i) * (x ** (2 * i))) / math.factorial(2 * i)
if (abs(current - prev) <= eps):
print(current)
return current
prev = current
i += 1
As a more general comment, printing inside a function like this is probably not the best idea - it makes the reusability of the function limited - you'd want to capture the return value and print it outside the function, ideally.
First, you are not summing the elements.
Recomputing everything from scratch is very wasteful and precision of floats is limited.
You can user Horner's method to refine the sum:
import math
def infin_sum(x, eps):
total = float(0)
e = 1
total = 0
i = 1
while abs(e) >= eps:
diff = (-1) * (x ** 2) / (2 * i) / (2 * i - 1)
e *= diff
total += e
i += 1
return total
if __name__ == "__main__":
x = infin_sum(0.2, 0.00001)
print(x)
Dont forget to add the result and not replace it:
prev += current
instead of
prev = current

Calculating the sum of a series?

This is my assignment and for the life of me i cant seem to think of a way to do it. This is the code I have so far:
sum = 0
k = 1
while k <= 0.0001:
if k % 2 == 1:
sum = sum + 1.0/k
else:
sum = sum - 1.0/k
k = k + 1
print()
This is my assignment :
Create a python program named sumseries.py that does the following:
Put comments at the top of your program with your name, date, and
description of what the program does.
Write a program to calculate and display the sum of the series:
1 - 1/2 + 1/3 - 1/4 + ...
until a term is reached that is less than 0.0001.
The answer with 10,000 iterations appears to be 0.6930971830599583
I ran the program with 1,000,000,000 (billion) iterations and came up with a number of 0.6931471810606472. I need to create a loop to programmably create the series.
Actually, you could write this shorter:
Answer = sum(1.0 / k if k % 2 else -1.0 / k for k in range(1, 10001))
What this code does:
the innermost part is a generator expression, which computes the elements of a series 'on the fly'
1.0 / k if k % 2 else -1.0 / k results in 1.0 / k if k is odd and -1.0 / k otherwise (a - b is the same as a + (-b))
for k in range(1, 10001) goes through all ks in range from 1 (included) to 10001 (excluded)
sum can compute the sum of any sequence (any iterable, to be precise), be it a list, a tuple, or a generator expression
The same without generator expressions:
Answer = 0
for k in range(1, 10001):
if k % 2:
Answer += 1.0 / k
else:
Answer -= 1.0 / k
# or simply:
# Answer += 1.0 / k if k % 2 else -1.0 / k
You're almost there, all you need to do is to replace
while k <= 0.0001:
with:
while term <= 0.0001:
term is naturally 1/k
To make the teacher happy, you must follow the details of the problem, as well as the spirit of the problem. The problem clearly states to print the sum, not all the partial sums. You will anger the teacher by submitting a solution that spews 10000 lines of crap not requested.
Some have suggested pre-calculating a loop limit of 10000, but that was not the requested algorithm. Instead, one is to calculate successive terms (1, -1/2, 1/3, -1/4, ...) until reaching a term less than 0.0001.
The reason the problem was specified that way is that one ends up with a more generally useful program, applicable to a wide class of term formulas. Not a fragile one that gets the wrong answer if the term formula is changed from (-1)**(k-1)/k, to say 1/k or 1/k^2.
The teacher's wording "term less than 0.0001" is imprecise and assumed some math knowledge. They want the magnitude (absolute value) of the term to be less than 0.0001. Otherwise, iteration would stop at the second term -1/2, as someone pointed out.
So, this answer would not be complete without a pompous pedantic solution that skips ahead a chapter. ;) Note that previous some answers will not work in Python2.x without a conversion to float.
def term(k):
return (-1)**(k - 1) / float(k)
err = 0.0001
def terms():
k = 1
t = term(k)
while abs(t) >= err:
yield t
k += 1
t = term(k)
print(sum(terms()))
Here is the answer your teacher is looking for for full credit.
until < .0001 means while >= 0.0001 This modifies your code the least, so makes it a correction of what you wrote
sum = 0
k = 1
while 1.0/k >= 0.0001:
if k % 2 == 1:
sum = sum + 1.0/k
else:
sum = sum - 1.0/k
k = k + 1
print(sum)
Absolutly simplest way would be the following
sum((-1)**(k) / k for k in range(1, 10001))

calculation of pi, getting different value than book

The mathematical constant π (pi) is an irrational number with value approximately 3.1415928... The precise value of π is equal to the following infinite sum: π = 4/1 - 4/3 + 4/5 - 4/7 + 4/9 - 4/11 + ... We can get a good approximation of π by computing the sum of the first few terms. Write a function approxPi() that takes as a parameter a floating point value error and approximates the constant π within error by computing the above sum, term by term, until the absolute value of the difference between the current sum and the previous sum (with one fewer terms) is no greater than error. Once the function finds that the difference is less than error, it should return the new sum. Please note that this function should not use any functions or constants from the math module. You are supposed to use the described algorithm to approximate π, not use the built-in value in Python.
I have done the below program but for some reason I am getting the different value from the one in book.
def pi(error):
prev = 1
current = 4
i = 1
while abs(current - prev) > error:
d = 2.0* i +1
sign = (-1)**i
prev = current
current = current + sign * 4 / d
i = i +1
return current
output In [2]: pi(0.01)
Out[2]: 3.146567747182955
But instead I need to get this value
>>> approxPi(0.01)
3.1611986129870506
>>> approxPi(0.0000001)
3.1415928535897395
The approximation you're using is very poor at converging, that is you have to loop quite a lot of times to get a reasonable value. You see that the difference will be 1/d and that's the accuracy. You'll have to loop 5000 times to get four digits 50k times to get next and 500k to get next and so on (that is exponential time complexity for the digits).
This could be one of the reasons that you see a discrepancy here, that you simply get the situation where rounding errors add up. Since you need to use that many iterations you will never get near the full precision of the floats you're using. Another source of discrepancy is that your reference probably is using another exit condition, with your condition you should get an error less than the provided (ideally), and you've got it (3.146567747182955-pi < 0.01). It actually looks like your reference is using the condition abs(current-prev) > 4*error instead.
The formula you're using is that pi=4arctan(1) and using McLaurin expansion of arctan(x) for a value of x that is on the limit of converging at all. To get better performance one should use lower x in that expansion. For example pi=16arctan(1/5)-4arctan(1/239) could be used (this gives linear time complexity for the digits):
def pi(error):
a = 1.0/5
b = 1.0/239
prev = 1
current = 0.0
i = 0
while abs(current - prev) > error:
d = 2.0* i +1
sign = (-1)**i
prev = current
current = current + sign * (16*a - 4*b)/d
a = a*1.0/(5*5)
b = b*1.0/(239*239)
i = i +1
return current
I guess the stopping rule for your function and approxPi are different. In fact, your estimation is better. If you print out all the values of current, you will see that when i equals 50 your function produces your desired output. But, it goes beyond that and produce a better approximation.
So to get the answer you are looking for then the question as posed is wrong in how it describes the exit condition.
Re-organizing you get Pi = 4*(1/1 + 1/3 + 1/5 + ...), to get 3.1611986129870506 with an error of 0.01, then you looking at the subsequent terms and stopping when the term < error:
from itertools import count, cycle #, izip for Py2
def approxPi(error):
p = 0
for sign, d in zip(cycle([1,-1]), count(1, 2)): # izip for Py2
n = sign / d
p += n
if abs(n) < error:
break
return 4*p
Then you get the correct answers:
>>> approxPi(0.01)
3.1611986129870506
>>> approxPi(0.0000001)
3.1415928535897395
Using your code (from #PaulBoddington: Find the value of pi)
def pi(error):
prev = 0
current = 1
i = 1
while abs(current - prev) > error:
d = 2.0*i + 1
sign = (-1)**i
prev = current
current = current + sign / d
i += 1
return 4*current
Note: This is not the difference between the current sum and the previous sum, so the question is wrong, but it is equal to difference_between_sums < error*4. So to get the correct exit for your original code just multiply the error by 4, e.g.:
>>> pi(0.04)
3.1611986129870506

Trapezoid Rule in Python

I am trying to write a program using Python v. 2.7.5 that will compute the area under the curve y=sin(x) between x = 0 and x = pi. Perform this calculation varying the n divisions of the range of x between 1 and 10 inclusive and print the approximate value, the true value, and the percent error (in other words, increase the accuracy by increasing the number of trapezoids). Print all the values to three decimal places.
I am not sure what the code should look like. I was told that I should only have about 12 lines of code for these calculations to be done.
I am using Wing IDE.
This is what I have so far
# base_n = (b-a)/n
# h1 = a + ((n-1)/n)(b-a)
# h2 = a + (n/n)(b-a)
# Trap Area = (1/2)*base*(h1+h2)
# a = 0, b = pi
from math import pi, sin
def TrapArea(n):
for i in range(1, n):
deltax = (pi-0)/n
sum += (1.0/2.0)(((pi-0)/n)(sin((i-1)/n(pi-0))) + sin((i/n)(pi-0)))*deltax
return sum
for i in range(1, 11):
print TrapArea(i)
I am not sure if I am on the right track. I am getting an error that says "local variable 'sum' referenced before assignment. Any suggestions on how to improve my code?
Your original problem and problem with Shashank Gupta's answer was /n does integer division. You need to convert n to float first:
from math import pi, sin
def TrapArea(n):
sum = 0
for i in range(1, n):
deltax = (pi-0)/n
sum += (1.0/2.0)*(((pi-0)/float(n))*(sin((i-1)/float(n)*(pi-0))) + sin((i/float(n))*(pi-0)))*deltax
return sum
for i in range(1, 11):
print TrapArea(i)
Output:
0
0.785398163397
1.38175124526
1.47457409274
1.45836902046
1.42009115659
1.38070223089
1.34524797198
1.31450259385
1.28808354
Note that you can heavily simplify the sum += ... part.
First change all (pi-0) to pi:
sum += (1.0/2.0)*((pi/float(n))*(sin((i-1)/float(n)*pi)) + sin((i/float(n))*pi))*deltax
Then do pi/n wherever possible, which avoids needing to call float as pi is already a float:
sum += (1.0/2.0)*(pi/n * (sin((i-1) * pi/n)) + sin(i * pi/n))*deltax
Then change the (1.0/2.0) to 0.5 and remove some brackets:
sum += 0.5 * (pi/n * sin((i-1) * pi/n) + sin(i * pi/n)) * deltax
Much nicer, eh?
You have some indentation issues with your code but that could just be because of copy paste. Anyways adding a line sum = 0 at the beginning of your TrapArea function should solve your current error. But as #Blender pointed out in the comments, you have another issue, which is the lack of a multiplication operator (*) after your floating point division expression (1.0/2.0).
Remember that in Python expressions are not always evaluated as you would expect mathematically. Thus (a op b)(c) will not automatically multiply the result of a op b by c like you would expect with a mathematical expression. Instead this is the function call notation in Python.
Also remember that you must initialize all variables before using their values for assignment. Python has no default value for unnamed variables so when you reference the value of sum with sum += expr which is equivalent to sum = sum + expr you are trying to reference a name (sum) that is not binded to any object at all.
The following revision to your function should do the trick. Notice how I place multiplication operators (*) between every expression that you intend to multiply.
def TrapArea(n):
sum = 0
for i in range(1, n):
i = float(i)
deltax = (pi-0)/n
sum += (1.0/2.0)*(((pi-0)/n)*(sin((i-1)/n*(pi-0))) + sin((i/n)*(pi-0)))*deltax
return sum
EDIT: I also dealt with the float division issue by converting i to float(i) within every iteration of the loop. In Python 2.x, if you divide one integer type object with another integer type object, the expression evaluates to an integer regardless of the actual value.
A "nicer" way to do the trapezoid rule with equally-spaced points...
Let dx = pi/n be the width of the interval. Also, let f(i) be sin(i*dx) to shorten some expressions below. Then interval i (in range(1,n)) contributes:
dA = 0.5*dx*( f(i) + f(i-1) )
...to the sum (which is an area, so I'm using dA for "delta area"). Factoring out the 0.5*dx, makes the whole some look like:
A = 0.5*dx * ( (f(0) + f(1)) + (f(1) + f(2)) + .... + (f(n-1) + f(n)) )
Notice that there are two f(1) terms, two f(2) terms, on up to two f(n-1) terms. Combine those to get:
A = 0.5*dx * ( f(0) + 2*f(1) + 2*f(2) + ... + 2*f(n-1) + f(n) )
The 0.5 and 2 factors cancel except in the first and last terms:
A = 0.5*dx(f(0) + f(n)) + dx*(f(1) + f(2) + ... + f(n-1))
Finally, you can factor dx out entirely to do just one multiplication at the end. Converting back to sin() calls, then:
def TrapArea(n):
dx = pi/n
asum = 0.5*(sin(0) + sin(pi)) # this is 0 for this problem, but not others
for i in range(1, n-1):
asum += sin(i*dx)
return sum*dx
That changed "sum" to "asum", or maybe "area" would be better. That's mostly because sum() is a built-in function, which I'll use below the line.
Extra credit: The loop part of the sum can be done in one step with a generator expression and the sum builtin function:
def TrapArea2(n):
dx = pi/n
asum = 0.5*(sin(0) + sin(pi))
asum += sum(sin(i*dx) for i in range(1,n-1))
return asum*dx
Testing both of those:
>>> for n in [1, 10, 100, 1000, 10000]:
print n, TrapArea(n), TrapArea2(n)
1 1.92367069372e-16 1.92367069372e-16
10 1.88644298557 1.88644298557
100 1.99884870579 1.99884870579
1000 1.99998848548 1.99998848548
10000 1.99999988485 1.99999988485
That first line is a "numerical zero", since math.sin(math.pi) evaluates to about 1.2e-16 instead of exactly zero. Draw the single interval from 0 to pi and the endpoints are indeed both 0 (or nearly so.)

Finding an approximation of the constant Pi within error [ PYTHON ]

I just started learning Python and I am having a problem writing the function.
The following is an infinite series that calculates an approximation of π :
π = 4/1 − 4/3 + 4/5 - 4/7 + 4/9 - 4/11 ...
I am trying to write a function that takes as a parameter a floating point value error and approximates the constant π within error by computing the above sum, term by term, until the absolute value of the difference between the current sum and the previous sum (with one fewer terms) is no greater than error. Once the function finds that the difference is less than error, it should return the new sum.
The following shows the execution of this function on some examples:
>>> aprPi(0.01)
3.1465677471829556
>>> aprPi(0.0000001)
3.1415927035898146
I still don't know how to compute it. Can someone help me?
This is what I have so far:
def aprPi(err):
first = 4/test(0) - 4/test(1)
second = first + 4/test(2) - 4/test(3)
n=4
while abs(first - second) > err:
first = second
second = second + test(n)
n +=1
return second
def test(n):
sum = 1
for i in range(n):
sum += 2
return sum
Thank you
You can do something like this:
mypie = 0
denominator = 1
sign = 1
while denominator < 100:
mypie = mypie + (4.0 / denominator) * sign
sign = -sign
denominator = denominator + 2

Categories

Resources