Finding an approximation of the constant Pi within error [ PYTHON ] - 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

Related

How do I make an infinite series using an equation?

I'm trying to make a function called cos_series that uses values x and nterms that gives me the sum of a series, using this equation 1 - x^2/2! + x^4/4! - x^6/6! +...
This is my code so far,
def cos_series(x,nterms):
lst = []
lst2 = []
for i in range(nterms):
lst+=[x**(2*i)/(math.factorial(i*2))]
for i in range(nterms):
lst2+=[(x**(2*i)/(math.factorial(i*2)))*-1]
return sum(lst2[1::2] + lst[::2])
cos_series(math.pi/3,3)
The return value should equal 0.501796 but I'm having trouble reaching it, can anyone help?
Your code seems to work just fine.
Your logic works with just:
def cos_series(x, n):
return sum((-1 if (i % 2) else 1) * x**(i*2) / math.factorial(i*2) for i in range(n))
Generating the sum of the series in one go and avoiding the computation of values you don't use.
(note that, after you changed your question, your code in fact returns 0.501796201500181 - which is the value you expected; there's no issue?)
You don't need to use math.factorial() and you don't need to store the terms in a list. Just build the numerator and denominator as you go and add up them up.
By producing the numerator and denominator iteratively, your logic will be much easier to manage and debug:
def cos(x,nTerms=10):
result = 0
numerator = 1
denominator = 1
for even in range(2,nTerms*2+1,2): # nTerms even numbers
result += numerator / denominator # sum of terms
numerator *= -x*x # +/- for even powers of x
denominator *= even * (even-1) # factorial of even numbers
return result
print(cos(3.141592653589793/3,3)) # 0.501796201500181

Calculate how many numbers were summed from 1 to n

I have a math question.
Having a sum of number from 1 to n. For example it may be:
sum([i for i in range(46)])
So the sum of it equals 1035.
Now my question is, knowing just sum - 1035 and that we start from 1. I want to calculate n.
How can I do that
The sum of the first n integers is n*(n+1)/2
So using the quadratic formula (and skipping the possibility of no real roots)
import math
s = 1035
n = (-1 + math.sqrt(1 + 8*s))/2 # the other root is negative
print(n)
45.0
which in python3 division is a float.
Note too -- although you probably knew this -- that range(46) sums 1 + 2 + .... + 45. The upper end of range is excluded.
The formula to calculate sum of upto n numbers is n*(n+1)/2.
checkout https://brilliant.org/wiki/sum-of-n-n2-or-n3/
You could try something like :
import math
def find_n_sum(s):
d=1+8*s
if (-1+math.sqrt(d))%2 == 0:
return (-1+math.sqrt(d))/2
else:
return "Text to return if it doesn't exist"
The summation will actually start from 0 and it will go to 45. You can test this by printing out the values:
for i in range(46):
print(i)
To reverse the summation you will want to use the formula for sum of first n numbers, n*(n+1)/2 and equate this to the sum, 1035.
solving the equation you will get the quadratic n^2 + n - 2070 = 0
now you will need to get the roots. Only the positive root will matter in this case, we will use the quadratic formula to get the roots:
D = 1^2 - 41(-2070);
x = (-1 + sqrt(D) / 2)
(math.sqrt(sum(i for i in range(46))*8 + 1) - 1) / 2
this will give you the last value that was added, 45. Now you can just add 1 to this to get 46.

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.)

Approximating pi within error

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)

Categories

Resources