Computers in Engineering WWW Site - Example 18.1

Example 18.1


FORTRAN version

!
!       Program - P125.F90
!
        PROGRAM Rooter
!  methods.

!       Function types!
        IMPLICIT NONE
        REAL :: Bisect, NwtRph, Secant
        REAL :: Tol, X1, X2
!        
        INTERFACE
        SUBROUTINE Swap(x1, x2)
        IMPLICIT NONE
        REAL ,INTENT(IN OUT) :: x1, x2
        END SUBROUTINE Swap
        END INTERFACE
!
        PRINT *, 'This is Program >> P125 = Root finding demo'
!
!     Tell program where data for  READ *  is coming from
      OPEN(UNIT=5, FILE='P125.DAT')      ! UNIT=5 is the default input
!
        PRINT *, 'Program to demonstrate numerical root finding'
        PRINT *, 'F = x**3 - Exp(-x)    for example'
        PRINT *
        PRINT *, 'Enter tolerance for roots: '
        READ *, Tol
        Print *, Tol
        PRINT *


        PRINT *, 'Enter low limit, high limit: '
        READ *, X1, X2
        Print *, X1, X2
        PRINT *
        PRINT *, 'Bisection: ', Bisect(X1, X2, Tol)

        PRINT *, 'Enter estimated root: '
        READ *, X1
        Print *, X1
        PRINT *
        PRINT *, 'Newton: ', NwtRph(X1, Tol)

        PRINT *, 'Enter 1st, 2nd root estimates: '
        READ *, X1, X2
        Print *, X1, X2
        PRINT *
        PRINT *, 'Secant: ', Secant(X1, X2, Tol)

        STOP
        END PROGRAM Rooter



! SUPPORT ROUTINES

        REAL FUNCTION F(x)
!       The function of which a root (zero) is to be found.

        F = x**3 - Exp(-x)
        RETURN
        END FUNCTION F


        REAL FUNCTION FPrime(x)
!       The derivative of F.

        FPrime = 3*x*x + Exp(-x)
        RETURN
        END FUNCTION FPrime



        SUBROUTINE Swap(x1, x2)
!       Exchanges two real values x1 and x2.
        IMPLICIT NONE
        REAL ,INTENT(IN OUT) :: x1, x2
        REAL :: Temp
        Temp = x1
        x1 = x2
        x2 = Temp
        RETURN
        END SUBROUTINE Swap


! ALGORITHMS

        REAL FUNCTION Bisect(x_a, x_b, Tol)
!  Finds one real root of the function F(x)
!  on the interval [x_a, x_b].
        IMPLICIT NONE
        REAL ::         x_a, x_b, Tol,       &
                        x_Lo, x_Hi, x_New,   &
                        F_Lo, F_Hi, F_New


        x_Lo = x_a
        x_Hi = x_b
        F_Lo = F(x_Lo)
        F_Hi = F(x_Hi)

!       Start of main Bisection loop
   10   x_New = (x_Lo + x_Hi)/2.0
        F_New = F(x_New)

!       The point (X_New, F_New) replaces the
!       interval boundary with the same sign of F,
!       to ensure that the new interval still
!       contains a sign change.
        IF (F_Lo*F_New < 0.0) THEN
          x_Hi = x_New
          F_Hi = F_New
        ELSE
          x_Lo = x_New
          F_Lo = F_New
        END IF

!       Repeat loop if have not fulfilled
!       any convergence criterion
        IF (Abs(F_New) > Tol .AND.      &
          (x_Hi - x_Lo) > Tol) GO TO 10

        Bisect = X_New
        RETURN
        END FUNCTION Bisect


        REAL FUNCTION NwtRph(x0, Tol)
!  Finds one real root of the function F(x) using
!  an initial guess at the root, x0.
        IMPLICIT NONE
        REAL ::         x0, Tol,       &
                        x_New, x_Old,  &
                        F_x, Fp_x

        x_New = x0
        F_x = F(x0)

