Métodos Numéricos 3: Raíces de ecuaciones: Métodos de Newton-Raphson y de la secante

Determinación de las raíces de una función.

 

Método de Newton-Raphson

Es otro método que se utiliza para calcular los ceros de una función real de variable real. Aunque no sea siempre el mejor método para un problema dado, su simplicidad formal y su rapidez de convergencia hacen que, con frecuencia, sea el primer algoritmo a considerar para esta tarea.

El método de Newton-Raphson se basa en el desarrollo de Taylor de la función cuya raíz se quiere calcular. Consideremos la ecuación \(f(x)=0\), y supongamos que posee una y sólo una solución \(\alpha\in\left[a,b\right]\). Partiendo de un punto \(x_{0}\) suficientemente cercano a dicha raíz, podemos escribir:

\[f\left(\alpha\right)=f(x_{0})+\left(\alpha-x_{0}\right)f^{\prime}(x_{0})+\frac{\left(\alpha-x_{0}\right)^{2}}{2}f^{\prime\prime}(x_{0}+\theta h)\]

con \(0<\theta<1\), \(h=\alpha-x_0\).

Suponiendo que \(f^{\prime}(x)\) no se anula en \([a,b]\), y que la diferencia \(\alpha-x_{0}\) es muy pequeña, el método de Newton-Raphson consiste en despreciar el sumando en \(\left(\alpha-x_{0}\right)^{2}\) del desarrollo anterior, quedándonos con la aproximación:

\[f\left(\alpha\right)\cong f(x_{0})+\left(\alpha-x_{0}\right)f^{\prime}(x_{0})\] Como \(\alpha\) es la solución de la ecuación \(f\left(x\right)=0\), se tiene que \(f(\alpha)\)=0 y por tanto, de la expresión anterior se sigue que:

\[f(x_{0})+\left(\alpha-x_{0}\right)f^{\prime}(x_{0})\cong 0\]

y despejando \(\alpha\) resulta:

\[\alpha\cong x_{0}-\frac{f(x_{0})}{f^{\prime}(x_{0})}=x_1\]

Así pues, si se parte de un valor \(x_0\) próximo a \(\alpha\), el valor \(x_1\) obtenido de esta forma proporciona un valor también próximo a la raíz \(\alpha\). Bajo determinadas condiciones (las veremos en un momento) ocurre que \(x_1\) está más próximo a \(\alpha\) que \(x_0\). En tales condiciones, como \(x_1\) es un valor más cercano a la solución \(\alpha\) que \(x_0\), podemos repetir el razonamiento anterior para acercarnos aún más a \(\alpha\), partiendo ahora de \(x_1\). Ello nos conduce a una segunda aproximación:

\[x_{2}=x_{1}-\frac{f(x_{1})}{f^{\prime}(x_{1})}\]

De la misma manera podemos obtener una nueva (y mejor) aproximación \(x_3\) a partir de \(x_2\) mediante:

\[x_{3}=x_{2}-\frac{f(x_{2})}{f^{\prime}(x_{2})}\]

El proceso puede seguir repitiéndose sucesivamente hasta encontrar un \(x_k\) lo suficientemente próximo a la raíz \(\alpha\).

El algoritmo de Newton-Raphson consiste entonces en partir de una aproximación inicial \(x_0\), y generar recursivamente la sucesión \(\{x_{i}\}_{i=1}^{n}\) con la fórmula recurrente:

\[x_{i+1}=x_{i}-\frac{f(x_{i})}{f^{\prime}(x_{i})}\]

El algoritmo parará en aquél valor \(i\) para el que la diferencia \(\left|x_{i+1}-x_{i}\right|\) (o bien la diferencia en términos relativos \(\left|\frac{x_{i+1}-x_{i}}{x_{i}}\right|\)) sea lo suficientemente pequeña.

 

Obviamente, para que este algoritmo funcione tienen que ocurrir dos cosas:

\[y=f(x_{0})+\left(x-x_{0}\right)f^{\prime}(x_{0})\]

corresponde s la recta tangente a \(f(x)\) en \(x_0\). Por tanto la solución \(x_1\) de la ecuación:

\[f(x_{0})+\left(x-x_{0}\right)f^{\prime}(x_{0}) = 0\]

es el punto de corte de la recta anterior con el eje de abcisas. La siguiente imagen (extraida de la entrada sobre el método de Newton en la Wikipedia) ilustra el funcionamiento del método:

 

 

