Roots of the equation f(x) = 0
(f and x are scalars)

The solutions for this case of root-finding are typified by the use of Newton's method or the bisection method. However, in MATLAB, your first line of attack for robust root-finding should use the built-in function fzero. The on-line information is fairly extensive and the focus here is on outlining a simple method for using fzero that leads to a minimal number of problems and execution errors.


[Back to main page] [Back to numerics page]

To use fzero, you need to know how to write functions in MATLAB.

As an illustration, consider the following problem:

Find the root(s) of f(x) = ae-bx - cx = 0 where, for example, a = 1, b = 2 and c = 3.

We'll go thru the basic use of fzero and then add on some refinements. Finally, the use of anonymous functions will be reviewed to bring your use of fzero into the new century. In any case, you should be sure to read the on-line help on fzero to make best use of its abilities.

Basic approach

  1. Write a MATLAB function that evaluates the function whose root(s) you seek. Your function should have a single input and a single output. Be sure to include all parameters needed for proper evaluation of the expression within the MATLAB function.

    For the example problem, the Mfile would be

       function y = rootex(x)
       %ROOTEX  Example function for root-finding
    
       % Parameters
       a = 1;   b = 2;  c = 3;
       
       % Evaluate function: y = f(x).  If x is a
       % root, y will be 0.
       y = a*exp(-b*x) - c*x;
       

    (To get the most of of this procedure, you should start up MATLAB and create the function Mfile just given. If you open a new editor window, you can copy and paste from your browser into MATLAB to save some typing.)

    Hint: If it is at all possible, make sure your function returns a variable (y) that is the same size as the input (x). While fzero does not require this, the next step will be much easier if you do so.

  2. Establish a reasonable range of x-values that you think should contain the root. If you are seeking multiple roots, you need to run fzero separately for each root. Be sure to restrict the search range so that only one root can be found (this may not be a trivial exercise!).

    The better the estimate of the interval that contains the root, the fewer problems you will have with fzero finding an answer.

    For the example problem, the interval that contains the root will depend on the parameters (a, b and c). One way to make a reasonable estimate of the interval is to plot the function and look for places that the curve passes through zero. If your MATLAB function was written to return an vector when provided with a vector, use this feature to your advantage. For example,

       x = linspace(0,1);     % intial guess for the interval
       y = rootex(x);         % f(x) in the interval
       plot(x,y), grid        % see how we did
       

    (If you are running MATLAB, copy and paste the lines just given into the command window to see the plot). The guess was "lucky" and indeed the function changes sign in the interval. If the guess was not so fortuitous, I would have changed the interval and tried again.

  3. Use fzero with the following syntax:
       r = fzero(fname,[lo hi])
      

    where fname is a string containing the name of the function Mfile (without the .m extension) and lo and hi define the interval which contains the root (which you should have determined in Step 2).

    Note that the first argument (fname) must be a string variable . There are two ways to ensure that this is so. For the example problem,

    Version 1: Use a variable.

       fname = 'rootex'     % the function Mfile name
       r = fzero(fname,[0 1])
      

    Version 2: Enter the name directly.

       r = fzero('rootex',[0 1])
      

    In the first version, a variable called fname was assigned the string 'rootex' while in the second, 'rootex' was used directly as an input. See also the use of function handles, presented below.

    When I ran this example (using either version), the root I found was 0.2163... .

Extensions

Controlling accuracy

There is an optional third input you can supply to fzero to set the accuracy of the final result. For instance, using the example problem from above,

   r = fzero('rootex',[0 1],1e-10)
overrides the default accuracy (1e-6) and tries to get the root accurate to 10 significant figures (roughly). More recent versions of MATLAB let you use the struct created by optimset in place of the single value used here for accuracy control. See the on-line help for what is available here.

Changing parameters

Very often, the function you are seeking the roots of comes from a physical problem and so there may be many parameters that change when the basis of the problem changes. For example:

The syntax of fzero allows you to pass extra input arguments to your Mfile and hence releases you from the requirement that your function have a single input argument.