!       Main loop---iterate until one convergence
!       criterion met.
   10   x_Old = x_New

        F_x = F(x_Old)
        Fp_x = FPrime(x_Old)

        x_New = x_Old - F_x/Fp_x

!       Repeat loop if have not converged.
        IF (Abs(F_x) > Tol .AND.      &
            Abs(x_New - x_Old) > Tol) GO TO 10

        NwtRph = x_New
        RETURN
        END FUNCTION NwtRph


        REAL FUNCTION Secant(x_g1, x_g2, Tol)
!  Finds one real root of the function F(x) using
!  two initial guesses at the root, x_g1 and x_g2.
        IMPLICIT NONE
        REAL ::         x_g1, x_g2, Tol,   &
                        x0, x1, x_New,     &
                        F0, F1

        x0 = x_g1
        x1 = x_g2
        F0 = F(x0)
        F1 = F(x1)

!       Ensure that x1 is the better guess to start
        IF (Abs(F0) < Abs(F1)) THEN
          CALL Swap(x0, x1)
          CALL Swap(F0, F1)
        END IF

!       Start of main Secant loop

!       Estimate new x as the point on the x-axis
!       intersected by the line joining (x0, F0)
!       and (x1, F1)
   10   x_New = x1 - F1*(x1 - x0)/(F1 -F0)

!       Update both estimates, assuming new x is
!       better than the two previous guesses.
        x0 = x1
        F0 = F1
        x1 = x_New
        F1 = F(x1)

!       Repeat loop if have not converged.
        IF (Abs(F1) > Tol .AND.      &
            Abs(x1 - x0) > Tol) GO TO 10

        Secant = x1
        RETURN
        END FUNCTION Secant

C version

#include <stdio.h>
#include <math.h>


/* Program to demonstrate numerical root finding methods.  Starting
  from some initial guesses at a root (i.e. value of x such that
  F(x) = 0), these algorithms iteratively improve the estimate of
  the root until some convergence criterion is fulfilled.

  Three techniques are presented.  The Bisection method is easy to
  understand and safe, but converges slowly.  The Newton-Raphson
  method converges quickly, but requires evaluation of the derivative
  of F.  The Secant method converges nearly as quickly as Newton-
  Raphson, but does not require any derivative evaluations.
*/

#define MinTol 1.0E-10
#define TrueRoot 0.772883
#define MaxStep 20



double f(double x){
/* The function for which a root is to be found */
  return(x*x*x - exp(-x));
}


double f_prime(double x){
/* The derivative of f */
  return(3*x*x + exp(-x));
}


void swap(double *a, double *b){
/* Exchange two real variables */

  double temp;
  temp = *a;
  *a = *b;
  *b = temp;
}



double bisect(double x0, double x1, double tolerance){

/* Finds one real root of the function F(x) on the interval
  [X_Lo, X_Hi], which must contain a sign change in F (i.e.
  this will not work for roots of even multiplicity).

  Proceeds by halving the interval [X_Lo, X_Hi].  Depending
  on the sign of the function at the midpoint of the interval,
  either the low or high limit is discarded (in order to preserve
  the sign change in the interval).

  The algorithm terminates when either the size of the interval
  shrinks below Tolerance, or the magnitude of F at the midpoint
  of the interval is less than Tolerance. */

  double root;
  int i;

  for(i=1; i<=MaxStep; i++){
      root=(x0 + x1) /2;
      if(f(x0)*f(root) > 0.0)
        x0=root;
      else
        x1=root;
      if( (x1-root) < tolerance || fabs(f(root)) < tolerance) break;
      }
  return(root);
}


