Simpson's rule in Python - python

For a numerical methods class, I need to write a program to evaluate a definite integral with Simpson's composite rule. I already got this far (see below), but my answer is not correct. I am testing the program with f(x)=x, integrated over 0 to 1, for which the outcome should be 0.5. I get 0.78746... etc.
I know there is a Simpson's rule available in Scipy, but I really need to write it myself.
I suspect there is something wrong with the two loops. I tried "for i in range(1, n, 2)" and "for i in range(2, n-1, 2)" before, and this gave me a result of 0.41668333... etc.
I also tried "x += h" and I tried "x += i*h". The first gave me 0.3954, and the second option 7.9218.
# Write a program to evaluate a definite integral using Simpson's rule with
# n subdivisions
from math import *
from pylab import *
def simpson(f, a, b, n):
h=(b-a)/n
k=0.0
x=a
for i in range(1,n/2):
x += 2*h
k += 4*f(x)
for i in range(2,(n/2)-1):
x += 2*h
k += 2*f(x)
return (h/3)*(f(a)+f(b)+k)
def function(x): return x
print simpson(function, 0.0, 1.0, 100)

You probably forget to initialize x before the second loop, also, starting conditions and number of iterations are off. Here is the correct way:
def simpson(f, a, b, n):
h=(b-a)/n
k=0.0
x=a + h
for i in range(1,n/2 + 1):
k += 4*f(x)
x += 2*h
x = a + 2*h
for i in range(1,n/2):
k += 2*f(x)
x += 2*h
return (h/3)*(f(a)+f(b)+k)
Your mistakes are connected with the notion of a loop invariant. Not to get into details too much, it's generally easier to understand and debug cycles which advance at the end of a cycle, not at the beginning, here I moved the x += 2 * h line to the end, which made it easy to verify where the summation starts. In your implementation it would be necessary to assign a weird x = a - h for the first loop only to add 2 * h to it as the first line in the loop.

All you need to do to make this code work is add a variable for a and b in the function bounds() and add a function in f(x) that uses the variable x. You could also implement the function and bounds directly into the simpsonsRule function if desired... Also, these are functions to be implimented into a program, not a program itself.
def simpsonsRule(n):
"""
simpsonsRule: (int) -> float
Parameters:
n: integer representing the number of segments being used to
approximate the integral
Pre conditions:
Function bounds() declared that returns lower and upper bounds of integral.
Function f(x) declared that returns the evaluated equation at point x.
Parameters passed.
Post conditions:
Returns float equal to the approximate integral of f(x) from a to b
using Simpson's rule.
Description:
Returns the approximation of an integral. Works as of python 3.3.2
REQUIRES NO MODULES to be imported, especially not non standard ones.
-Code by TechnicalFox
"""
a,b = bounds()
sum = float()
sum += f(a) #evaluating first point
sum += f(b) #evaluating last point
width=(b-a)/(2*n) #width of segments
oddSum = float()
evenSum = float()
for i in range(1,n): #evaluating all odd values of n (not first and last)
oddSum += f(2*width*i+a)
sum += oddSum * 2
for i in range(1,n+1): #evaluating all even values of n (not first and last)
evenSum += f(width*(-1+2*i)+a)
sum += evenSum * 4
return sum * width/3
def bounds():
"""
Description:
Function that returns both the upper and lower bounds of an integral.
"""
a = #>>>INTEGER REPRESENTING LOWER BOUND OF INTEGRAL<<<
b = #>>>INTEGER REPRESENTING UPPER BOUND OF INTEGRAL<<<
return a,b
def f(x):
"""
Description:
Function that takes an x value and returns the equation being evaluated,
with said x value.
"""
return #>>>EQUATION USING VARIABLE X<<<

You can use this program for calculating definite integrals by using Simpson's 1/3 rule. You can increase your accuracy by increasing the value of the variable panels.
import numpy as np
def integration(integrand,lower,upper,*args):
panels = 100000
limits = [lower, upper]
h = ( limits[1] - limits[0] ) / (2 * panels)
n = (2 * panels) + 1
x = np.linspace(limits[0],limits[1],n)
y = integrand(x,*args)
#Simpson 1/3
I = 0
start = -2
for looper in range(0,panels):
start += 2
counter = 0
for looper in range(start, start+3):
counter += 1
if (counter ==1 or counter == 3):
I += ((h/3) * y[looper])
else:
I += ((h/3) * 4 * y[looper])
return I
For example:
def f(x,a,b):
return a * np.log(x/b)
I = integration(f,3,4,2,5)
print(I)
will integrate 2ln(x/5) within the interval 3 and 4

