miércoles, 14 de noviembre de 2012

Método de Gauss-Seidel

Los métodos iterativos o aproximados proveen una alternativa aceptable a los métodos de pivoteo como el método de Gauss y Gauss-Jordan. En el caso que les expongo en esta ocasión, el método de Gauss-Seidel emplea un enfoque similar al utilizado por los métodos para obtener las raíces de ecuaciones simples lineales o no lineales.

Dichos planteamientos se fundamentan en suponer un valor inicial de la raíz y luego utilizar un método sistemático para obtener una aproximación más precisa. En este caso extenderemos este concepto a sistemas de ecuaciones lineales.

El método de Gauss-Seidel, es el método iterativo más comúnmente utilizado para obtener la solución de sistemas de ecuaciones  lineales, Supongamos que se nos da en conjunto de $n$ ecuaciones con $n$ incógnitas:

$$[A]\{x\}=\{b\}$$
en donde:

\begin{eqnarray}
A&=&\left [
\begin{array}{ccccccc}
a_{11}    &a_{12}   &a_{13} &+&\cdots&+&a_{1n}\\
a_{21}   &a_{22}  &a_{23} &+&\cdots&+&a_{1n}\\
\vdots      & \vdots   &  \vdots & \vdots  & \vdots  & \vdots \\
a_{n1}   &a_{n2}  &a_{n3}&+&\cdots&+&a_{nn}
\end{array}
\right ]
\end{eqnarray}

\begin{eqnarray}
b&=&\left \{
\begin{array}{c}
b_1\\b_2\\\vdots\\b_n
\end{array}
\right \}
\end{eqnarray}



Si suponemos que los elementos de la diagonal principal son distintos de cero y  $n=3$, el sistemas se pude resolver de la siguiente forma:

\begin{eqnarray}
x_{1}&=&\frac{b_1-a_{12}x_2-a_{13}x_3}{a_{11}} \label{ec2}\\
x_{2}&=&\frac{b_2-a_{21}x_1-a_{23}x_3}{a_{22}} \label{ec1}\\
x_{3}&=&\frac{b_3-a_{31}x_1-a_{32}x_2}{a_{33}}\label{ec3}
\end{eqnarray}

Ahora es posible obtener la solución de los sistemas de ecuaciones presentados en \eqref{ec2}, \eqref{ec1} y \eqref{ec3} a partir de un valor inicial $x$, el cual representa un vector con los valores iniciales para $x_1$, $x_2$ y $x_3$. Lo que se acostumbra a hacer para fijar estos valores iniciales es asumirlos todos iguales a $0$, de esta forma se remplaza $x_2=0$ y $x_3=0$ en la ecuación \eqref{ec2}, obteniéndose de esta manera el primer valor para $x_1$. Luego se reemplaza este nuevo valor  de $x_1$ y el valor  $x_3=0$ en la ecuación \eqref{ec2} obteniéndose así el nuevo valor de $x_2$. Y por último los nuevos valores de $x_1$ y $x_2$ se reemplazan en la ecuación \eqref{ec3} y se obtiene el nuevo valor estimado de $x_3$. El método iterativo se repite hasta alcanzar una aproximación lo suficientemente precisa.

Como criterio para verificar que el método converge a una solución se utiliza el error relativo, esto es:
\begin{equation}
|\epsilon_{r,i}|=\left | \frac{x_i^j-x_i^{j-1}}{x_i^j}\right |
\end{equation}
Para todas las $i$, en donde $j$ y $j-1$ son las iteraciones actuales y previas respectivamente.

Ahora volvamos al caso general en el que se cuenten con $n$ ecuaciones y $n$ incógnitas. ¿Cómo podemos determinar una expresión general para evaluar los valores de $x$, para de esta forma realizar un algoritmo que luego nos permita generar un programa?. Veamos.

Podemos escribir un conjunto de expresiones como las siguientes:
\begin{eqnarray}
x_i&=&\frac{b_i-\sum_{j=i+1}^n a_{ij}x_j}{a_{ii}}& i=1 \label{ec4}\\
x_i&=&\frac{b_i-\sum_{j=i+1}^n a_{ij}x_j-\sum_{j=1}^{i-1} a_{ij}x_j}{a_{ii}}& i=2,3,..,n-1 \label{ec5}\\
x_i&=&\frac{b_i-\sum_{j=1}^{n-1} a_{ij}x_j}{a_{ii}}& i=n \label{ec6}
\end{eqnarray}