double newton_raphson(double x, double tolerance){
/* Finds one real root of the function F(x) using the Newton-
  Raphson method and an initial guess at the root, x0.

  While this method converges rapidly to the root, it does
  require the evaluation of the derivative of F, F'(x).

  The algorithm terminates when either the difference between
  new and old estimates of the root falls below Tolerance, or
  the magnitude of the function at the latest estimate is less
  than Tolerance.

  As a fail-safe, the algorithm is abandoned after MaxStep
  (a global constant) iterations. */

  double root;
  int i;

  for(i=1; i<=MaxStep; i++){
      root = x - (f(x) / f_prime(x));
      if( (root-x) < tolerance || fabs(f(root)) < tolerance) break;
      x=root;

      }
  return(root);
}


double secant(double x0, double x1, double tolerance){
/* Finds one real root of the function F(x) using the Secant
  method and two initial guesses at the root, x0 and x1.

  This method has similar convergence properties to Newton-
  Raphson, but does not require the evaluation of F'.

  The algorithm terminates when either the difference between
  new and old estimates of the root falls below Tolerance, or
  the magnitude of the function at the latest estimate is less
  than Tolerance.

  As a fail-safe, the algorithm is abandoned after MaxStep
  (a global constant) iterations.

  If you understand the Newton-Raphson method, can you see how
  the f(x0) * (x2-x1)/(f(x2) - f(x1)) term in this method replaces
  the f(x)/f_prime(x) ?  This secant method is actually approximating
  the slope, to avoid evaluating the derivative (which may at times
  be impossible to do.) */


  double root;
  int i;

  for(i=1; i<=MaxStep; i++){
      root = x1 - f(x0)*(x1-x0)/(f(x1)-f(x0));
      if( fabs(x1-x0) < tolerance || fabs(f(root)) < tolerance) break;
      x0 = x1;

      x1= root;

      }
  return(root);
}

main(){

  double x0, x1, tolerance, root;

  printf("Root-finding Program\n\n\n");
  printf(" This program finds the root of the function x^3 - e^(-x).  It\n");
  printf("will prompt you for two guesses, as well as the tolerance, and\n");
  printf("then it will use the bisection method, the secant method, and \n");
  printf("the Newton-Raphson method to find the root.  The actual root \n");
  printf("is approximately  x = 0.772883  .\n\n\n");
  printf("Enter first point value  x=");
  scanf("%lf",&x0);
  printf("Enter last point value  x=");
  scanf("%lf",&x1);
  printf("Enter Tolerance :");
  scanf("%lf",&tolerance);

  if(x0 > x1) swap(&x0, &x1);

  if( fabs(f(x0)) <= tolerance || fabs(f(x1)) <= tolerance)

      printf("One of the endpoints is a root.\n");

  else if( f(x0)*f(x1) > 0 )
      printf("There is no single root in that interval.\n");

  else if(tolerance < MinTol)

      printf("That is too small a tolerance.\n");

  else if( (x1 - x0) < tolerance )
      printf("The specified interval is smaller than the tolerance.\n");

  else{

  /* Approximate the root with bisection method */
      root=bisect(x0, x1, tolerance);
  /* If the root returned does not yield zero within tolerance,
     it means the function could not find the root with the allowed
     number of iterations. */
      if(f(root) > tolerance){
        printf("The bisection method could not find a root within\n");
        printf("tolerance with %d iterations.\n",MaxStep); }
      else{
        printf("\nBisection method yields;\n");
        printf("Root=%8.5lf True Root=%8.5lf Error=%8.5lf",
               root, TrueRoot, (root-TrueRoot) );  }

  /* Approximate the root with Newton-Raphson method */
      root=newton_raphson(x0, tolerance);
      if(f(root) > tolerance){
        printf("\nThe Newton-Raphson method could not find a root within\n");
        printf("tolerance with %d iterations.\n",MaxStep); }
      else{
        printf("\nNewton-Raphson method yields;\n");
        printf("Root=%8.5lf True Root=%8.5lf Error=%8.5lf",
               root, TrueRoot, (root-TrueRoot) );}

  /* Approximate the root with the secant method */
      root=secant(x0, x1, tolerance);
      if(f(root) > tolerance){
        printf("\nThe secant method could not find a root within\n");
        printf("tolerance with %d iterations.\n",MaxStep); }
      else{
        printf("\nSecant method yields;\n");
        printf("Root=%8.5lf True Root=%8.5lf Error=%8.5lf\n",
               root, TrueRoot, (root-TrueRoot) );  }

  } /* END the long if-else block */

}  /* END main() */