There is my code (i think that is the most easy method). I done this in jupyter notebook.
The easiest and most accurate code for Simpson method is 1/3.
Explanation
For standard method (a=0, h=4, b=12) and f=100-(x^2)/2
We got:
n= 3.0, y0 = 100.0, y1 = 92.0, y2 = 68.0, y3 = 28.0,
So simpson method = h/3*(y0+4*y1+2*y2+y3) = 842,7 (this is not true).
Using 1/3 rule we got:
h = h/2= 4/2= 2 and then:
n= 3.0, y0 = 100.0, y1 = 98.0, y2 = 92.0, y3 = 82.0, y4 = 68.0, y5 = 50.0, y6 = 28.0,
Now we calculate the integral for each step (n=3 = 3 steps):
Integral of the first step: h/3*(y0+4*y1+y2) = 389.3333333333333
Integral of the second step: h/3*(y2+4*y3+y4) = 325.3333333333333
Integral of the third step: h/3*(y4+4*y5+y6) = 197.33333333333331
Sum all, and we get 912.0 AND THIS IS TRUE
x=0
b=12
h=4
x=float(x)
h=float(h)
b=float(b)
a=float(x)
def fun(x):
return 100-(x**2)/2
h=h/2
l=0 #just numeration
print('n=',(b-x)/(h*2))
n=int((b-a)/h+1)
y = [] #tablica/lista wszystkich y / list of all "y"
yf = []
for i in range(n):
f=fun(x)
print('y%s' %(l),'=',f)
y.append(f)
l+=1
x+=h
print(y,'\n')
n1=int(((b-a)/h)/2)
l=1
for i in range(n1):
nf=(h/3*(y[+0]+4*y[+1]+y[+2]))
y=y[2:] # with every step, we deleting 2 first "y" and we move 2 spaces to the right, i.e. y2+4*y3+y4
print('Całka dla kroku/Integral for a step',l,'=',nf)
yf.append(nf)
l+=1
print('\nWynik całki/Result of the integral =', sum(yf) )
At the beginning I added simple data entry:
d=None
while(True):
print("Enter your own data or enter the word "test" for ready data.\n")
x=input ('Enter the beginning of the interval (a): ')
if x == 'test':
x=0
h=4 #krok (Δx)
b=12 #granica np. 0>12
#w=(20*x)-(x**2) lub (1+x**3)**(1/2)
break
h=input ('Enter the size of the integration step (h): ')
if h == 'test':
x=0
h=4
b=12
break
b=input ('Enter the end of the range (b): ')
if b == 'test':
x=0
h=4
b=12
break
d=input ('Give the function pattern: ')
if d == 'test':
x=0
h=4
b=12
break
elif d != -9999.9:
break
x=float(x)
h=float(h)
b=float(b)
a=float(x)
if d == None or d == 'test':
def fun(x):
return 100-(x**2)/2 #(20*x)-(x**2)
else:
def fun(x):
w = eval(d)
return w
h=h/2
l=0 #just numeration
print('n=',(b-x)/(h*2))
n=int((b-a)/h+1)
y = [] #tablica/lista wszystkich y / list of all "y"
yf = []
for i in range(n):
f=fun(x)
print('y%s' %(l),'=',f)
y.append(f)
l+=1
x+=h
print(y,'\n')
n1=int(((b-a)/h)/2)
l=1
for i in range(n1):
nf=(h/3*(y[+0]+4*y[+1]+y[+2]))
y=y[2:]
print('Całka dla kroku/Integral for a step',l,'=',nf)
yf.append(nf)
l+=1
print('\nWynik całki/Result of the integral =', sum(yf) )

def simps(f, a, b, N): # N must be an odd integer
""" define simpson method, a and b are the lower and upper limits of
the interval, N is number of points, dx is the slice
"""
integ = 0
dx = float((b - a) / N)
for i in range(1,N-1,2):
integ += f((a+(i-1)*dx)) + 4*f((a+i*dx)) + f((a+(i+1)*dx))
integral = dx/3.0 * integ
# if number of points is even, then error araise
if (N % 2) == 0:
raise ValueError("N must be an odd integer.")
return integral
def f(x):
return x**2
integrate = simps(f,0,1,99)
print(integrate)