Así pues, para que el método de Newton-Raphson converja, la función \(f(x)\) debe ser derivable en el intervalo considerado, y en dicho intervalo no debe tener puntos de inflexión, ni máximos ni mínimos. En la siguiente figura podemos apreciar, como aún partiendo de un punto cercano a la raíz buscada, en un caso el método converge y en el otro caso no:

 

 

El siguiente teorema muestra condiciones suficientes para la convergencia del método de Newton:

Teorema: (Condiciones de convergencia para el método de Newton-Raphson). Supongamos que \(f\in C^{2}([a,b])\) (f admite primera y segunda derivadas) y verifica:

  1. \(f(a)f(b)<0\), es decir, \(f(x)\) tiene una raíz en \((a,b)\).

  2. \(f^{\prime}(x)\neq0\;\;\;\; x\in\left[a,b\right]\). En otras palabras, la raíz es única.

  3. \(f^{\prime\prime}(x)\neq0\;\;\;\; x\in\left[a,b\right]\). Esto es, la función es cóncava o convexa, pero no tiene puntos de inflexión en el intervalo.

Entonces, existe una única raíz \(\alpha\) de \(f\) en \([a,b]\) y, la sucesión \(\{x_{n}\}\) generada por el método de Newton converge hacia \(\alpha\), para cualquier valor inicial \(x_{0}\in [a,b]\) que cumpla que:

\[f(x_{0})f^{\prime\prime}(x_{0})\geq0\].

 

Ejemplo: La función \(f(x)=x^{2}-x-\frac{1}{2}\) tiene una y sólo una raíz en \([-0.5,0]\). Comprobar si se cumplen las condiciones del teorema anterior. En caso afirmativo hallar la solución para las condiciones iniciales \(x_0=-\frac{1}{2}\), \(x_0=-\frac{2}{5}\) y \(x_0=-0.3\)

 

 

Algoritmo para el método de Newton-Raphson

La siguiente figura sintetiza el diagrama de flujo para el algoritmo de Newton-Raphson:

 

Implementación del método de Newton-Raphson.

Para implementar el método de bisección, los argumentos de entrada deben ser:

La llamada a la función podría ser de la forma:

>> newtonRaphson(f, fprima, x0, tol)

y debería obtenerse como resultado una raíz de \(f(x)\) próxima a \(x_0\). La implementación del algoritmo anterior puede llevarse a cabo de la forma siguiente (nótese que las lineas de comentario al inicio de la función son las que se mostrarán como ayuda si el usuario ejecuta help biseccion):

function [raiz, niter]=newtonRaphson(f,fprima,x0,tol,nmaxiter=10000)
% Resolución de la ecuación f(x)=0 mediante el algoritmo de Newton-Raphson.
% Argumentos:
% f: es la función cuya raiz se desea calcular (deberá definirse 
%      como función anónima, previamente o en la llamada a esta funcion).
% fprima: derivada de la función f, definida también como función anónima
% x0: Aproximación inicial de la solución.
% tol (tolerancia): Si abs(x1-x0)<tol el algoritmo se detiene.  
% nmaxiter: número máximo de iteraciones. Si se alcanza este valor sin encontrar
% la solución el algoritmo se detiene.
   if f(x0)==0
       raiz=x0;
   else
     niter=1;  
     do       
       fp=fprima(x0);
       if fp==0
         disp('ERROR: la derivada se anula y el método no puede proseguir')
         x1=[];
         break; % Se fuerza la salida del bucle do
       else
         delta=f(x0)/fprima(x0);
         x1=x0-delta;
         x0=x1;
         niter=niter+1;
        end%if
     until f(x1)==0 | abs(delta)<tol | niter>nmaxiter;
     raiz=x1;
     if niter>nmaxiter
       disp("Se ha alcanzado el número máximo de iteraciones sin lograr la convergencia");
       raiz=[];
      end% if niter... 
    end%if
end%function

 

Dado que a priori no se tiene garantía de que el método de Newton-Raphson converja, en esta implementación se ha añadido un contador de iteraciones. Por defecto, si tras 10.000 iteraciones no se ha encontrado la solución, el procedimiento se detiene y lanza un aviso.

Ejemplos de aplicación

>> f=@(x) exp(x)-x.^2;
 fp=@(x) exp(x)-2.*x;
 [a,niter]=newtonRaphson(f,fp,-1,0.00001)
a = -0.70347
niter =  5

 

Si partimos de una aproximación inicial peor el algoritmo converge pero necesita más iteraciones:

