'How to do the Bisection method in Python

I want to make a Python program that will run a bisection method to determine the root of:

f(x) = -26 + 85x - 91x2 +44x3 -8x4 + x5

The Bisection method is a numerical method for estimating the roots of a polynomial f(x).

Are there any available pseudocode, algorithms or libraries I could use to tell me the answer?



Solution 1:[1]

Basic Technique

Here's some code showing the basic technique:

>>> def samesign(a, b):
        return a * b > 0

>>> def bisect(func, low, high):
    'Find root of continuous function where f(low) and f(high) have opposite signs'

    assert not samesign(func(low), func(high))

    for i in range(54):
        midpoint = (low + high) / 2.0
        if samesign(func(low), func(midpoint)):
            low = midpoint
        else:
            high = midpoint

    return midpoint

>>> def f(x):
        return -26 + 85*x - 91*x**2 +44*x**3 -8*x**4 + x**5

>>> x = bisect(f, 0, 1)
>>> print(x, f(x))
0.557025516287 3.74700270811e-16

Tolerance

To exit early when a given tolerance is achieved, add a test at the end of the loop:

def bisect(func, low, high, tolerance=None):
    assert not samesign(func(low), func(high))   
    for i in range(54):
        midpoint = (low + high) / 2.0
        if samesign(func(low), func(midpoint)):
            low = midpoint
        else:
            high = midpoint
        if tolerance is not None and abs(high - low) < tolerance:
            break   
    return midpoint

Solution 2:[2]

You could see the solution in an earlier Stack Overflow question here that uses scipy.optimize.bisect. Or, if your purpose is learning, the pseudocode in the Wikipedia entry on the bisection method is a good guide to doing your own implementation in Python, as suggested by a commenter on the the earlier question.

Solution 3:[3]

My implementation is more generic and yet simpler than the other solutions: (and public domain)

def solve(func, x = 0.0, step = 1e3, prec = 1e-10):
    """Find a root of func(x) using the bisection method.

    The function may be rising or falling, or a boolean expression, as long as
    the end points have differing signs or boolean values.

    Examples:
        solve(lambda x: x**3 > 1000) to calculate the cubic root of 1000.
        solve(math.sin, x=6, step=1) to solve sin(x)=0 with x=[6,7).
    """
    test = lambda x: func(x) > 0  # Convert into bool function
    begin, end = test(x), test(x + step)
    assert begin is not end  # func(x) and func(x+step) must be on opposite sides
    while abs(step) > prec:
        step *= 0.5
        if test(x + step) is not end: x += step
    return x

Solution 4:[4]

# Defining Function
def f(x):
    return x**3-5*x-9

# Implementing Bisection Method
def bisection(x0,x1,e):
    step = 1
    print('\n\n*** BISECTION METHOD IMPLEMENTATION ***')
    condition = True
    while condition:
        x2 = (x0 + x1)/2
        print('Iteration-%d, x2 = %0.6f and f(x2) = %0.6f' % (step, x2, f(x2)))

        if f(x0) * f(x2) < 0:
            x1 = x2
        else:
            x0 = x2

        step = step + 1
        condition = abs(f(x2)) > e

    print('\nRequired Root is : %0.8f' % x2)


# Input Section
x0 = input('First Guess: ')
x1 = input('Second Guess: ')
e = input('Tolerable Error: ')

# Converting input to float
x0 = float(x0)
x1 = float(x1)
e = float(e)

#Note: You can combine above two section like this
# x0 = float(input('First Guess: '))
# x1 = float(input('Second Guess: '))
# e = float(input('Tolerable Error: '))


# Checking Correctness of initial guess values and bisecting
if f(x0) * f(x1) > 0.0:
    print('Given guess values do not bracket the root.')
    print('Try Again with different guess values.')
else:
    bisection(x0,x1,e)

Code and Output Here

Additionally codesansar.com/numerical-methods/ has large collection of algorithms, pseudocodes, and programs using different programming languages for Numerical Analysis.

Solution 5:[5]