De esta forma se nos es fácil implementar el método en un lenguaje de programación. verifiquemos  que sucede con las ecuaciones \eqref{ec4}, \eqref{ec5} y \eqref{ec6} para $n=3$.

De la ecuación \eqref{ec4} tenemos:
\begin{eqnarray}
x_1&=&\frac{b_1-\sum_{j=2}^3 a_{1j}x_j}{a_{11}} \nonumber\\
x_1&=&\frac{b_1-a_{12}x_{2}+a_{13}x_3}{a_{11}} \nonumber
\end{eqnarray}
De la ecuación \eqref{ec5}:
\begin{eqnarray}
x_2&=&\frac{b_2-\sum_{j=3}^3 a_{2j}x_j-\sum_{j=1}^1 a_{2j}x_j}{a_{22}}\nonumber\\
x_2&=&\frac{b_2-a_{23}x_3-a_{21}x_1}{a_{22}}\nonumber
\end{eqnarray}
y de la ecuación \eqref{ec6}:
\begin{eqnarray}
x_3&=&\frac{b_3-\sum_{j=1}^2 a_{3j}x_j}{a_{33}}\nonumber\\
x_3&=&\frac{b_3-a_{31}x_1-a_{32}x_2}{a_{33}}
\end{eqnarray}
Adicionalmente recordemos que  los elementos de la diagonal principal deben ser distintos de cero para evitar una división por cero. De modo que se debe tener esto en cuenta a la hora de especificar la matriz de coeficientes $A$.

Cabe mencionar que al ser un método numérico, pudiera darse el caso que el mismo no convergiera. De tal forma que para garantizar una convergencia la matriz  $A$ debe cumplir lo siguiente:


Lo cual quiere decir que el elemento perteneciente a la diagonal principal en cada ecuación debe ser mayor que la suma del resto de elementos de la misma ecuación. De esta forma la convergencia se garantiza si se cumple la condición, aunque pueden existir algunos casos en que el método converja sin cumplir con ella. Los sistemas que cumplen con la condición antes mencionada son conocidos como diagonalmente dominantes.

Iteración de Jacobi
Como en el caso del método de Gauss-Seidel cada valor de $x_i$ que se calcula es utilizado  inmediatamente para calcular el valor siguiente, de tal forma que si la solución es convergente se utilizará la mejor combinación posible de los valores de $x_i$. Pero también existe otra forma de hacer esto, y es primero reemplazar cada uno de  los valores de $x_i$ en las ecuaciones \eqref{ec4}, \eqref{ec5} y \eqref{ec6}, obteniendo otro nuevo conjunto de valores $x_i$, este es el planteamiento conocido como Iteración de Jacobi

Veamos un ejemplo utilizando el método de Gauss-Seidel. Obtener la solución del siguiente sistema de ecuaciones:
\begin{eqnarray}
x_1&+&10x_2&+&4x_3&-&2x_4&=&2 \nonumber\\
8x_1&&&-&10x_3&+&2x_4&=&-7 \nonumber\\
8x_1&+&3x_2&+&x_3&+&17x_4 &=&8\nonumber\\
11x_1&+&7x_2&-&3x_3&+&2x_4&=&12 \nonumber
\end{eqnarray}

Si creamos una función llamada en este caso gseidel.m en matlab, tendríamos lo siguiente:


>> A=[1 10 4 -2;8  0 -10 2;8 3 1 17;11 7 -3 2];
>> b=[2 ;-7 ;8 ;12];

>> x=gseidel(A,b,0.001,300)

x =

    2.9129
   -1.4015
    2.8661
   -0.8215

De tal forma que la solución es:

\begin{eqnarray}
x_1&=&2.9129 \nonumber\\
x_2&=&-1.4015 \nonumber\\
x_3&=&2.8661\nonumber\\
x_4&=&-0.8215 \nonumber
\end{eqnarray}




Verificamos en Matlab:



A*x

