Search code examples
pythonnumpybisection

Bisection function returning the correct values with only some functions


When i apply my bisection to a numbers in a function it only returns the correct values with certain functions.

import numpy as np

def x_2(x):
    return x**2-5


def x_sin_exp(x):
    return 0.5 + -(x + np.sin(x)) * np.exp(-x**2.0)


def bisection(f: callable, a: float, b: float, tol=0.001, maxiter=100)-> tuple[float, int]:
    r=(a+b)/2
    for nit in range(maxiter+1):
        r = (a + b) / 2.0
        if np.abs(f(r))<tol:
            return (r,nit)
        elif f(a)*f(r)<0:
            b = r
        else:
            a = r
        

    return (r,nit)

For example, when apllying the x_2 it works correctly, but not with x_sin_exp as shown bellow:

bisection(x_2,-3,3) returns correctly (-2.236083984375, 12) bisection(x_sin_exp,-3,3) returns incorrectly (3.0,100)

I have tried checking the symbols on the bisection function, and drawing them to check if the first outputed values are correct. The main problem is when it has to enter the else part of the function, it never does. I have tried with an other elif, but doesnt correct it.


Solution

  • The problem in your code arises from the initial choice of ( a ) and ( b ) for the function x_sin_exp. The function x_sin_exp does not have a root in the interval ([-3, 3]) such that ( f(a) \times f(b) < 0 ). Therefore, your bisection method does not enter the "else" part, and you get incorrect results.

    First, make sure ( f(a) \times f(b) < 0 ) before running the bisection algorithm. Second, it's good to check for cases where the function at the endpoints is already within the tolerance.

    Here's a revised version of your function:

    import numpy as np
    
    def bisection(f: callable, a: float, b: float, tol=0.001, maxiter=100) -> tuple[float, int]:
        if f(a) * f(b) >= 0:
            raise ValueError("f(a) and f(b) must have different signs.")
    
        for nit in range(1, maxiter + 1):
            r = (a + b) / 2.0
            if np.abs(f(r)) < tol:
                return r, nit
            elif f(a) * f(r) < 0:
                b = r
            else:
                a = r
        return r, nit
    

    Now, calling bisection(x_sin_exp, 3, 3) will raise a ValueError with a message explaining that ( f(a) ) and ( f(b) ) must have different signs.