Example of implementing Simpson's rule for integral sinX with a = 0 and b = pi/4. And use 10 panels for the integration
def simpson(f, a, b, n):
x = np.linspace(a, b, n+1)
w = 2*np.ones(n+1); w[0] = 1.0; w[-1] = 1;
for i in range(len(w)):
if i % 2 == 1:
w[i] = 4
width = x[1] - x[0]
area = 0.333 * width * np.sum( w*f(x))
return area
f = lambda x: np.sin(x)
a = 0.0; b = np.pi/4
areaSim = simpson(f, a, b, 10)
print(areaSim)

Related

How do I go about coding a custom function (without using any library) which returns the log of a number in python [duplicate]

I need to generate the result of the log.
I know that:
Then I made my code:
def log(x, base):
log_b = 2
while x != int(round(base ** log_b)):
log_b += 0.01
print(log_b)
return int(round(log_b))
But it works very slowly. Can I use other method?
One other thing you might want to consider is using the Taylor series of the natural logarithm:
Once you've approximated the natural log using a number of terms from this series, it is easy to change base:
EDIT: Here's another useful identity:
Using this, we could write something along the lines of
def ln(x):
n = 1000.0
return n * ((x ** (1/n)) - 1)
Testing it out, we have:
print ln(math.e), math.log(math.e)
print ln(0.5), math.log(0.5)
print ln(100.0), math.log(100.0)
Output:
1.00050016671 1.0
-0.692907009547 -0.69314718056
4.6157902784 4.60517018599
This shows our value compared to the math.log value (separated by a space) and, as you can see, we're pretty accurate. You'll probably start to lose some accuracy as you get very large (e.g. ln(10000) will be about 0.4 greater than it should), but you can always increase n if you need to.
I used recursion:
def myLog(x, b):
if x < b:
return 0
return 1 + myLog(x/b, b)
You can use binary search for that.
You can get more information on binary search on Wikipedia:
Binary search;
Doubling search.
# search for log_base(x) in range [mn, mx] using binary search
def log_in_range(x, base, mn, mx):
if (mn <= mx):
med = (mn + mx) / 2.0
y = base ** med
if abs(y - x) < 0.00001: # or if math.isclose(x, y): https://docs.python.org/3/library/math.html#math.isclose
return med
elif x > y:
return log_in_range(x, base, med, mx)
elif x < y:
return log_in_range(x, base, mn, med)
return 0
# determine range using doubling search, then call log_in_range
def log(x, base):
if base <= 0 or base == 1 or x <= 0:
raise ValueError('math domain error')
elif 0 < base < 1:
return -log(x, 1/base)
elif 1 <= x and 1 < base:
mx = 1
y = base
while y < x:
y *= y
mx *= 2
return log_in_range(x, base, 0, mx)
elif 0 <= x < 1 and 1 < base:
mn = -1
y = 1/base
while y > x:
y = y ** 0.5
mn *= 2
return log_in_range(x, base, mn, 0)
import math
try :
number_and_base = input() ##input the values for number and base
##assigning those values for the variables
number = int(number_and_base.split()[0])
base = int(number_and_base.split()[1])
##exception handling
except ValueError :
print ("Invalid input...!")
##program
else:
n1 = 1 ##taking an initial value to iterate
while(number >= int(round(base**(n1),0))) : ##finding the most similer value to the number given, varying the vlaue of the power
n1 += 0.000001 ##increasing the initial value bit by bit
n2 = n1-0.0001
if abs(number-base**(n2)) < abs(base**(n1)-number) :
n = n2
else :
n = n1
print(math.floor(n)) ##output value
Comparison:-
This is how your log works:-
def your_log(x, base):
log_b = 2
while x != int(round(base ** log_b)):
log_b += 0.01
#print log_b
return int(round(log_b))
print your_log(16, 2)
# %timeit %run your_log.py
# 1000 loops, best of 3: 579 us per loop
This is my proposed improvement:-
def my_log(x, base):
count = -1
while x > 0:
x /= base
count += 1
if x == 0:
return count
print my_log(16, 2)
# %timeit %run my_log.py
# 1000 loops, best of 3: 321 us per loop
which is faster, using the %timeit magic function in iPython to time the execution for comparison.
It will be long process since it goes in a loop. Therefore,
def log(x,base):
result = ln(x)/ln(base)
return result
def ln(x):
val = x
return 99999999*(x**(1/99999999)-1)
log(8,3)
Values are nearly equal but not exact.

