Root finding algorithm#

Where is the function crossing 0 ? How to find these points effectively ?

Introduction#

\(F(x)\) is a non linear function. We want to find points where this function reach zero if they exist. Root finding refers to the general problem of searching for a solution of an equation \(F(x) = 0\).

I - Naive method#

\(f\) is a function:

  • continuous on \( \mathopen{[}a\,,b\mathclose{]} \)

  • monotonous on \( \mathopen{[}a\,,b\mathclose{]} \)

  • such as \(f(a)f(b) < 0 \)

Method :

  • we choose a step \(dx\)

  • we test \(f(a + k.dx)\) until \(f\) changes its sign. \(k\) is a positive integer

## input : function f and interval [a,b] and our step is dx

import math 

def naive():
    x = a + dx
    while (f(x) * f(a) > 0):
        x = x + dx
    return x

Be aware the result is an interval \( \mathopen{[}x - dx\,,x\mathclose{]} \). You can launch again the algorithm on this interval with a smaller step.

It is actually a really bad method.

II - Bisection#

Bisection is the simplest root finding algorithm. It can be applied to any type of continuous function \(f(x)\) on the interval \( \mathopen{[}a\,,b\mathclose{]} \) where the value of the function \(f(x)\) changes sign from \(a\) to \(b\). The idea is simple: divide the interval in two, a solution must exist within one subinterval, select the subinterval where the sign of changes and repeat.

\(f\) is a function:

  • continuous on \( \mathopen{[}a\,,b\mathclose{]} \)

  • monotonous on \( \mathopen{[}a\,,b\mathclose{]} \)

  • such as \(f(a)f(b) < 0 \)

Method :

We’re looking for a small interval containing the zero.

  • We split the start interval in half

  • We keep the interval that contains the 0

  • And we do it all over again

def bisection(f,a,b,N):
    '''
    Parameters
    ----------
    f : function
        The function for which we are trying to approximate a solution f(x)=0.
    a,b : numbers
        The interval in which to search for a solution. The function returns
        None if f(a)*f(b) >= 0 since a solution is not guaranteed.
    N : (positive) integer
        The number of iterations to implement.
    '''
    if f(a)*f(b) >= 0:
        print("Bisection method fails.")
        return None
    for i in range(1,N):
        c = (a+b)/2
        if(f(a) * f(c) < 0):
            b = c
        else:
            a = c
    return (a+b)/2
f = lambda x: x**2 - x - 1
bisection(f,1,2,25)
1.6180339753627777

Note : To obtain a 2 times higher precision, make another interation. Bisection method only used sign, not the values of the function. Bisection method is an improvement of the recursive naive method, where the step equals half the interval.

III - False position#

This method is the trial and error technique of using test (“false”) values for the variable and then adjusting the test value according to the outcome.

\(f\) is a function:

  • continuous on \( \mathopen{[}a\,,b\mathclose{]} \)

  • monotonous on \( \mathopen{[}a\,,b\mathclose{]} \)

  • such as \(f(a)f(b) < 0 \)

Method :

  • we do a linear approximation of \(f\) on \(\mathopen{[}a\,,b\mathclose{]}\)

    • we approximate the \(f\) curve by its chord

  • we solve the linear equation

    • we calculate the equation of the line passing through \((a, f(a)), (b, f(b))\)

    • we calculate its intersection with the abscissa axis

  • we get a new interval

  • we start again

You stop when \(f(c)\) is small enough

# The function is x^3 - x^2 + 2 
def function( x ): 
    return (x * x * x - x * x + 2) 

def falsePosition(a,b,N):
    if function(a) * function(b) >= 0: 
        print("You have not assumed right a and b") 
        return -1
      
    c = a # Initialize result 
      
    for i in range(N): 
          
        # Find the point that touches x axis 
        c = (a * function(b) - b * function(a))/ (function(b) - function(a)) 
          
        # Check if the above found point is root 
        if function(c) == 0: 
            break
          
        # Decide the side to repeat the steps 
        elif function(c) * function(a) < 0: 
            b = c 
        else: 
            a = c 
    print("The value of root is : " , '%.4f' %c, "with", '%i' %N ,"iterations")
    
falsePosition(-200, 300, 1000)
falsePosition(-200, 300, 100000)
The value of root is :  -6.2309 with 1000 iterations
The value of root is :  -1.0033 with 100000 iterations

This method can be unsuitable for some functions. The false position method is adapted to the functions whose behaviour is linear on \( \mathopen{[}a\,,b\mathclose{]} \), which allows to limit \( \mathopen{[}a\,,b\mathclose{]} \) more quickly than with the dichotomy method. It uses the values of \(f\) (unlike the bisection)