For the example problem, the Mfile can be modified so that the parametes are in the input argument list.

   function y = rootex2(x,a,b,c)
   %ROOTEX2  Modified example function for root-finding

   % The parameters are now in the input list
   
   % Evaluate function: y = f(x).  If x is a
   % root, y will be 0.
   y = a*exp(-b*x) - c*x;
   

Then, fzero can be used as

   r = fzero('rootex2',[0 1],1e-10,1,2,3)

or, in a more vebose form,

   a = 1; b = 2; c = 3;
   r = fzero('rootex2',[0 1],1e-10,1,2,3)

to reproduce the first example (root at 0.2163) and as

   r = fzero('rootex2',[0 1],1e-10,3,2,1)

to examine a different set of parameters (a = 3, b = 2 and c = 1, in this case which leads to a root of 0.7162).

Note: You must explicitly include the accuracy argument when you ask fzero to pass parameters to your function.

Using anonymous functions

The introduction of function handles in MATLAB is a good thing and, once you get used to this new data type and its associated syntax, you find yourself with much more flexibility in the development of code. Getting used to function handles, though, can take work and a good place to see the utility of function handles is to see them in action.

To re-do the first problem on this page, we could first "capture" the function rootex in a function handle via the @ operator

   f1 = @ rootex

and then send that handle to fzero to find the root via

   r = fzero(f1,[0 1])

Granted there is not much difference in passing the name of the file versus passing a function handle derived from the name of the file but there is more than meets the eye here. The function handle provides a complete picture of the function at the time the handle was created and gives you a way, then, of doing things with that instance of the file that you cannot do with just a string of characters. For example, you can send the handle off to anywhere and not worry about where the file is that lead to the handle, a useful feature for more complex programming settings.

The problem involving the function rootex2 can better illustrate the utility of function handles. In the example, the name of the function and the list of parameters that were meant to be active when that function was used in fzero were located in two parts of the input list. No big deal (we got an answer, didn't we?) except that if you take a quick look at the command, which values in the input are parameters to rootex2 and which are other inputs to fzero is not easy to discern. Function handles can be used to make it clear what inputs belong to which function.

Since we are anticipating that there are parameters to provide to rootex2, let's give them values first (and be sure to read the warning, below)

   a = 1;  b = 2;  c = 3;

and then create the function handle via

   f2 = @(x) rootex2(x,a,b,c)

Important! Note that we defined the "parameters" (a, b and c here) before we created the function handle. The @ operator, in creating the anonymous function, grabs values of parameters (by name) from the current workspace. If the variables are not present, nothing gets included and that would lead to an annoying error. If you want to change the parameters, you need to make those changes and then re-create the function handle.

In his use mode, the @ operator creates an "anonymous function". The key difference between the handles f1 and f2 is the anonymous function syntax distinguishes between the essential inputs to the function captured in the handle (inputs that fzero in this case will make changes to) and parametric inputs to that function (inputs that are simply hauled around and used without change when the function is evaluated).

I can't say that it's very easy to look at the syntax for the definition of an anonymous function and grasp what it does. However, one way to read an anonymous-function definition is

  h = @(essential variable list) f(essential variable list,parameter variable list)

Variables in the essential list must be provided by the function that uses the function handle while the variables in the parameter list will be set once (at handle creation time) and simply provided every time the function contained in the function handle is evaluated.

Continuing, the root of the function for the set of parameters listed above is computed via

   r = fzero(f2,[0 1])

Again, the difference in syntax between using a function handle to an anonymous function and just the Mfile name with parameters tacked on to the end of the call to fzero is not one of substance (they both work!) but one of organization and, in more complex settings, flexibility. By using function handles (and anonymous functions, in particular), you make a cleaner and clearer division between the attributes of the function doing the operating (fzero in this instance) and the function being operated upon (rootex2 in this instance).


[Back to main page] [Back to numerics page]
Comments? Contact Jim Maneval at maneval@bucknell.edu