Issue with program to approximate sin and cosine values - python

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

Related

Reduce time /space complexity of simple loop

So basically if i have an iteration like this in python
Ive editted the question to include my full code
class Solution:
def myPow(self, x: float, n: int) -> float:
temp = [];
span = range(1,abs(n))
if n ==0:
return 1
if abs(n)==1:
temp.append(x)
else:
for y in span:
if y == 1:
temp = []
temp.append(x*x)
else:
temp.append(temp[-1] * x)
if(n < 0):
return 1/temp[-1]
else:
return temp[-1]
The problem link is : Pow(x,n)-leetcode
How can I modify this to conserve memory and time. Is there another data structure i can use. Im just learning python....
------------EDIT------------
ive modified the code to use a variable instead of a list for the temp data
class Solution:
def myPow(self, x: float, n: int) -> float:
span = range(1,abs(n))
if n ==0:
return 1
if abs(n)==1:
temp = x
else:
for y in span:
if y == 1:
temp = x*x
else:
temp = temp * x
if(n < 0):
return 1/temp
else:
return temp
I still have a problem with my time complexity.
Its working for many testcases, however when it trys to run with x = 0.00001 and n = 2147483647. The time limit issue arises
To reduce the time complexity you can divide the work each time by taking x to the power of 2 and dividing the exponent by two. This makes a logarithmic time algorithm since the exponent is halved at each step.
Consider the following examples:
10^8 = 10^(2*4) = (10^2)^4 = (10*10)^4
Now, there is one edge case. When the exponent is an odd number you can't integer divide it by 2. So in that case you need to multiply the results by the base one additional time.
The following is a direct recursive implementation of the above idea:
class Solution:
def myPow(self, x: float, n: int) -> float:
sign = -1 if n < 0 else 1
n = abs(n)
def helper(x, n):
if n == 1: return x
if n == 0: return 1
if n % 2 == 1:
return helper(x*x, n // 2) * x
else:
return helper(x*x, n // 2)
res = helper(x, n)
if sign == -1:
return 1/res
else:
return res
Note that we have taken abs of the exponent and stored the sign and deal with it at the end.
Instead of iterating from 1 to n, use divide-and-conquer: divide the exponent by 2 and use recursion to get that power, and then square that result. If n was odd, multiply one time more with x:
class Solution:
def myPow(self, x: float, n: int) -> float:
if n == 0:
return 1
if n == 1:
return x
if n < 0:
return self.myPow(1/x, -n)
temp = self.myPow(x, n // 2)
temp *= temp
if n % 2:
temp *= x
return temp
A simple naive solution might be:
def myPow(x: float, n: int) -> float:
## -----------------------
## if we have a negative n then invert x and take the absolute value of n
## -----------------------
if n < 0:
x = 1/x
n = -n
## -----------------------
retval = 1
for _ in range(n):
retval *= x
return retval
While this technically works, you will wait until the cows come home to get a result for:
x = 0.00001 and n = 2147483647
So we need to find a shortcut. Lets' consider 2^5. Our naïve method would calculate that as:
(((2 * 2) * 2) * 2) * 2 == 32
However, what might we observe about the problem if we group some stuff together in a different way:
(2 * 2) * (2 * 2) * 2 == 32
similarly:
((2 * 2) * (2 * 2) * 2) * ((2 * 2) * (2 * 2) * 2) == 32 * 32 = 1024
We might observe that we only technically need to calculate
(2 * 2) * (2 * 2) * 2 == 32
once and use it twice to get 2^10.
Similarly we only need to calcuate:
2 * 2 = 4
once and use it twice to get 2^5....
This suggests a recursion to me.
Let's modify our first attempt to use this divide and concur method.
def myPow2(x: float, n: int) -> float:
## -----------------------
## if we have a negative n then invert x and take the absolute value of n
## -----------------------
if n < 0:
x = 1/x
n = -n
## -----------------------
## -----------------------
## We only need to calculate approximately half the work and use it twice
## at any step.
## -----------------------
def _recurse(x, n):
if n == 0:
return 1
res = _recurse(x, n//2) # calculate it once
res = res * res # use it twice
return res * x if n % 2 else res # if n is odd, multiple by x one more time (see 2^5 above)
## -----------------------
return _recurse(x, n)
Now let's try:
print(myPow2(2.0, 0))
print(myPow2(2.0, 1))
print(myPow2(2.0, 5))
print(myPow2(2.1, 3))
print(myPow2(2.0, -2))
print(myPow2(0.00001, 2147483647))
That gives me:
1
2.0
32.0
9.261000000000001
0.25
0.0
If you have to loop, you have to lope and there is nothing that can be done. Loops in python are slow. That said you may not have to loop and if you do have to loop, it may be possible to push this loop to a highly optimised internal function. Tell us what you are trying to do (not how you think you have to do it, appending elements to a lis may or may not be needed). Always recall the two rules of program optimisation General Rule: Don't do it. Rule for experts: Don't do it yet. Make it work before you make it fast, who knows, it may be fast enough.

How to define a variable by an equation containing it? (Python)

I´m solving a problem in which i have to print all the fibonacci numbers such that:
a <= f <= b
And i would like to start them by the smallest fibonacci number that is greater than or equal to a, in order to make my program run faster. For that, i need to define a variable "n", such that the nth Fibonacci number satisfies the condition above (smallest one that is greater than or equal to a). To define such variable, i need to find the smallest "n" that satisfies the fibonacci(n) general term equation.
I tried to find it by making a for loop, but it just ends up being as slow as if i started to check from the first Fibonacci Number. Anyone has any ideas on how to define it efficiently?
P.S. Here is my attempted code:
from math import sqrt, log, ceil
def Fibo(n):
if n == 1: return 1
elif n == 2: return 2
return Fibo(n-1) + Fibo(n-2)
while True:
try:
a, b = [int(i) for i in input().split()]
cont = 0
phi = (sqrt(5) + 1) / 2
i = ceil(log(a * sqrt(5), phi))
if Fibo(i-1) >= a: i -= 1
elif Fibo(n) < a: i += 1
while True:
if a <= Fibo(i) <= b: cont += 1
elif Fibo(i) > b:
break
i -= 1
print(cont)
except input() == "0 0":
break
Probably the most useful formula for your purpose for F(n), the nth Fibonacci number, is
from math import sqrt
phi = (sqrt(5) + 1) / 2 # the golden ratio
F(n) = round(phi**n / sqrt(5))
Therefore, one formula to get the value of n for a given value of a is
from math import sqrt, log, ceil
phi = (sqrt(5) + 1) / 2 # the golden ratio
n = ceil(log(a * sqrt(5), phi))
Due to approximation and rounding issues, you should check the values of n-1, n, and n+1 to ensure you got exactly the desired value. If you do this often, you should pre-define variables holding the value of the golden ratio and of the square root of five. If your value of a is too large for the float type to store it accurately, you would need a more complicated routine to handle the larger number.

Simpson's rule in 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)

Why does recursive sequence not work correctly?

I'm totally new to Python and I'm trying to print the solution for a recursive sequence:
#Input sequence variables
a = float(raw_input("type in a = "))
n0 = int(raw_input("type in n_0 = "))
n1 = int(raw_input("type in n_1 = "))
y0 = float(raw_input("type in y_0 = "))
#Define function y_n (forward iteration)
def yn(n):
if (n==0):
return y0
else:
return (1/n)-a*yn(n-1)
#backward iteration
def yn_back(n):
return (1/a)*((1/n)-yn(n-1))
if(n1>=n0):
for i in range(n0,n1+1):
print(yn(i))
else:
for i in range(n0,n1+1):
print(yn_back(i))
But if I run this script with a=5, n0=1, n1=30 and y0=log(5/6)=0.182322 the solutions are very high (from 0.08839 to 3.29e+18) and the values are negative for even n. The solution is right for n=1. For other n, the (1/n) in the definition of yn(n) seems to be ignored.
Can someone help me?
Thanks a lot!
n is probably an integer, so 1/n is returning 0 for n greater than 1:
>>> 1/1
1
>>> 1/2
0
>>> 1.0/2
0.5
To make sure you're using float division, change 1 to 1.0 wherever you calculate 1/n:
(1.0/n)
Or convert n into a float.

Factor a quadratic polynomial in Python

Since factoring a quadratic equation in my head just happens, and has done that since I learned it - how would I go about starting to write a quadratic factorer in Python?
Improving Keiths's answer:
Start with a polynomial P(x) = a*x^2 + b*x + c.
Use the quadratic formula (or another method of your choice) to find the roots r1 and r2 to P(x) = 0.
You can now factor P(x) as a*(x-r1)(x-r2).
If your factor (3x - 4)(x - 9) the solution will be 3*(x - 4/3)(x - 9).
You might want to find a way to multiply the 3 into the factors to get rid of fractions / look pretty. In this case, it might help to use fraction arithmetic instead of doubles so you can know the denominators better.
Use the quadratic formula.
I tried implementing hugomg's approach. I stole the "gcd" and "simplify fraction" function from online. Here is my sloppy approach:
from math import sqrt
def gcd(a, b):
while b:
a, b = b, a % b
return a
def simplify_fraction(numer, denom):
if denom == 0:
return "Division by 0 - result undefined"
# Remove greatest common divisor:
common_divisor = gcd(numer, denom)
(reduced_num, reduced_den) = (numer / common_divisor, denom / common_divisor)
# Note that reduced_den > 0 as documented in the gcd function.
if common_divisor == 1:
return (numer, denom)
else:
# Bunch of nonsense to make sure denominator is negative if possible
if (reduced_den > denom):
if (reduced_den * reduced_num < 0):
return(-reduced_num, -reduced_den)
else:
return (reduced_num, reduced_den)
else:
return (reduced_num, reduced_den)
def quadratic_function(a,b,c):
if (b**2-4*a*c >= 0):
x1 = (-b+sqrt(b**2-4*a*c))/(2*a)
x2 = (-b-sqrt(b**2-4*a*c))/(2*a)
# Added a "-" to these next 2 values because they would be moved to the other side of the equation
mult1 = -x1 * a
mult2 = -x2 * a
(num1,den1) = simplify_fraction(a,mult1)
(num2,den2) = simplify_fraction(a,mult2)
if ((num1 > a) or (num2 > a)):
# simplify fraction will make too large of num and denom to try to make a sqrt work
print("No factorization")
else:
# Getting ready to make the print look nice
if (den1 > 0):
sign1 = "+"
else:
sign1 = ""
if (den2 > 0):
sign2 = "+"
else:
sign2 = ""
print("({}x{}{})({}x{}{})".format(int(num1),sign1,int(den1),int(num2),sign2,int(den2)))
else:
# if the part under the sqrt is negative, you have a solution with i
print("Solutions are imaginary")
return
# This function takes in a, b, and c from the equation:
# ax^2 + bx + c
# and prints out the factorization if there is one
quadratic_function(7,27,-4)
If I run this I get the output:
(7x-1)(1x+4)

Categories

Resources