ans =

    2.0049
   -7.0011
    8.0000
   11.9898

De esta forma podemos ver que el método converge. A continuación les dejo la función implementada en Matlab
Función Gauss-Seidel

Función Gauss-Seidel


%Desarrollada por: Jorge De La Cruz
%Para un sistema de la forma 'Ax=b'
%imax: Número máxima de iteraciones en caso de
%que el método no converja
%tol: tolerancia deseada
function x=gseidel(A,b,tol,imax)
n=max(size(A));         % Obtenemos de la matriz A
                        % el número de ecuaiones
                        % y por ende de incognitas.
c=diag(A);              % Almacenamos en 'c' los elementos
                        % de la diagonal principal.
d=1;
i=1;
while (d~=0)&&(i<n)     % revisamos si en la diagonal principal 'c'
    d=c(i,1)*c(i+1,1);  % existe algún elemento igual a 0.
    i=i+1;              % En caso de que lo haya
end                     % salir del bucle y d=0.

while d==0              % Si se ha detectado algún cero en la
    B(1,:)=A(n,:);      % diagonal principal se entra en este bucle
    U(1,1)=b(n,1);      % para así cambiar las filas. Se alternarán
    for i=2:n           % las filas hasta que todos los elementos
        B(i,:)=A(i-1,:);% de la diagonal principal
        U(i,1)=b(i-1,1);% sean distintos de cero.
    end                 % Todo esto para evitar una división
    A=B;                % por cero. Aunque se recomienda
    b=U;                % siempre ordenar las ecuaciones apriori.
    c=diag(A);
    d=1;i=1;
    while (d~=0)&&(i<n)
        d=c(i,1)*c(i+1,1);
        i=i+1;
    end
end

% Se selecciona un vector X(n,1) sólo para evaluar
% el error en la primera iteración.
for i=1:n
    X(i,1)=b(i,1)/A(i,i);
end

% se sespecifica el vector x(n,1) con valores iguales a cero
% para iniciar las iteraciones.
x=zeros(n,1);

err=tol+1;
iter=1;

% Se inician las iteraciones
while (tol<err)&&(iter<imax)
    for i=1:n
        if i==1
            sum=0;
            for j=i+1:n
                sum=A(i,j)*x(j,1)+sum;
            end
            x(i,1)=(b(i,1)-sum)/A(i,i);
        end
        if i==n
            sum=0;
            for j=i-1:-1:1
                sum=A(i,j)*x(j,1)+sum;
            end
            x(i,1)=(b(i,1)-sum)/A(i,i);
        end
        if 2<=i<=n-1
            sum1=0;
            for j=i+1:n
                sum1=A(i,j)*x(j,1)+sum1;
            end
            sum2=0;
            for j=i-1:-1:1
                sum2=A(i,j)*x(j,1)+sum2;
            end
            x(i,1)=(b(i,1)-sum1-sum2)/A(i,i);
        end
    end
    err=abs((x-X)./x);
    err=max(err);
    X=x;
    iter=iter+1;
end

Licencia de Creative Commons
Método de Gauss-Seidel by Jorge De La Cruz is licensed under a Creative Commons Reconocimiento 3.0 Unported License.

lunes, 12 de noviembre de 2012

Método de la Secante

Uno de los problemas de la implementación del método de Newton-Raphson es el de la evaluación de la derivada. Puede que esto no sea un inconveniente para los polinomios y algunas otras funciones, pero existen algunas otras funciones para las cuales la derivada puede ser difícil de evaluar. Además también pudiera darse el caso en que en realidad no contemos con una función como tal sino que solo dispongamos de un conjunto de puntos $(x_i,f(x_i))$ de tal manera que tampoco sería posible obtener de forma analítica la derivada de nuestra función objetivo.Para casos como estos, la derivada su puede aproximar como una diferencia dividida finita regresiva. La derivada se puede aproximar entonces de la siguiente forma:

$$f'(x_i) \cong \frac{f(x_{i-1})-f(x_i)}{x_{i-1}-x_i} \label{ec1}$$


Esta aproximación se puede sustituir en la ecuación empleada en el método de Newton-Raphson:

