Programming a math function in python - python

This is what I have to program:
sen(x) = (x/1!) - (x^3/3!) + (x^5/5!) - (x^7/7!) + ...
So far I have this:
def seno(x, n):
for i in range(1, n+1, 2):
result = (x**i/math.factorial(i))
result1 = (x**i/math.factorial(i))
result2 = (x**i/math.factorial(i))
result3 = (x**i/math.factorial(i))
return math.sin(result-result1 + result2 - result3)
The thing I can’t understand is how to actually change the i value for each result.
Another thing is I can’t use any non-built-in function. So no imports except for the math.
EDIT: Thank you for the quick reply.

It looks like you're doing a Taylor Series approximation of Sine.
You probably shouldn't declare separate result1, result2, etc. Instead, you compute each value in a loop, and accumulate it in a single result variable.
def seno(x,n):
result = 0
sign = 1 # Sign starts out positive
for i in range(1, n+1, 2):
result += x**i/math.factorial(i)
sign *= -1 # use negative sign on odd terms
return result
Note that you don't actually call math.sin on the result. The whole point of using a Taylor Series approximation is to estimate the value of math.sin(x) without actually calling that function.
You can optimize this loop a bit more. You can do a strength reduction on math.factorial by accumulating the answer rather than recomputing the whole factorial value on each iteration. You can also do a similar strength reduction on the x**i term, and roll the sign-switching logic into the update logic for exp as well.
def seno(x,n):
result = 0.0
fact = 1.0 # start with '1!'
exp = x # start with 'x¹'
xx = x*x # xx = x²
for i in range(1, n+1, 2):
result += exp / fact
exp *= -xx # update exponential term to 'xⁱ', and swap sign
fact *= (i+1) * (i+2) # update factorial term to '(i+2)!'
return result

You are using the for loop incorrectly. Each iteration will compute one term of the series; you need to accumulate those values rather than trying to set 4 results at a time.
def seno(x, n):
sign = 1
result = 0
for i in range(1, n+1, 2):
term = x**i/math.factorial(i)
result += sign * term
sign *= -1 # Alternate the sign of the term
return result

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

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

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

Taking square of summed numbers

This code adds all natural numbers up to 10, then takes the square of that sum in Python. Where did I go wrong?
def square_of_sum():
sum = 0
for x in xrange(11):
if x <= 10:
x + sum = sum
x += 1
else:
print sum*sum
break
Ah, I see you like Project Euler :)
Solution
I think this is what you meant by your code:
def square_of_sum():
sum_ = 0
for x in xrange(1, 11):
sum_ += x
return sum_ ** 2
To rewrite this more idiomatically, use generator comprehensions and built-ins:
def square_of_sum():
return sum(range(11)) ** 2
If your performance conscious, you can eliminate the loop by noticing that your finding the sum of an arithmetic series:
def square_of_sum(x):
print (x * (x + 1) / 2) ** 2
Fixes
As to why your code isn't working, it's b'coz of many reasons.
First of all, I think you're confused about how the for loop in Python works. Basically, it just loops over an array. You didn't have to check and break when x became greater than 10, nor increment it. Read up on the Python docs on how to use the for loop. To see an example of when to use it, see the wiki page.
Secondly, variable assignments are done with the variable on the left and the expression to be evaluated on the right. So x + sum = sum should really have been sum = sum + x or sum += x for brevity.
Thirdly, sum is a built-in function. You probably didn't want to nor shouldn't over-shadow it, so rename your sum variable to something else.
And last, sum*sum is equivalent to just raising it to the power of 2 and you can do that using the ** operator as so: sum ** 2.
Hope this helped you understand.
To fix the errors in your code:
def square_of_sum():
s = 0
for x in xrange(11):
s += x
print s**2
or, more idiomatically,
def square_of_sum(n):
print sum(range(n + 1)) ** 2
or, to eliminate the loop:
def square_of_sum(n):
print (n * (n + 1) / 2) ** 2
A couple of problems. First of all, sum is a builtin function, so you probably don't want to name anything that, so use a variable called something like total instead.
Second, variables assignment is done with the variable on the left and the expression on the right, so x + total = total should be total = x + total, or total += x for brevity.
Third, since the case when x == 11 is basically just a return case, it should be outside the loop.
And, finally, total * total is equivalent to total ** 2; this is easier to use for things like
def square_of_sum():
total = 0
for x in xrange(11):
if x <= 10:
total += x
x += 1
print total ** 2
But, if I were you, I'd just use
sum(range(11))**2

floating points precision in complicated calculations

For calculating Catalan Numbers, I wrote two codes. One (def "Catalan") works recursively and returns the right Catalan Numbers.
dicatalan = {}
def catalan(n):
if n == 0:
return 1
else:
res = 0
if n not in dicatalan:
for i in range(n):
res += catalan(i) * catalan(n - i - 1)
dicatalan[n] = res
return dicatalan[n]
the other (def "catalanFormula") applies the implicit formula, but doesn't calculate accurately starting from n=30. the problem derives from floating points - for k=9 the program returns "6835971.999999999" instead of "6835972" and from this moment on accumulates mistakes till the final wrong answer.
(print line is for checking)
def catalanFormula(n):
result = 1
for k in range(2, n + 1):
result *= ((n + k) / k)
print (result)
return int(result)
I tried rounding and failed, tried Decimal import and still got nothing right.
I need the "catalanFormula" work perfectly as "catalan";
Any Ideas?
Thanks!
Try calculating the numerator and denominator separately and dividing them at the end. If you do this, you should be able to make it a little bit farther with floating-point.
I'm sure Python has a package for rational numbers. Using rationals is an even better idea.
See the bigfloat package.
from bigfloat import *
setcontext(quadruple_precision)
def catalanFormula(n):
result = BigFloat(1)
for k in range(2, n + 1):
result *= ((BigFloat(n) + BigFloat(k)) / BigFloat(k))
return result
catalanFormula(30)
Output:
BigFloat.exact('3814986502092304.00000000000000000043', precision=113)

Categories

Resources