Search code examples
pythonmathstatisticscalculus

What is the best method to check for what value the elements of a list are approaching?


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:

  • I generate a list with the values of f(x) very close to the value which x tends
  • The code analyzes which value is most repeated in this list, and it's concluded that this value is the limit of the function f(x) tending to x.

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:

  • Input 1: Exercise 1, item m)
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']

  • Input 2: Exercise 1, item p)
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']

  • Input 3: Exercise 1, item u)
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']

  • Input 4: Exercise 1, item z)
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']

  • Input 5: Exercise 3, item a)
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?


Solution

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