$$x_{i+1}=x_i-\frac{f(x_i)}{f'(x_i)} \label{ec2}$$

Sustituyendo ahora \eqref{ec1} en \eqref{ec2}, obtenemos:

$$x_{i+1}=x_i-\frac{f(x_i)(x_{i-1}-x_i)}{f(x_{i-1})-f(x_i)} \label{ec3}$$

De esta forma la ecuación \eqref{ec3} es la formula para el Método de la Secante. Nótese que en este caso a diferencia del método de Newton-Raphson es necesario dos puntos para poder iniciar el método.

Figura: Ilustración de cómo el Método de la Secante se aproxima a la raiz.



Método de la Secante

Método de la Secante


%///////////////////////////////////////////////////
%//          Función Secante                     ///
%//        Desarrollada por Jorge De La Cruz     ///
%/                                              ///
%//////////////////////////////////////////////////
function xr=secante(fun,x0,x1,tol,imax)
x=[x0 x1];
f=eval(fun);
err=tol+1;
n=1;
while (err>tol)&&(n<imax)
    xr=x(1,2)-f(1,2)*(x(1,1)-x(1,2))/(f(1,1)-f(1,2));
    err=abs((xr-x(1,2))/xr);
    error=err*100;
    x(1,1)=x(1,2);
    x(1,2)=xr;
    f=eval(fun);
    fprintf('Iteración: %d, Error: %f, Raiz: %f\n',n,error,xr)
    n=n+1;
end



Licencia de Creative Commons
Método de la Secante by Jorge De La Cruz is licensed under a Creative Commons Reconocimiento 3.0 Unported License.

domingo, 11 de noviembre de 2012

Método de Newton-Raphson

Tal vez, dentro de los métodos para determinar el valor de las raíces de una función ya sea lineal o no lineal, el método de Newton es uno de los más utilizados. Si el valor inicial con el que se aproxima  la raíz es $x_i$, entonces se puede extender una tangente desde el punto $[x_i,f(x_i)]$ en donde $i=0,1,2,..,n$. De esta forma el punto de la tangente que corta al eje $x$ representa una aproximación mejorada de la raíz (ver la figura).

Figura: Ilustración del modo en que el método de Newton-Raphson se aproxima a la solución.


 El método de Newton-Raphson se puede deducir sobre la base de una interpretación geométrica (un método alterno basado en la serie de Taylor). Como podemos apreciar en la figura, la derivada de $x$ es equivalente a:
$$f'(x_i)=\frac{f(x_i)-0}{x_i-x_{i+1}}$$

Que se puede ordenar para obtener:

$$x_{i+1}=x_i-\frac{f(x_i)}{f'(x_i)}$$

Esta es la formula utilizada en el método de Newton-Raphson

Estimación del error

Para poder especificar un criterio de paro en el método de Newton-Raphson se suele utilizar la siguiente expresión para calcular el error en cada iteración:
$$error=\left | \frac{x_{i+1}-x_i}{x_{i+1}}\right | $$

Por otra parte si desarrollamos el modelo por medio de la serie de Taylor, esto nos proporciona una base teórica firme para poder estimar la velocidad de variación del error en cada iteración y así poder verificar la convergencia del método. Veamos:

La expansión de la serie de taylor se expresa de la siguiente manera:
$$f(x_{i+1})=f(x_i)+f'(x_i)(x_{i+1}-x_i)+\frac{f''(\xi)}{2!}(x_{i+1}-x_i)^2+... +\frac{f^n(\xi)}{n!}(x_{i+1}-x_i)^n\label{ref3}$$

En donde $\xi$ se encuentra en alguna parte en el intervalo entre $x_{i+1}$ y $x_i$. Si truncamos la serie de Taylor después del segundo termino derivado obtenemos la siguiente aproximación:

$$f(x_{i+1}) \cong f(x_i)+f'(x_i)(x_{i+1}-x_i)$$

En a intersección con el eje $x$ debemos tener $f(x_{i+1}))=0$, por lo tanto:

$$0 = f(x_i)+f'(x_i)(x_{i+1}-x_i)$$

Por lo que obtenemos la formula utilizada en el Método de Newton-Raphson
\begin{equation}
x_{i+1}=x_i-\frac{f(x_i)}{f'(x_i)} \label{ref1}
\end{equation}
Ahora si analizamos el error. Para esto hacemos $x_{i+1}=x_r$ en la ecuación \eqref{ref3}, en donde $x_r$ representa el valor real de la raiz y a la vez $f(x_r)=0$ :

$$0=f(x_i)+f'(x_i)(x_r-x_i)+\frac{f''(\xi)}{2!}(x_r-x_i)^2 \label{ref4}$$

y truncando  la ecuación \eqref{ref3} hasta el tercer termino:

$$f(x_{i+1}) \cong f(x_i)+f'(x_i)(x_{i+1}-x_i)+\frac{f''(\xi)}{2!}(x_{i+1}-x_i)^2 \label{ref2}$$

Luego restamos \eqref{ref4} de \eqref{ref2}:


$$0=f'(x_i)(x_r-x_{i+1})+\frac{f''(\xi)}{2!}(x_r-x_i)^2 \label{ref5}$$

Ahora sabiendo que el error entre la aproximación del método y el valor real es igual a la direfencia entre $x_{i+1}$ y $x_r$:

$$E_{t,i+1}=x_r-x_{i+1}$$
entonces:

$$0=f'(x_i)E_{t,i+1}+\frac{f''(\xi)}{2!}E_{t,i}^2 \label{ref6}$$

Si se supone que el método efectivamente converge a una solución, se podría decir entonces que tanto $x_i, \xi \longrightarrow x_r$, de esta manera la ecuación \eqref{ref6} quedaría así:

$$E_{t,i+1}=\frac{-f''(x_r)}{2f'(x_r)}E_{t.i}^2 \label{ref7}$$



Por lo que podemos ver en la ecuación \eqref{ref7} el error es proporcional al cuadrado del error anterior, este es uno de los motivos por los cuales en el caso de que la solución converja lo hace de una manera bastante rápida, sólo después de un par de iteraciones. A este comportamiento se le conoce como convergencia cuadrática.

A continuación les dejo un código que hice en MATLAB con la implementación del método.

Function Newton-Raphson

Function Newton-Raphson


%%%/////////////////////////////////////////////////
% /          Función Newton-Raphson            ///
%/        Desarrollada por Jorge De La Cruz     ///
%/                                             ///
%////////////////////////////////////////////////
% La función 'fun' debe ser introducida como un string
% x: representa el valor inicial necesario para iniciar
% las iteraciones.
% tol: La tolerancia deseada.
% imax: máximo número de iteraciones, evita que el
% programa se quede en un bucle infinito en caso de que
% el método no converja.
function [xr]=newton(fun,x,tol,imax)
err=100;
n=1;
while (tol<=err)&&(n<imax)
    xr = x-eval(fun)/(eval(diff(sym(fun))));
    err=abs((xr-x)/xr);
    error=err*100;
    x=xr;
    fprintf('Iteración: %d, Error: %f, Raiz: %f\n',n,error,xr)
    n=n+1;
end
x=-2:0.001:xr+1;
y=eval(fun);
plot(x,y)
title('METODO DE NEWTON-RAPHSON')
xlabel('$x$','interpreter','latex','fontsize',18)
ylabel('$y$','interpreter','latex','fontsize',18)
grid on
end
Y el código en Scilab sería:
0001  //Función a la cual se le desea determinar la raiz, fun.sci
0002  function y=fun(x)
0003      y=exp(-x)-x;
0004  endfunction

0001  //FUnción Newton-Raphson en otro archivo llamado newton.sci
0002  function xr=newton(d, tol, imax)
0003      err=100;
0004      n=1;
0005      while (tol<=err)&(imax>n)
0006          xr=d-(fun(d)/(derivative(fun,d)));
0007          n=n+1;
0008          err=abs((xr-d)/xr);
0009          e=err*100;
0010          d=xr;
0011          printf("iter: %i, error: %f, raiz: %f\n",n,e,xr)
0012      end
0013      x=xr-2:0.001:xr+2;
0014      y=fun(x);
0015      plot(x,y)
0016  endfunction

Licencia de Creative Commons
Newton-Raphson by Jorge De La Cruz is licensed under a Creative Commons Reconocimiento 3.0 Unported License.