Pascal version

PROGRAM RootDemo;
{ Program to demonstrate numerical root finding methods.  Starting
  from some initial guesses at a root (i.e. value of x such that
  F(x) = 0), these algorithms iteratively improve the estimate of
  the root until some convergence criterion is fulfilled.

  Three techniques are presented.  The Bisection method is easy to
  understand and safe, but converges slowly.  The Newton-Raphson
  method converges quickly, but requires evaluation of the derivative
  of F.  The Secant method converges nearly as quickly as Newton-
  Raphson, but does not require any derivative evaluations.

  This program was compiled with Turbo Pascal V5.5, but should be
  compilable with Turbo Pascal versions 3.0 and above.  Deviations from
  standard Pascal are also minimal, particularly in the coding of the
  algorithms themselves. }


CONST
  N_Alg    =  3;                { Number of algorithms implemented }
  MinTol   = 1.0E-10;
  TrueRoot = 0.772883;          { Found by experiment!! }
  MaxStep  = 20;

TYPE
  { Record containing information specific to an algorithm }
  AlgRec = RECORD
             Title:     String[80];     { Identifying text string }
             X0, X1:    Real;           { Initial guess(es) at root }
           END;


VAR
  Tolerance,                    { Convergence criterion
                                  (allowed error in Root) }
  Root:         Real;           { Algorithm's estimate of root }
  Alg,
  Step:         Integer;        { Number of steps to get Root }
  Ch:           Char;
  AlgData:      ARRAY [1..N_Alg] OF AlgRec;



FUNCTION F(x: Real): Real;
{ The function of which a root (zero) is to be found. }

BEGIN
  F:= x*x*x - exp(-x);
END;  { FUNCTION F }



FUNCTION F_Prime(x: Real): Real;
{ The derivative of F. }

BEGIN
  F_Prime:= 3*x*x + exp(-x);
END;  { FUNCTION F_Prime }



PROCEDURE Swap(VAR x1, x2: Real);
{ Exchanges two real values x1 and x2. }

VAR
  Temp: Real;

BEGIN
  Temp:= x1;
  x1:= x2;
  x2:= Temp;
END;  { PROCEDURE Swap }



FUNCTION Bisect(
        X_Lo, X_Hi,
        Tolerance:      Real;
        VAR Step:       Integer): Real;
{ Finds one real root of the function F(x) on the interval
  [X_Lo, X_Hi], which must contain a sign change in F (i.e.
  this will not work for roots of even multiplicity).

  Proceeds by halving the interval [X_Lo, X_Hi].  Depending
  on the sign of the function at the midpoint of the interval,
  either the low or high limit is discarded (in order to preserve
  the sign change in the interval).

  The algorithm terminates when either the size of the interval
  shrinks below Tolerance, or the magnitude of F at the midpoint
  of the interval is less than Tolerance. }

VAR
  X_New,
  F_Lo, F_Hi,
  F_New:        Real;

BEGIN
  { Ensure proper ordering of initial interval. }
  IF X_Lo > X_Hi THEN
    Swap(X_Lo, X_Hi);

  Tolerance:= Abs(Tolerance);
  IF Tolerance < MinTol THEN
    Tolerance:= MinTol;


  Step:= 0;


  F_Lo:= F(X_Lo);

  F_Hi:= F(X_Hi);


  { First check whether one of the initial endpoints is a root. }
  IF Abs(F_Lo) < Tolerance THEN
    BEGIN
      Bisect:= X_Lo;

      Exit;
    END
  ELSE IF Abs(F_Hi) < Tolerance THEN
    BEGIN
      Bisect:= X_Hi;

      Exit;
    END
  { Complain if interval does not contain a change of sign of F. }
  ELSE IF NOT (F_Lo*F_Hi < 0) THEN
    BEGIN
      WriteLn('***WARNING (Bisect):');
      WriteLn('  No sign change of F on interval [',
        X_Lo:7:3, ',', X_Hi:7:3, '].');
      Step:= -1;

      Exit;
    END;


  REPEAT
    X_New:= (X_Lo + X_Hi)/2;
    F_New:= F(X_New);


    { The point (X_New, F_New) replaces the interval boundary
      with the same sign of F, to ensure that the new interval
      still contains a sign change. }
    IF F_Lo*F_New < 0 THEN
      BEGIN
        X_Hi:= X_New;

        F_Hi:= F_New;

      END
    ELSE
      BEGIN
        X_Lo:= X_New;

        F_Lo:= F_New;

      END;

    Step:= Succ(Step);

  UNTIL (Abs(F_New) < Tolerance) OR
        ((X_Hi - X_Lo) < Tolerance);

  Bisect:= X_New;

END;  { FUNCTION Bisect }



FUNCTION Newton_Raphson(
        x0, Tolerance:  Real;
        VAR Step:       Integer): Real;
{ Finds one real root of the function F(x) using the Newton-
  Raphson method and an initial guess at the root, x0.

  While this method converges rapidly to the root, it does
  require the evaluation of the derivative of F, F'(x).

  The algorithm terminates when either the difference between
  new and old estimates of the root falls below Tolerance, or
  the magnitude of the function at the latest estimate is less
  than Tolerance.

  As a fail-safe, the algorithm is abandoned after MaxStep
  (a global constant) iterations. }

VAR
  X_New,
  X_Old,
  F_x,
  Fp_x:         Real;

BEGIN
  Tolerance:= Abs(Tolerance);

  IF Tolerance < MinTol THEN
    Tolerance:= MinTol;


  Step:= 0;

  X_New:= x0;

  X_Old:= X_New + 2*Tolerance;
  F_x:= F(x0);


  WHILE (Step < MaxStep) AND
        (Abs(F_x) > Tolerance) AND
        (Abs(X_New - X_Old) > Tolerance) DO
    BEGIN
      X_Old:= X_New;

      F_x:= F(X_Old);
      Fp_x:= F_Prime(X_Old);

      IF Abs(Fp_x) < MinTol THEN
        BEGIN
          WriteLn('***WARNING (Newton_Raphson):');
          WriteLn('  |F''(', X_Old:7:3, ')| --> 0.');
          Step:= -1;
          Exit;
        END;

      X_New:= X_Old - F_x/Fp_x;
      Step:= Succ(Step);
    END;  { WHILE }

  IF Step >= MaxStep THEN
    BEGIN
      WriteLn('***WARNING (Newton_Raphson):');
      WriteLn('  Maximum of ', MaxStep:1, ' iterations exceeded.');
      Step:= -1;
    END
  ELSE
    Newton_Raphson:= X_New;
END;  { Newton_Raphson }



FUNCTION Secant(
        x0, x1, Tolerance:      Real;
        VAR Step:               Integer): Real;
{ Finds one real root of the function F(x) using the Secant
  method and two initial guesses at the root, x0 and x1.

  This method has similar convergence properties to Newton-
  Raphson, but does not require the evaluation of F'.

  The algorithm terminates when either the difference between
  new and old estimates of the root falls below Tolerance, or
  the magnitude of the function at the latest estimate is less
  than Tolerance.

  As a fail-safe, the algorithm is abandoned after MaxStep
  (a global constant) iterations. }

VAR
  x_New,
  F0, F1:       Real;

BEGIN
  Tolerance:= Abs(Tolerance);
  IF Tolerance < MinTol THEN
    Tolerance:= MinTol;


  { Complain if initial guesses are the same (within Tolerance). }
  IF Abs(x0 - x1) < Tolerance THEN
    BEGIN
      WriteLn('***WARNING (Secant):');
      WriteLn('  Initial guesses are not distinct.');
      Step:= -1;

      Exit;
    END;


  F0:= F(x0);

  F1:= F(x1);


  { Ensure that x0 is the better guess to start. }
  IF Abs(F0) < Abs(F1) THEN
    BEGIN
      Swap(x0, x1);
      Swap(F0, F1);
    END;

  { Check if x1 is a root }
  IF Abs(F1) < Tolerance THEN
    BEGIN
      Secant:= x1;

      Exit;
    END;

  Step:= 0;

  WHILE (Step < MaxStep) AND
        (Abs(F1) > Tolerance) AND
        (Abs(x1 - x0) > Tolerance) DO
    BEGIN
      F0:= F1 - F0;
      IF Abs(F0) < MinTol THEN
        BEGIN
          WriteLn('***WARNING (Secant):');
          WriteLn('  |F(', x1:7:3, ') - F(', x0:7:3,')| --> 0.');
          Step:= -1;
          Exit;
        END;

      { Estimate new x as the point on the x-axis intersected by
        the line joining (x0, F0) and (x1, F1) }
      x_New:= x1 - F1*(x1 - x0)/F0;

      { Update both estimates, assuming new x is better than the
        two previous guesses. }
      x0:= x1;
      F0:= F1;
      x1:= x_New;
      F1:= F(x1);

      Step:= Succ(Step);
    END;  { WHILE }

  IF Step >= MaxStep THEN
    BEGIN
      WriteLn('***WARNING (Secant):');
      WriteLn('  Maximum of ', MaxStep:1, ' iterations exceeded.');
      Step:= -1;
    END
  ELSE
    Secant:= x1;
END;  { Secant }



BEGIN  { Main }
  AlgData[1].Title:= 'Bisection';
  AlgData[2].Title:= 'Newton-Raphson';
  AlgData[3].Title:= 'Secant';

  REPEAT
    REPEAT
      Write('Enter tolerance for roots: ');
      ReadLn(Tolerance);
    UNTIL Tolerance > 0;

    FOR Alg:= 1 TO N_Alg DO
      WITH AlgData[Alg] DO
        BEGIN
          WriteLn;
          WriteLn(Title+' Method');

          CASE Alg OF
            1: BEGIN
                 Write('Enter low limit, high limit: ');
                 ReadLn(X0, X1);
               END;
            2: BEGIN
                 Write('Enter estimated root: ');
                 ReadLn(X0);
               END;
            3: BEGIN
                 Write('Enter 1st, 2nd root estimates: ');
                 ReadLn(X0, X1);
               END;
          END;  { CASE Alg }
        END;  { WITH AlgData[Alg] }


    WriteLn; WriteLn;
    WriteLn('Algorithm':15, 'Steps':8, 'Root':12, 'F(Root)':12,
        'True':12, 'Error':12);
    WriteLn;

    FOR Alg:= 1 TO N_Alg DO
      WITH AlgData[Alg] DO
        BEGIN
          CASE Alg OF
            1: Root:= Bisect(X0, X1, Tolerance, Step);
            2: Root:= Newton_Raphson(X0, Tolerance, Step);
            3: Root:= Secant(X0, X1, Tolerance, Step);
          END;  { CASE Alg }

          IF Step >= 0 THEN
            WriteLn(Title:15, Step:8, Root:12:8, F(Root):12:8,
                TrueRoot:12:8, (Root - TrueRoot):12:8);
        END;  { WITH AlgData[Alg] }

    WriteLn; WriteLn;
    Write('Another run? (y/[N]): ');
    ReadLn(Ch);
  UNTIL NOT ((Ch = 'Y') OR (Ch = 'y'));
END.

Last modified: 08/07/97