How to recall the same function until it become true in Python

I need to build a python code for the following equation.
As I understood this equation has only 4 iterations and until it becomes true need to recall the function again. But when recalling the function the value which use to store the number of iterations will be lost.
The code which I have write is this.
def answer(z):
for i in 5:
if i == 4:
zn = z + 1/z
else:
y = 1 / z + zn
return y
z = float(input("Enter value for Z : "))
print("The Y Value is : %.4f" %answer(z))
I need to know to build the code for this equation.
You could use the following. Your algorithm needs to update the denominator several times.
def answer(z, n=4):
z_den = z
for i in range(n):
z_den = z + 1/z_den
final_answer = 1 / z_den
return final_answer
I think you could utilize recursion for this:
def answer(z, y = 0, n = 1):
if n == 1:
y = z + 1/z
answer(z, y, n+1)
elif n > 1 and n < 4:
y = z + 1/y
answer(z, y, n+1)
else:
print(1/y)
Where
answer(2) -> 0.4137931034482759
answer(10) -> 0.09901951266867294
answer(15) -> 0.0663729751856689

TypeError in Newton-Raphson in Python

I am a newbie at Python. I am attempting the Newton-Raphson root-finding method. In line 4, I get the error "Exception has occurred: TypeError 'numpy.float64' object is not callable". Would appreciate if someone could enlighten me on what the issue is. Thanks.
import numpy as np
def newton(f, df, x, tol=1e-8, max_it=20, it_count = 0):
x -= f(x)/df(x)
it_count += 1
if it_count > max_it:
raise ValueError("Maximum number of iterations has been exceeded")
elif abs(f(x)) <= tol:
return x
else:
x = newton(f, df, x)
def f(x):
return np.tan(x) - 2*x
def df(x):
d = 0.0000001
return (f(x+d)-f(x-d))/(2*d)
print(newton(f(1.2), df(1.2), 1.2))
In the last line you are handing the function and the gradient evaluated at a certain point instead of the functions themselves. You could try to alter your last line to the following:
print(newton(f, df, 1.2))
As pointed out by Belliger, you should also hand over the iteration count in the recursive function call. Besides, you should return the value in the recursion. Here is a working version of the code:
import numpy as np
def newton(f, df, x, tol=1e-8, max_it=20, it_count = 0):
x -= f(x)/df(x)
it_count += 1
if it_count > max_it:
raise ValueError("Maximum number of iterations has been exceeded")
elif abs(f(x)) <= tol:
return x
else:
x = newton(f, df, x, it_count=it_count)
return x
def f(x):
return np.tan(x) - 2*x
def df(x):
d = 0.0000001
return (f(x+d)-f(x-d))/(2*d)
print(newton(f, df, 1.2))
Other answers have answered your question, but just another thing I've noticed, you'll need to pass the it_count when you apply your recursive call, e.g.
else:
x = newton(f, df, x, it_count=it_count)
# Defining Function
def f(x):
return x**3 - 5*x - 9
# Defining derivative of function
def g(x):
return 3*x**2 - 5
# Implementing Newton Raphson Method
def newtonRaphson(x0,e,N):
print('\n\n*** NEWTON RAPHSON METHOD IMPLEMENTATION ***')
step = 1
flag = 1
condition = True
while condition:
if g(x0) == 0.0:
print('Divide by zero error!')
break
x1 = x0 - f(x0)/g(x0)
print('Iteration-%d, x1 = %0.6f and f(x1) = %0.6f' % (step, x1, f(x1)))
x0 = x1
step = step + 1
if step > N:
flag = 0
break
condition = abs(f(x1)) > e
if flag==1:
print('\nRequired root is: %0.8f' % x1)
else:
print('\nNot Convergent.')
# Input Section
x0 = input('Enter Guess: ')
e = input('Tolerable Error: ')
N = input('Maximum Step: ')
# Converting x0 and e to float
x0 = float(x0)
e = float(e)
# Converting N to integer
N = int(N)
#Note: You can combine above three section like this
# x0 = float(input('Enter Guess: '))
# e = float(input('Tolerable Error: '))
# N = int(input('Maximum Step: '))
# Starting Newton Raphson Method
newtonRaphson(x0,e,N)
View Code & Output Here
Also check codesansar.com/numerical-methods/. This site has large collection of algorithms, pseudocodes, and programs using C, C++, Python & MATLAB for Numerical Methods.

