I'm working on a method for calculating limits of a function in Python, but certain functions give an incorrect output. My main goal is to calculate derivatives, but for that, I must calculate the limit of a function.
This is for a personal project, I would not like a module to solve the limit, or search an algorithm on Wikipedia. I tried using the following method:
This method is obviously antimathematical, but I could not think of a better solution. I tried to apply centralization measures, but I don't think it works for a periodic continued fraction, like 2/7.
Here is my code:
# this function resolves a numerical expression
# using eval function from Python
def Solve(Str):
if '^' in Str:
Str = Str.replace('^', '**')
return eval(Str)
# this function solves a mathematical function by substituting x
# for a value passed by parameter and returning its result
def SolveF(f, x, var = 'x'):
f = f.replace(' ', '')
# inserts a multiplication sign between numbers
# example: 5x --> 5*x
f = list(f)
i = 0
while i < len(f)-1:
L = f[i:i+2]
if L[0] in '0123456789' and L[1] == var:
f.insert(i+1, '*')
i += 1
f = ''.join(f)
f = f.replace(var, '(' + var + ')')
f = f.replace(var, str(x))
return Solve(f)
# this function returns f(x) for a value very close
# to the value at which x tends. for example, if x
# tends to 5, it returns f(5.0000000000001). the tiny
# amount that is added to x is 10^(-13) (arbitrary value)
def Lim(f, x, c = 13):
return SolveF(f, x + (10**(-c)))
# this function returns several f(x) in a list to values
# very close to the value at which x tends. for example,
# if x tends to 0, it will add the list f(0.001), f(0.000001),
# f(0.0000001), ..., f(0.0000000001). then returns the value
# that most repeats in that list, which is supposed to be the
# value whose function is approaching.
def LimM(f, x):
i = 0
L = []
for i in range(5, 20):
try:
L.append("{:.10f}".format(Lim(f, x, i)))
except ZeroDivisionError:
i += 1
continue
print(L)
List2 = [L.count(i) for i in set(L)]
if List2 == [1]*len(List2):
return 'inf'
else:
return list(set(L))[List2.index(max(List2))]
from fractions import Fraction
while True:
F = input('Function: ')
X = float(input('x --> '))
Res = LimM(F, X)
if Res != 'inf':
print(Fraction(Res).limit_denominator())
else:
print(Res)
Example 1: the function (x^2 - 4)/(x - 2)
approaching x = 2
.
The list generated by the LimM function is equal to ['4.0000100000', '4.0000010001', '4.0000000977', '4.0000000000', '4.0000000000', '4.0000000000', '4.0000000000', '4.0000000000', '4.0000000000', '4.0000000000', '4.0000000000']
.
Notice that the value that repeats the most in the list is '4.0000000000'
, so the limit is equal to 4.
Example 2: the function ((x + 1)/(x - 1)
approaching x = 2
.
The list generated by the LimM function is equal to ['2.9999800002', '2.9999980000', '2.9999998000', '2.9999999800', '2.9999999980', '2.9999999998', '3.0000000000', '3.0000000000', '3.0000000000', '3.0000000000', '3.0000000000', '3.0000000000', '3.0000000000', '3.0000000000', '3.0000000000']
.
Notice that the value that repeats the most in the list is '3.0000000000'
, so the limit is equal to 3.
I tested 28 different limits (you can check the questions here), and only 6 were incorrect. Among them are:
Function: (1/(1-x)) - (3/(1-x^3))
x --> 1
Right answer: -1
Code output: 0
List generated: ['-0.9999930434', '-1.0000138859', '-0.9992006216', '-0.7401486933', '0.0000000000', '0.0000000000', '0.0000000000', '0.0000000000', '0.0000000000', '0.0000000000', '0.0000000000']
Function: (3x^4 - 4x^3 + 1)/(x - 1)^2
x --> 1
Right answer: 6
Code output: 0
List generated: ['6.0000848733', '6.0000893153', '5.9952043260', '8.8817843050', '0.0000000000', '0.0000000000', '0.0000000000', '0.0000000000', '0.0000000000', '0.0000000000', '0.0000000000']
Function: (x^2 + 7x - 44)/(x^2 - 6x + 8)
x --> 4
Right answer: 15/2
Code output: 4222/563
List generated: ['7.4999675007', '7.4999967484', '7.4999995648', '7.4999992895', '7.4999982236', '7.4999911182', '7.4991119005', '7.4991119005', '7.5714285714', '6.6666666667']
Function: (1/(x^2 - 1)) - (2/(x^4 - 1))
x --> 1
Right answer: 1/2
Code output: 0
List generated: ['0.4999950374', '0.4999879392', '0.4996002605', '0.8326672688', '0.0000000000', '0.0000000000', '0.0000000000', '0.0000000000', '0.0000000000', '0.0000000000', '0.0000000000']
Function: ((1 + 2x)^(0.5) - 1)/(3x)
x --> 0
Right answer: 1/3
Code output: 0
List generated: ['0.3333316667', '0.3333331667', '0.3333333165', '0.3333333313', '0.3333332869', '0.3333333609', '0.3333333609', '0.3332889520', '0.3330669074', '0.3330669074', '0.2960594732', '0.0000000000', '0.0000000000', '0.0000000000', '0.0000000000']
Therefore, what is the best method to check for what value the elements of a list are approaching?
Just take the last value. It's the best approximation you're going to get.
Higher-level algorithmic improvement: don't even bother computing the other elements of the list. Just take a difference quotient with a small difference and use that as your computed derivative.
This is of course not going to be fully reliable, but that's an unavoidable problem of the approach you've chosen. It is simply not possible to compute limits by looking at a finite number of points near where you want to take the limit.