IV - Newton#

Newton’s method is a root finding method that uses linear approximation. In particular, we guess a solution \(x_0\) of the equation \(f(x)=0\), compute the linear approximation of \(f(x)\) at \(x_0\) and then find the \(x\)-intercept (\(x_1\)) of the linear approximation.

Hypothesis : \(x_0\) close to the result Iterative method

Method:

  • we calculate the tangent to the point \((x_i,f(x_i))\)

  • we calculate the intersection of the tangent with the abscissa axis

  • we get \(x_{i+1}\)

  • we start again

Tangent : $\(y = f'(x_0)x + y_0 - f'(x_0)x_0\)$

If \(y_1 = 0\) and \(y_0 = f(x_0)\), then

\[x_1 = x_0 - \frac{f(x_0)}{f'(x_0)}\]

We stop when \(f(x)\) is small enough.

def newton(f,Df,x0,epsilon,max_iter):
    '''Parameters
    ----------
    f : function
        Function for which we are searching for a solution f(x)=0.
    Df : function
        Derivative of f(x).
    x0 : number
        Initial guess for a solution f(x)=0.
    epsilon : number
        Stopping criteria is abs(f(x)) < epsilon.
    max_iter : integer
        Maximum number of iterations of Newton's method.
    '''
    xn = x0
    for n in range(0,max_iter):
        fxn = f(xn)
        if abs(fxn) < epsilon:
            print('Found solution after',n,'iterations.')
            return xn
        Dfxn = Df(xn)
        if Dfxn == 0:
            print('Zero derivative. No solution found.')
            return None
        xn = xn - fxn/Dfxn
    print('Exceeded maximum iterations. No solution found.')
    return None

f = lambda x: x**3 - x**2 - 1
Df = lambda x: 3*x**2 - 2*x
newton(f,Df,1,1e-10,10)
Found solution after 6 iterations.
1.4655712318767877

V - Secant#

We want to use Newton’s method but we don’t know the derivative \(f'\) of \(f\). We make an estimation of the derivative of \(f\) in \(x_0\) that we reinject in Newton’s method.

**Method : **

  • We’re evaluating the derivative of \(f(x_i)\).

  • We compute the tangent to the point \((x_i, f(x_i))\)

  • Xe calculate the intersection of the tangent with the abscissa axis

  • We get x_{i+1}

  • We do it all over again

We stop when \(f(x)\) is small enough.

Derivative : $\(f'(x_0) = lim_{x->x_0}\frac{f(x) - f(x_0)}{x - x_0} \)$

Numerical Estimation of the derivative (back derivative) : $\(f'(x) \simeq \frac{f(x) - f(x - \Delta x)}{\Delta x}\)$

Iteration : $\( x_{n+1} = x_n − \Delta x \frac{f(x_n)}{f(x_n) - f(x_n - \Delta x)} \)$

def function(x): 
    # we are taking equation x^3+x-1  
    f = pow(x, 3) + x - 1;  
    return f;  
  
def secant(x1, x2, E): 
    n = 0; xm = 0; x0 = 0; c = 0;  
    if (function(x1) * function(x2) < 0): 
        while True:  
            # calculate the intermediate value  
            x0 = ((x1 * function(x2) - x2 * function(x1)) / (function(x2) - function(x1)));  
  
            # check if x0 is root of  
            # equation or not  
            c = function(x1) * function(x0);  
  
            # update the value of interval  
            x1 = x2;  
            x2 = x0;  
  
            # update number of iteration  
            n += 1;  
  
            # if x0 is the root of equation  
            # then break the loop  
            if (c == 0):  
                break;  
            xm = ((x1 * function(x2) - x2 * function(x1)) / (function(x2) - function(x1))); 
              
            if(abs(xm - x0) < E): 
                break; 
          
        print("Root of the given equation =",round(x0, 6));  
        print("Number of iterations = ", n);  
          
    else: 
        print("Can not find a root in ", 
                   "the given inteval");  
        
x1 = 0;  
x2 = 1;  
E = 0.0001;  
secant(x1, x2, E);
Root of the given equation = 0.682326
Number of iterations =  5

Conclusion#

The root-finding problem is one of the most important computational problems. It arises in wide variety of practical applications.

📖 Sources#

Websites#

Name

Author

Date

Description

Mathematical Python

Patrick Walls

2019

Explanation of mathematical computing

Math classes#

Teacher

Curriculum

Vincent Nozick

IMAC