Post

Root finidng method

Finding the roots of a function is crucial in various fields of science, engineering, and mathematics.

Bisection method

The bisection method is a simple and robust algorithm used to find the roots of a function. It starts by requiring two initial points, (x_0) and (x_1), and then iteratively narrows down the interval containing the root. It is essential that the function values at the initial points, (f(x_0)) and (f(x_1)), have different signs to ensure the existence of solutions for a continuous function. Then, the algorithm evaluates the sign of the function value at the midpoint, (f((x_0+x_1)/2)), and selects the new (x_0) or (x_1) with differing signs. This iterative process continues until the interval becomes sufficiently small, leading to the identification of the root. The following equation is the example function for bisection method.

(1)f(x)=(x+2)(x3)ex



Figure 1. Given function and two initial points for the bisection method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import numpy as np

def function(x):
  return (x+2.) * (x-3.) *  np.exp(x)

def bisection_method(func, x1, x2, tol=1e-6, max_iter=100):
    if func(x1) * func(x2) >= 0:
        raise ValueError("The function values at x1 and x2 must have opposite signs.")
    
    iter_count = 0
    while iter_count < max_iter:
        x_mid = (x1 + x2) / 2
        f_mid = func(x_mid)
        
        if np.abs(f_mid) < tol:
            return x_mid, iter_count
        
        if func(x1) * f_mid < 0:
            x2 = x_mid
        else:
            x1 = x_mid
        
        iter_count += 1
        print( x_mid )
    
    raise ValueError("Maximum number of iterations reached without convergence.")

# Initial guess values
x1 = 2.2
x2 = 3.3

root, iterations = bisection_method(function, x1, x2)
print("Root found:", root)
print("Iterations:", iterations)
iteration (n)(xn+xn+1)/2
12.75
23.025
32.8875
42.95625
52.990625
63.0078125
72.99921875
83.003515625
93.001367187
103.000292969
112.999755859
123.000024414
132.999890137
142.999957275
152.999990845
163.000007629
172.999999237
183.000003433
193.000001335
203.000000286
212.999999762
223.000000024
232.999999893
242.999999958

Newton method

Newton method is also an iterative algorithm for finding the root. Unlike the bisection method, it commences with one initial point and approaches to the solution by utilizing the derivative of the function. Starting with the initial guess, a linear line is drawn, and the root of this line is found, which serves as the next point in the iteration.



Figure 1. Bits allocation for single-precision floating-point numbers

(2)l(x)=f(xn)(xxn)+f(xn)xn+1=xnf(xn)f(xn)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import numpy as np

def function(x):
    return (x + 2) * (x - 3) * np.exp(x)

def derivative(x):
    return (2 * x - 1) * np.exp(x)

def newton_method(func, derivative, x0, tol=1e-6, max_iter=100):
    x = x0
    iter_count = 0
    
    while iter_count < max_iter:
        f_x = func(x)
        f_prime_x = derivative(x)
        
        if np.abs(f_prime_x) < tol:
            raise ValueError("Derivative close to zero. Newton's method failed.")
        
        x_new = x - f_x / f_prime_x
        
        if np.abs(x_new - x) < tol:
            return x_new, iter_count
        
        x = x_new
        iter_count += 1
        print( x )
    
    raise ValueError("Maximum number of iterations reached without convergence.")

# Initial guess value
x0 = 1.5

root, iterations = newton_method(function, derivative, x0)
print("Root found:", root)
print("Iterations:", iterations)

This method shows the more efficient performance compared to the bisection method.

iteration (n)xn
14.12500000000000
23.17456896600000
33.00569705300000
43.00000647700000
53.000000000008389

However, the selection of an appropriate initial point is crucial, as an unsuitable choice may lead to divergence.



Figure 2. Divergence due to the inappropriate starting point

Newton method for the system equation

The Newton’s method can be extended to solve systems of equations. For instance, consider the following system of equations which can be solved using the Newton’s method, yielding roots at x=0, y=2, and z=3:

(3)x+y+z=5x2+y2+z2=13ex+xy+xz=1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import numpy as np

x0 = np.array([1., 3., 5.])

def function(x):
    f = np.array([x[0] + x[1] + x[2] - 5.,
                  x[0]**2 + x[1]**2 + x[2]**2 - 13.,
                  np.exp(x[0]) + x[0]*x[1] - x[0]*x[2] - 1.])
    return f

def derivative(x):
    df = np.array([[1., 1., 1.],
                   [2.*x[0], 2.*x[1], 2.*x[2]],
                   [np.exp(x[0]) + x[1] - x[2], x[0], -x[0]]])
    return df

def newton_method_system(func, derivative, x0, tol=1e-6, max_iter=100):
    x = x0
    iter_count = 0
    
    while iter_count < max_iter:
        f_x = func(x)
        jacobian = derivative(x)
        
        if np.linalg.norm(f_x) < tol:
            print(np.linalg.norm(f_x))
            return x, iter_count
        
        try:
            x_new = x - np.linalg.solve(jacobian, f_x)
        except np.linalg.LinAlgError:
            raise ValueError("Singular Jacobian matrix. Newton's method failed.")
        
        print(np.linalg.norm(f_x))
        x = x_new
        iter_count += 1
    
    raise ValueError("Maximum number of iterations reached without convergence.")

# Initial guess value
x0 = np.array([1., 3., 5.])

root, iterations = newton_method_system(function, derivative, x0)
print("Root found:", root)
print("Iterations:", iterations)

iteration (n)||f(xn)||2
122.3624543627970
28.69354732514425
31.48209143410228
40.126598379841041
50.0212901707189886
60.00609083316951065
70.00149434368995687
80.000374527505121139
99.36714099866895e-05
102.34237308180405e-05
115.85667658839108e-06
121.46426296802727e-06
133.66077521047241e-07

Remark

When dealing with scalar values, determining the error involves a straightforward subtraction of the real value from the approximated value. If only the magnitude of the error is required, the sign can be disregarded. However, when working with multidimensional data such as 2D vectors or 3D matrices, the concept of “norm” comes into play. In numerical analysis, the p-norm is often employed as a common method for handling high-dimensional values.

(4)||x||p=(i=1n|xi|p)1/p
This post is licensed under CC BY 4.0 by the author.

Comments powered by Disqus.