With tolerance:

# there is only one root
def fn(x):
 return x**3 + 5*x - 9
 # define bisection method
def bisection( eq, segment, app = 0.3 ):
 a, b = segment['a'], segment['b']
 Fa, Fb = eq(a), eq(b)
 if Fa * Fb > 0:
  raise Exception('No change of sign - bisection not possible')   
 while( b - a > app ): 
  x = ( a + b ) / 2.0
  f = eq(x)
  if f * Fa > 0: a = x
  else: b = x  
 return x
 #test it
print bisection(fn,{'a':0,'b':5}, 0.00003) # => 1.32974624634

Live: http://repl.it/k6q

Solution 6:[6]

I'm trying to make some modifications :

  1. While loop : the tolerance and the number of iterations performed by the algorithm
  2. save in addition to the root approach, the vector of points generated by the algorithm xn (all c points), the vector of all images f(c)
  3. Assuming Xs is a given approximation of the root, save the absolute error np.linalg.norm(Xs-xn)
  4. Save the approximate error : np.linalg.norm(xn+1 - xn).

This is my code:

import numpy as np
def bisection3(f,x0,x1,tol,max_iter):
    c = (x0+x1)/2.0
    x0 = c
    Xs = 0.3604217029603
    err_Abs = np.linalg.norm(x0-Xs)
    itr = 0
    f_x0 = f(x0)
    f_x1 = f(x1)
    xp = [] # sucesion que converge a la raiz
    errores_Abs = []
    errores_Aprox = []
    fs = [] # esta sucecion debe converger a cero 
    while(((x1-x0)/2.0 > tol) and (itr< max_iter)):
        if f(c) == 0:
            return c
        elif f(x0)*f(c) < 0:
            x1 = c
        else :
            x0 = c
        c = (x0+x1)/2.0    
        itr +=1
        err_Abs = np.linalg.norm(x0-Xs)
        err_Aprox = np.linalg.norm(x1 - x0)
        fs.append(f(c))
        xp.append(c)
        errores_Abs.append(err_Abs)
        errores_Aprox.append(err_Aprox)
    return x0,errores_Abs, errores_Aprox,fs,xp

And i have an example of execution :

f  = lambda x : 3*x + np.sin(x) - np.exp(x)
X0_r1 ,  err_Abs_r1,err_Aprox_r1, fs_r1 , xp_r1 =   bisection3(f,0,0.5,1e-5,100)

Solution 7:[7]

It is possible to modify the Bisection-method above with a tolerance as the stopper:

def samesign(a, b):
        return a*b > 0

def bisect(func, low, high):
    assert not samesign(func(low), func(high))
    n = 20
    e = 0.01 # the epsilon or error we justify for tolerance
    for i in range(n):
        if abs(func(low)-func(high)) > e:
            midpoint = (low + high) / 2
            print(i, midpoint, f(midpoint))
            if samesign(func(low), func(midpoint)):
                low = midpoint
            else:
                high = midpoint
        else:
            return round(midpoint, 2)
    return round(midpoint, 2)

Solution 8:[8]

  def f(x):
    return -26 + 58 * x - 91 * x**2 + 44 * x**3 - 8 * x**4 + x**5


def relative_error(p, p_old):
    return abs((p - p_old) / p)


def bisection(a, b, no_iterations, accuracy):
    i = 1
    f_a = f(a)
    p_old = 0
    while i <= no_iterations:
        p = (a + b) / 2
        f_p = f(p)
        error = relative_error(p, p_old)
        print('%2d %4f %4f %6f %6f %4.6f' % (i, a, b, p, f_p, error))
        if error < accuracy or f_p == 0:
            break
        p_old = p
        i += 1
        if f_a * f_p > 0:
            a = p
            f_a = f_p
        else:
            b = p


bisection(1, 2, 20, 0.00001)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 dermen
Solution 3 Tronic
Solution 4 saki
Solution 5
Solution 6 robintux
Solution 7 Ardent Coder
Solution 8 Nirmal Sankalana