>> [a,niter]=newtonRaphson(f,fp,10,0.00001)
a = -0.70347
niter =  17

 

Si partimos de una aproximación aún más mala y fijamos el máximo de iteraciones en 1000, el algoritmo se detiene antes de llegar a la solución:

>> [a,niter]=newtonRaphson(f,fp,5000,0.00001,1000)
Se ha alcanzado el número máximo de iteraciones sin lograr la convergencia
a = [](0x0)
niter =  1001

 

 

Método de la secante

Es una variante del método de Newton-Raphson en la que el cálculo de la derivada en \(x_k\) se sustituye por una aproximación. Recordemos que la definición de derivada es:

\[f'\left(x\right)=\underset{\Delta\rightarrow0}{lim}\frac{f\left(x\right)-f\left(x-\Delta\right)}{\Delta}\] Cuando se dan las condiciones de convergencia del método de Newton ocurre que \(x_k-x_{k-1}\rightarrow 0\). En tal caso, llamando \(\Delta=x_k-x_{k-1}\), la derivada en \(x_k\) puede expresarse como:

\[f'\left(x_{k}\right)=\underset{x_{k-1}\rightarrow x_{k}}{lim}\frac{f\left(x_{k}\right)-f\left(x_{k-1}\right)}{x_{k}-x_{k-1}}\]

y cuando \(x_k\) y \(x_{k-1}\) están muy próximos entre sí:

\[f'\left(x_{k}\right)\cong\frac{f\left(x_{k}\right)-f\left(x_{k-1}\right)}{x_{k}-x_{k-1}}\]

El método de la secante es análogo al método de Newton-Raphson, salvo que en cada iteración la derivada se sustituye por esta aproximación, esto es:

\[x_{k+1}=x_{k}-\frac{f(x_{k})}{f^{\prime}(x_{k})}=x_{k}-\frac{f(x_{k})}{\frac{f\left(x_{k}\right)-f\left(x_{k-1}\right)}{x_{k}-x_{k-1}}}=x_{k}-\frac{\left(x_{k}-x_{k-1}\right)f\left(x_{k}\right)}{f\left(x_{k}\right)-f\left(x_{k-1}\right)}=\frac{x_{k-1}f\left(x_{k}\right)-x_{k}f\left(x_{k-1)}\right)}{f\left(x_{k}\right)-f\left(x_{k-1}\right)}\] Obviamente para poner en marcha el método hacen falta dos aproximaciones iniciales de la solución \(x_0\) y \(x_1\). Podemos entonces sintetizar el algoritmo de la secante como:

  1. Partir de dos aproximación iniciales \(x_0\) y \(x_1\).

  2. En cada paso generar: \[x_{k+1}=\frac{x_{k-1}f\left(x_{k}\right)-x_{k}f\left(x_{k-1)}\right)}{f\left(x_{k}\right)-f\left(x_{k-1}\right)}\]

  3. El algoritmo para cuando la diferencia entre dos valores sucesivos \(\left|x_{k+1}-x_{k}\right|\) (o bien la diferencia en términos relativos \(\left|\frac{x_{k+1}-x_{k}}{x_{k}}\right|\)) sea lo suficientemente pequeña.

 

Dado que se está utilizando una aproximación de la derivada, este algoritmo converge más lentamente que el de Newton-Raphson. Modificando el programa que hicimos anteriormente para el algoritmo de bisección, podemos construir la siguiente función para aplicar el algoritmo de la secante:  

function [raiz, niter]=secante(f,x0,x1,tol,nmaxiter=10000)
% Resolución de la ecuación f(x)=0 mediante el algoritmo de Newton-Raphson.
% Argumentos:
% f: es la función cuya raiz se desea calcular (deberá definirse 
%      como función anónima, previamente o en la llamada a esta funcion).
% x0: Aproximación inicial de la solución.
% x1: Otra aproximación inicial de la solución.
% tol (tolerancia): Si abs(x1-x0)<tol el algoritmo se detiene.  
% nmaxiter: número máximo de iteraciones. Si se alcanza este valor sin encontrar
% la solución el algoritmo se detiene.
   if f(x0)==0
       raiz=x0;
   elseif f(x1)==0
       raiz=x1;
   else
     niter=1;  
     do       
       denom=f(x1)-f(x0);
       if denom==0
         disp('ERROR: la aproximacion de la derivada se anula y el método no puede proseguir')
         x1=[];
         break; % Se fuerza la salida del bucle do
       else
         delta=x1-x0;
         x1_anterior=x1;
         x1=(x0*f(x1)-x1*f(x0))/denom;
         x0=x1_anterior;
         niter=niter+1;
        end%if
     until f(x1)==0 | abs(delta)<tol | niter>nmaxiter;
     raiz=x1;
     if niter>nmaxiter
       disp("Se ha alcanzado el número máximo de iteraciones sin lograr la convergencia");
       raiz=[];
      end% if niter... 
    end%if
end%function

 

Al igual que hicimos con el algoritmo de bisección, dado que a priori no se tiene garantía de que el método de la secante converja, hemos añadido un contador de iteraciones. Por defecto, si tras 10.000 iteraciones no se ha encontrado la solución, el procedimiento se detiene y lanza un aviso.

 

Ejemplo

>> f=@(x) exp(x)-x.^2;
 [raiz,numiter]=secante(f,-1,0,0.00001)
raiz = -0.70347
numiter =  8

 

En matlab, la función fzero() utiliza el algoritmo de bisección cuando se proporciona un intervalo [a,b] donde buscar la solución, o el algoritmo de la secante cuando se proporciona una solución inicial x0.

 

 

Método de Newton Raphson para sistemas de ecuaciones no lineales

El método de Newton-Raphson puede generalizarse para resolver sistemas de ecuaciones no lineales como el siguiente:

\[\begin{cases} x^{2}+y^{2}-5 & =0\\ xe^{y}-e^{2x} & =0 \end{cases}\]

Este sistema puede expresarse también de la forma \(f\left(\boldsymbol{v}\right)=\boldsymbol{0}\), siendo \(\boldsymbol{v}=(x,y)\), y \(f(\boldsymbol{v})\) la función vectorial:

\[f\left(\boldsymbol{v}\right)=f\left(x,y\right)=(f_1\left(x,y\right),f_2\left(x,y\right))=\left(x^{2}+y^{2}-5,\,\,xe^{y}-e^{2x}\right)\]

El método de Newton-Raphson para funciones vectoriales sigue el mismo esquema que para funciones de variable real; se trata de ir generando aproximaciones sucesivas de la forma:

\[x_{i+1}=x_{i}-\frac{f(x_{i})}{f^{\prime}(x_{i})}\] con la diferencia (notable) de que la derivada \(f^{\prime}(x_i)\) del denominador debe sustituirse por el jacobiano de la función \(f(x)\). Si expresamos la función \(f\) de la forma:

\[f\left(\boldsymbol{v}\right)=\left(f_{1}\left(\boldsymbol{v}\right),f_{1}\left(\boldsymbol{v}\right),\dots,f_{m}\left(\boldsymbol{v}\right)\right)=\]

\[=\left(f_{1}\left(v_{\text{1},}v_{2},\dots,v_{n}\right),f_{2}\left(v_{\text{1},}v_{2},\dots,v_{n}\right),\dots,f_{m}\left(v_{\text{1},}v_{2},\dots,v_{n}\right)\right)\] su jacobiano es la matriz:

\[\left(\frac{\partial f_{i}}{\partial v_{j}}\right)_{ij}\]

formada por las derivadas parciales de las \(f_i\) con respecto a cada una de las \(v_j\). En nuestro ejemplo anterior en que \(f\left(\boldsymbol{v}\right)=f\left(x,y\right)=\left(x^{2}+y^{2}-5,\,\,xe^{y}-e^{2x}\right)\), el jacobiano sería la matriz:

\[J\left(x,y\right)=\left(\begin{array}{cc} 2x & 2y\\ e^{y}-2e^{2x} & xe^{y} \end{array}\right)\]

En la relación de recurrencia del método de Newton-Raphson debemos sustituir \(f'(x_i)\) por el jacobiano; ahora bien, como el jacobiano es una matriz y \(f'(x_i)\) está en el denominador, en realidad la expresión:

\[\frac{f(x_{i})}{f^{\prime}(x_{i})}\]

se sustituye por:

\[\left(J\left(\boldsymbol{v}\right)\right)^{-1}f\left(\boldsymbol{v}\right)\] de tal forma que la relación de recurrencia (vectorial) quedaría de la forma:

\[\boldsymbol{v}_{k+1}=\boldsymbol{v}_{k}-\left(J\left(\boldsymbol{v}_{k}\right)\right)^{-1}f\left(\boldsymbol{v}_{k}\right)\]

El método de Newton-Raphson para resolver sistemas de ecuaciones no lineales sería, pues, análogo al método ya visto para resolver una ecuación, con la única diferencia de que la función cuya raíz se va a encontrar es una función vectorial, y la derivada es su jacobiano. Tanto la función como su jacobiano deben ser definidos explícitamente antes de poner en marcha el algoritmo.

En Octave/matlab, la implementación de este método sería la siguiente:  

function [raiz, niter]=newtonRaphsonMulti(f,J,x0,tol,nmaxiter=10000)
% Resolución de la ecuación f(x)=0 mediante el algoritmo de Newton-Raphson
% para sistemas de ecuaciones.
% Argumentos:
% f: es la función cuya raiz se desea calcular (deberá definirse 
%      como función anónima, previamente o en la llamada a esta funcion).
% J: Jacobiano de la función f, definida también como función anónima
% x0: Aproximación inicial de la solución.
% tol (tolerancia): Si abs(x1-x0)<tol el algoritmo se detiene.  
% nmaxiter: número máximo de iteraciones. Si se alcanza este valor sin encontrar
% la solución el algoritmo se detiene.
% f y J son funciones vectoriales; x0 es un vector
   if f(x0)*f(x0)'==0
       raiz=x0;
   else
     niter=1;  
     do       
       Jc=J(x0);
       if det(Jc)==0
         disp('ERROR: la derivada se anula y el método no puede proseguir')
         x1=[];
         break; % Se fuerza la salida del bucle do
       else
         delta=Jc\f(x0)';
         x1=x0-delta';
         x0=x1;
         niter=niter+1;
        end%if
     until f(x1)==0 | abs(delta'*delta)<tol | niter>nmaxiter;
     raiz=x1;
     if niter>nmaxiter
       disp("Se ha alcanzado el número máximo de iteraciones sin lograr la convergencia");
       raiz=[];
      end% if niter... 
    end%if
end%function

 

Para resolver la ecuación planteada al inicio de esta sección definimos primero la función:

>> f=@(x) [x(1)^2+x(2)^2-5, x(1)*exp(x(2))-exp(2*x(1))];

Su jacobiano:

>> J =@(x) [2 * x(1), 2 * x(2); exp(x (2)) - 2 * exp(2 * x (1)), x(1) * exp(x (2))];

y podemos ejecutar el algoritmo de Newton-Raphson, utilizando como aproximación inicial el vector \(x_0=[1,1]\) mediante:

>> newtonRaphsonMulti(f,J,[1,1],0.0001)
ans =

   1.0000   2.0000

Matlab tiene incorporado el algoritmo de Newton-Raphson en la función fsolve. Esta función resuelve el problema \(f(x)=0\) siendo \(x\) un vector de dimensión \(n\), \(f\) una función vectorial que devuelve como salida un vector también de dimensión \(n\), y \(0\) un vector de ceros de dimensión \(n\). Para utilizar el algoritmo de Newton-Raphson es además necesario que al definir la función se defina también su jacobiano. Así para resolver el problema anterior con fsolve creamos un archivo .m con una función que proporciona como salida tanto el valor de \(f\) como el de su jacobiano \(J\):

>> function [f J]=sistemaEc(x)
   f=[x(1)^2+x(2)^2-5, x(1)*exp(x(2))-exp(2*x(1))];
   J = [2 * x(1), 2 * x(2); exp(x (2)) - 2 * exp(2 * x (1)), x(1) * exp(x (2))];
 end

Por defecto, matlab resuelve el problema utilizando el algoritmo de la secante (sustituyendo el jacobiano por una aproximación suya). Si queremos que utilice el jacobiano (si lo empleamos el algoritmo gana en velocidad y precisión) hemos de indicarlo poniendo la opción “SpecifyObjectiveGradient” a ‘true’.

En nuestro ejemplo, para resolver el sistema llamamos a la función fsolve del siguiente modo:

>> options = optimoptions('fsolve','SpecifyObjectiveGradient',true);
 fsolve(@sistemaEc, [1,1],options)
ans =

   1.0000   2.0000

 

Si no se especifica ninguna opción, el sistema se resuelve por el método de la secante (y no sería necesario incluir la definición del jacobiano en el archivo .m que contiene a la función \(f\)):

>> fsolve(@sistemaEc, [1,1])
ans =

   1.0000   2.0000


 

Ejercicio: utiliza matlab para resolver el sistema de ecuaciones:

\[\begin{cases} e^{x}-2y & =-1\\ x^{2}+sen\left(y\pi\right) & =0 \end{cases}\]