How to make perfect power algorithm more efficient?

I have the following code:
def isPP(n):
pos = [int(i) for i in range(n+1)]
pos = pos[2:] ##to ignore the trivial n** 1 == n case
y = []
for i in pos:
for it in pos:
if i** it == n:
y.append((i,it))
#return list((i,it))
#break
if len(y) <1:
return None
else:
return list(y[0])
Which works perfectly up until ~2000, since I'm storing far too much in memory. What can I do to make it work efficiently for large numbers (say, 50000 or 100000). I tried to make it end after finding one case, but my algorithm is still far too inefficient if the number is large.
Any tips?
A number n is a perfect power if there exists a b and e for which b^e = n. For instance 216 = 6^3 = 2^3 * 3^3 is a perfect power, but 72 = 2^3 * 3^2 is not.
The trick to determining if a number is a perfect power is to know that, if the number is a perfect power, then the exponent e must be less than log2 n, because if e is greater then 2^e will be greater than n. Further, it is only necessary to test prime es, because if a number is a perfect power to a composite exponent it will also be a perfect power to the prime factors of the composite component; for instance, 2^15 = 32768 = 32^3 = 8^5 is a perfect cube root and also a perfect fifth root.
The function isPerfectPower shown below tests each prime less than log2 n by first computing the integer root using Newton's method, then powering the result to check if it is equal to n. Auxiliary function primes compute a list of prime numbers by the Sieve of Eratosthenes, iroot computes the integer kth-root by Newton's method, and ilog computes the integer logarithm to base b by binary search.
def primes(n): # sieve of eratosthenes
i, p, ps, m = 0, 3, [2], n // 2
sieve = [True] * m
while p <= n:
if sieve[i]:
ps.append(p)
for j in range((p*p-3)/2, m, p):
sieve[j] = False
i, p = i+1, p+2
return ps
def iroot(k, n): # assume n > 0
u, s, k1 = n, n+1, k-1
while u < s:
s = u
u = (k1 * u + n // u ** k1) // k
return s
def ilog(b, n): # max e where b**e <= n
lo, blo, hi, bhi = 0, 1, 1, b
while bhi < n:
lo, blo, hi, bhi = hi, bhi, hi+hi, bhi*bhi
while 1 < (hi - lo):
mid = (lo + hi) // 2
bmid = blo * pow(b, (mid - lo))
if n < bmid: hi, bhi = mid, bmid
elif bmid < n: lo, blo = mid, bmid
else: return mid
if bhi == n: return hi
return lo
def isPerfectPower(n): # x if n == x ** y, or False
for p in primes(ilog(2,n)):
x = iroot(p, n)
if pow(x, p) == n: return x
return False
There is further discussion of the perfect power predicate at my blog.
IIRC, it's far easier to iteratively check "Does it have a square root? Does it have a cube root? Does it have a fourth root? ..." You will very quickly get to the point where putative roots have to be between 1 and 2, at which point you can stop.
I think a better way would be implementing this "hack":
import math
def isPP(n):
range = math.log(n)/math.log(2)
range = (int)(range)
result = []
for i in xrange(n):
if(i<=1):
continue
exponent = (int)(math.log(n)/math.log(i))
for j in [exponent-1, exponent, exponent+1]:
if i ** j == n:
result.append([i,j])
return result
print isPP(10000)
Result:
[[10,4],[100,2]]
The hack uses the fact that:
if log(a)/log(b) = c,
then power(b,c) = a
Since this calculation can be a bit off in floating points giving really approximate results, exponent is checked to the accuracy of +/- 1.
You can make necessary adjustments for handling corner cases like n=1, etc.
a relevant improvement would be:
import math
def isPP(n):
# first have a look at the length of n in binary representation
ln = int(math.log(n)/math.log(2)) + 1
y = []
for i in range(n+1):
if (i <= 1):
continue
# calculate max power
li = int(math.log(i)/math.log(2))
mxi = ln / li + 1
for it in range(mxi):
if (it <= 1):
continue
if i ** it == n:
y.append((i,it))
# break if you only need 1
if len(y) <1:
return None
else:
return list(y[0])

Issue with program to approximate sin and cosine values

I'm trying to write a program that takes an angle in degrees, and approximates the sin and cos value based on a number of given terms that the user chooses. In case you don't know
how to find sin and cos. So, with that being said, here is my current code:
import math
def main():
print()
print("Program to approximate sin and cos.")
print("You will be asked to enter an angle and \na number of terms.")
print("Written by ME")
print()
sinx = 0
cosx = 0
x = int(input("Enter an angle (in degrees): "))
terms = int(input("Enter the number of terms to use: "))
print()
for i in range(1, terms+1):
sinx = sinx + getSin(i, x)
cosx = cosx + getCos(i, x)
print(cosx, sinx)
def getSin(i, x):
if i == 1:
return x
else:
num, denom = calcSinFact(i, x)
sin = num/denom
return sin
def getCos(i, x):
if i == 1:
return 1
else:
num, denom = calcCosFact(i, x)
cos = num/denom
return cos
def calcSinFact(i, x):
if i % 2 == 1:
sign = -1
if i % 2 == 0:
sign = +1
denom = math.factorial(i*2-1)
num = sign * (x**(i*2-1))
return num, denom
def calcCosFact(i, x):
if i % 2 == 1:
sign = -1
if i % 2 == 0:
sign = +1
denom = math.factorial(i*2)
num = sign * (x**(i*2))
return num, denom
It runs but if i use the example shown in the picture above, i get cos = -162527117141.85715 and sin = -881660636823.117. So clearly something is off. In the picture above the answers should be cos = 0.50000000433433 and sin = 0.866025445100. I'm assuming it's the way I'm adding together the values in the first loop but i could be wrong. Any help is appreciated!
There are several issues here as pointed out in Russell Borogove's comments.
Issue no 1 is that the formulas you are using
(see wikipedia) expect x to be in radians not degrees. Going once round a circle is 360 degrees or 2*pi, so you can convert from degrees to radians by multipling by pi/180, as shown below in python code to incorrectly and then correctly get the sin of 90 degrees.
>>> math.sin(90)
0.8939966636005579
>>> math.sin(90*math.pi/180)
1.0
Issue no 2 is the rest of the code. As pointed out in the comments, there are some bugs, and the best way to find them would be to use some strategic print statements. However, you could write your program with far fewer lines of code, and simpler programs tend to have fewer bugs and be easier to debug if they do have problems.
As this is an assignment, I won't do it for you, but a related example is the series for sinh(x).
(again from wikipedia)
You can produce the terms in "one shot", using a Python list comprehension. The list can be printed and summed to get the result, as in the program below
x = 90 * math.pi / 180 # 90 degrees
n = 5
terms = [x**(2*i+1)/math.factorial(2*i+1) for i in range(n)]
print terms
sinh = sum(terms)
print sinh, math.sinh(x)
The output of this program is
[1.5707963267948966, 0.6459640975062462, 0.07969262624616703, 0.004681754135318687, 0.00016044118478735975]
2.30129524587 2.30129890231
I produced the Python list comprehension code directly from the mathematical formula for the summation, which is conveniently given in "Sigma" notation on the left hand side. You can produce sin and cos in a similar way. The one missing ingredient you need is the signs at each point in the series. The mathematical formulas tell you you need (-1)n. The Python equivalent is (-1)**n, which can be slotted into the appropriate place in the list comprehension code.
First, a few notes. It's better to print \n at the end of the previous print or at the beginning of the following then empty print().
It's useful to use a debugging tool, use logging module or just use print and find the bug by comparing expected values with returned values.
Here is a code that is working for me:
import math
def main():
print()
print("Program to approximate sin and cos.")
print("You will be asked to enter an angle and \na number of terms.")
print("Written by ME")
print()
sinx = 0
cosx = 0
x = int(input("Enter an angle (in degrees): "))
terms = int(input("Enter the number of terms to use: "))
print()
x = x / 180.0 * math.pi; # added
for i in range(1, terms+1):
sinx = sinx + getSin(i, x)
cosx = cosx + getCos(i, x)
print("Cos:{0}, Sinus:{1}".format(cosx,sinx)); # changed
def getSin(i, x):
if i == 1:
return x
else:
num, denom = calcSinFact(i, x)
sin = float(num)/denom # changed
return sin
def getCos(i, x):
if i == 1:
return 1
else:
num, denom = calcCosFact(i, x)
cos = float(num)/denom # changed
return cos
def calcSinFact(i, x):
if i % 2 == 1:
sign = +1 # changed
if i % 2 == 0:
sign = -1 # changed
denom = math.factorial(i*2-1)
num = sign * (x**(i*2-1))
return num, denom
def calcCosFact(i, x):
if i % 2 == 1:
sign = +1 # changed
if i % 2 == 0:
sign = -1 # changed
denom = math.factorial(i*2-2) # changed
num = sign * (x**(i*2-2)) # changed
return num, denom
And what I have changed? (I hope I won't forget anything)
Your sign variables were wrong. Just the opposite. So I changed the signes in the conditions.
It is perhaps not necessary but I added conversion from int to float when you divided num by denom.
According to the definition of the approximation, the input x is in radians. So I added conversion from degrees to radians. x = x / 180.0 * math.pi;
Your index in calcCosFact was wrong. It was always higher by 2. (i.e. 4 instead of 2, 8 instead of 6...)
I got this result:
Enter an angle (in degrees): 180 Enter the number of terms to use: 5
Cos:-0.976022212624, Sinus:0.00692527070751
It should be correct now. I can also recommend WolphramAlpha when you need to quickly do some math.
Here is an improved version:
from math import radians
import sys
# version compatibility shim
if sys.hexversion < 0x3000000:
# Python 2.x
inp = raw_input
rng = xrange
else:
# Python 3.x
inp = input
rng = range
def type_getter(type):
def fn(prompt):
while True:
try:
return type(inp(prompt))
except ValueError:
pass
return fn
get_float = type_getter(float)
get_int = type_getter(int)
def calc_sin(theta, terms):
# term 0
num = theta
denom = 1
approx = num / denom
# following terms
for n in rng(1, terms):
num *= -theta * theta
denom *= (2*n) * (2*n + 1)
# running sum
approx += num / denom
return approx
def calc_cos(theta, terms):
# term 0
num = 1.
denom = 1
approx = num / denom
# following terms
for n in rng(1, terms):
num *= -theta * theta
denom *= (2*n - 1) * (2*n)
# running sum
approx += num / denom
return approx
def main():
print(
"\nProgram to approximate sin and cos."
"\nYou will be asked to enter an angle and"
"\na number of terms."
)
theta = get_float("Enter an angle (in degrees): ")
terms = get_int ("Number of terms to use: ")
print("sin({}) = {}".format(theta, calc_sin(radians(theta), terms)))
print("cos({}) = {}".format(theta, calc_cos(radians(theta), terms)))
if __name__=="__main__":
main()
Note that because the Maclaurin series is centered on x=0, values of theta closer to 0 will converge much faster: calc_sin(radians(-90), 5) is -1.00000354258 but calc_sin(radians(270), 5) is -0.444365928238 (about 157,000 times further from the correct value of -1.0).
One can completely avoid all power and factorial computations by using and combining the recursive definitions of factorial and integer powers. Further optimization is obtained by computing both cos and sin values at once, so that the powers are only computed once.
PI = 3.1415926535897932384;
RadInDeg=PI/180;
def getCosSin(x, n):
mxx = -x*x;
term = 1;
k = 2;
cossum = 1;
sinsum = 1;
for i in range(n):
term *= mxx
term /= k; k+=1
cossum += term
term /= k; k+=1
sinsum += term
return cossum, x*sinsum
def main():
print "\nProgram to approximate sin and cos."
print "You will be asked to enter an angle and \na number of terms."
x = int(input("Enter an angle (in degrees): "))
terms = int(input("Enter the number of terms to use: "))
print
x = x*RadInDeg;
cosx, sinx = getCosSin(x,terms)
print cosx, sinx
if __name__=="__main__":
main()

Categories

Resources