Inefficient algorithm! Can you help me make this more efficient? - python

In mathematics, a Diophantine equation is a polynomial equation, usually with two or more unknowns, such that only the integer solutions are sought or studied.
This is the equation:
x**2 - 4 * y**2 = n
(where the unknowns are x and y, and n is a given positive number.
My function iterates through every value between 0-n. This is inefficient and can't always hack big n numbers. What is the best way to get the first unknown variable?
def sol_equa(n):
answers = []
for x in range(1 , n+1):
y = ((x ** 2 - n) / 4) ** (1 / 2)
try:
if y == y // 1:
answers.append([x, int(y)])
except TypeError:
continue
if len(answers) >= 1:
answers = list(reversed(answers))
return answers
else:
return []
sol_equation(90005) --> "[[45003, 22501], [9003, 4499], [981, 467], [309, 37]]"

You can use sympy.solvers.diophantine. Take your equation as
from sympy.solvers.diophantine import diophantine
from sympy.abc import x,y
n = 90005
solutions = diophantine(x**2 - 4*y**2 - n)
print(solutions)
#{(-981, 467), (-309, 37), (45003, 22501), (-45003, -22501), (309, -37), (-9003, -4499), (981, 467), (9003, -4499), (-9003, 4499), (45003, -22501), (309, 37), (-309, -37), (-45003, 22501), (9003, 4499), (981, -467), (-981, -467)}
If you want positive solutions only, you can use set comprehension:
positive_solutions = {sol for sol in solutions if sol[0] > 0 and sol[1] > 0}
print(positive_solutions)
#{(981, 467), (9003, 4499), (309, 37), (45003, 22501)}
Execution time tests
from timeit import timeit
sol_equa= '''
def sol_equa(n):
answers = []
for x in range(1 , n+1):
y = ((x ** 2 - n) / 4) ** (1 / 2)
try:
if y == y // 1:
answers.append([x, int(y)])
except TypeError:
continue
if len(answers) >= 1:
answers = list(reversed(answers))
return answers
else:
return []
sol_equa(90005)
'''
sympy_solver = '''
def sympy_solver(n):
solutions = diophantine(x**2 - 4*y**2 - n)
return {sol for sol in solutions if sol[0] > 0 and sol[1] > 0}
sympy_solver(90005)
'''
setup = '''
from sympy.solvers.diophantine import diophantine
from sympy.abc import x,y
'''
print(timeit(sympy_solver,number=1000, setup = setup))
#13.50360608200208
print(timeit(sol_equa,number=1000))
#I don't know, it took too long :P

Related

Approximating sin using the Taylor series

I'm trying to calculate sin(x) using Taylor series without using factorials.
import math, time
import matplotlib.pyplot as plot
def sin3(x, i=30):
x %= 2 * math.pi
n = 0
dn = x**2 / 2
for c in range(4, 2 * i + 4, 2):
n += dn
dn *= -x**2 / ((c + 1) * (c + 2))
return x - n
def draw_graph(start = -800, end = 800):
y = [sin3(i/100) for i in range(start, end)]
x = [i/100 for i in range(start, end)]
y2 = [math.sin(i/100) for i in range(start, end)]
x2 = [i/100 for i in range(start, end)]
plot.fill_between(x, y, facecolor="none", edgecolor="red", lw=0.7)
plot.fill_between(x2, y2, facecolor="none", edgecolor="blue", lw=0.7)
plot.show()
When you run the draw_graph function it uses matplotlib to draw a graph, the redline is the output from my sin3 function, and the blue line is the correct output from the math.sin method.
As you can see the curve is not quite right, it's not high or low enough (seems to peak at 0.5), and also has strange behavior where it generates a small peak around 0.25 then drops down again. How can I adjust my function to match the correct output of math.sin?
You have the wrong equation for sin(x), and you also have a messed up loop invariant.
The formula for sin(x) is x/1! - x^3/3! + x^5/5! - x^7/7!..., so I really don't know why you're initializing dn to something involving x^2.
You also want to ask yourself: What is my loop invariant? What is the value of dn when I reach the start of my loop. It is clear from the way you update dn that you expect it to be something involving x^i / i!. Yet on the very first iteration of the loop, i=4, yet dn involves x^2.
Here is what you meant to write:
def sin3(x, i=30):
x %= 2 * math.pi
n = 0
dn = x
for c in range(1, 2 * i + 4, 2):
n += dn
dn *= -x**2 / ((c + 1) * (c + 2))
return n

karatsuba's integer multiplication algorithm python

This code is not passing all the test cases, can somebody help? I only pass the straight forward test then it loses precision.
import math
import unittest
class IntegerMultiplier:
def multiply(self, x, y):
if x < 10 or y < 10:
return x * y
x = str(x)
y = str(y)
m_max = min(len(x), len(y))
x = x.rjust(m_max, '0')
y = y.rjust(m_max, '0')
m = math.floor(m_max / 2)
x_high = int(x[:m])
x_low = int(x[m:])
y_high = int(y[:m])
y_low = int(y[m:])
z1 = self.multiply(x_high, y_high)
z2 = self.multiply(x_low, y_low)
z3 = self.multiply((x_low + x_high), (y_low + y_high))
z4 = z3 - z1 - z2
return z1 * (10 ** m_max) + z4 * (10 ** m) + z2
class TestIntegerMultiplier(unittest.TestCase):
def test_easy_cases(self):
integerMultiplier = IntegerMultiplier()
case2 = integerMultiplier.multiply(2, 2)
self.assertEqual(case2, 4)
case3 = integerMultiplier.multiply(2, 20000)
self.assertEqual(case3, 40000)
case4 = integerMultiplier.multiply(2000, 2000)
self.assertEqual(case4, 4000000)
def test_normal_cases(self):
intergerMultiplier = IntegerMultiplier()
case1 = intergerMultiplier.multiply(1234, 5678)
self.assertEqual(case1, 7006652)
if __name__ == '__main__':
unittest.main()
for the first test case, 'test_easy_cases' all are passing for the other two cases, I get error e.g. AssertionError: 6592652 != 7006652
In choosing m, you choose a base for all following decompositions and compositions. I recommend one with a representation of length about the average of the factors' lengths.
I have "no" idea why time and again implementing Karatsuba multiplication is attempted using operations on decimal digits - there are two places you need to re-inspect:
when splitting a factor f into high and low, low needs to be f mod m, high f // m
in the composition (last expression in IntegerMultiplier.multiply()), you need to stick with m (and 2×m) - using m_max is wrong every time m_max isn't even.

Graphing Intergration in Python

In the following code I have implemented Simpsons Rule in Python. I have attempted to plot the absolute error as a function of n for a suitable range of integer values n. I know that the exact result should be 1-cos(pi/2). However my graph doesn't seem to be correct. How can I fix my code to get the correct output? there were two loops and I don't think I implemented my graph coding correctly
def simpson(f, a, b, n):
"""Approximates the definite integral of f from a to b by the composite Simpson's rule, using n subintervals (with n even)"""
h = (b - a) / (n)
s = f(a) + f(b)
diffs = {}
for i in range(1, n, 2):
s += 4 * f(a + i * h)
for i in range(2, n-1, 2):
s += 2 * f(a + i * h)
r = s
exact = 1 - cos(pi/2)
diff = abs(r - exact)
diffs[n] = diff
ordered = sorted(diffs.items())
x,y = zip(*ordered)
plt.autoscale()
plt.loglog(x,y)
plt.xlabel("Intervals")
plt.ylabel("Error")
plt.show()
return s * h / 3
simpson(lambda x: sin(x), 0.0, pi/2, 100)
Your simpson method should just calculate the integral for a single value of n (as it does), but creating the plot for many values of n should be outside that method. as:
from math import pi, cos, sin
from matplotlib import pyplot as plt
def simpson(f, a, b, n):
"""Approximates the definite integral of f from a to b by the composite Simpson's rule, using 2n subintervals """
h = (b - a) / (2*n)
s = f(a) + f(b)
for i in range(1, 2*n, 2):
s += 4 * f(a + i * h)
for i in range(2, 2*n-1, 2):
s += 2 * f(a + i * h)
return s * h / 3
diffs = {}
exact = 1 - cos(pi/2)
for n in range(1, 100):
result = simpson(lambda x: sin(x), 0.0, pi/2, n)
diffs[2*n] = abs(exact - result) # use 2*n or n here, your choice.
ordered = sorted(diffs.items())
x,y = zip(*ordered)
plt.autoscale()
plt.loglog(x,y)
plt.xlabel("Intervals")
plt.ylabel("Error")
plt.show()

I'm getting a "mul object is not callable"-error

import sympy as sp
def taylorCoefficient(f, a, n):
x = sp.symbols("x")
coefficient = []
for i in range(0, n + 1):
afgeleide = sp.diff(f(x), x, n=n)
def f(x0): return afgeleide.subs(x, x0)
coefficient += f(a) / sp.factorial(n)
return coefficient
x = sp.symbols("x")
taylorCoefficient(x ** 2 * sp.sin(x / 2), 0, 3)
I'm getting a mul object is not callable, but why? (On line afgeleide=...)
Try changing the last line of your script to taylorCoefficient( lambda x: x**2*sp.sin(x/2), 0, 3).

Simpson's Rule returning 0

I coded a function for Simpson's Rule of numerical integration. For values of n more than or equal to 34, the function returns 0.
Here, n is the number of intervals, a is the start point, and b is the end point.
import math
def simpsons(f, a,b,n):
x = []
h = (b-a)/n
for i in range(n+1):
x.append(a+i*h)
I=0
for i in range(1,(n/2)+1):
I+=f(x[2*i-2])+4*f(x[2*i-1])+f(x[2*i])
return I*(h/3)
def func(x):
return (x**(3/2))/(math.cosh(x))
x = []
print(simpsons(func,0,100,34))
I am not sure why this is happening. I also coded a function for the Trapezoidal Method and that does not return 0 even when n = 50. What is going on here?
Wikipedia has the code for Simpson's rule in Python :
from __future__ import division # Python 2 compatibility
import math
def simpson(f, a, b, n):
"""Approximates the definite integral of f from a to b by the
composite Simpson's rule, using n subintervals (with n even)"""
if n % 2:
raise ValueError("n must be even (received n=%d)" % n)
h = (b - a) / n
s = f(a) + f(b)
for i in range(1, n, 2):
s += 4 * f(a + i * h)
for i in range(2, n-1, 2):
s += 2 * f(a + i * h)
return s * h / 3
def func(x):
return (x**(3/2))/(math.cosh(x))

Categories

Resources