Está en la página 1de 40

tema1_aproxNumerica_Errores

June 16, 2023

1 Unidad 1. Aproximación numérica y errores


En este apunte se habla acerca del surgimiento de los métodos numéricos, su importancia y un
concepto fundamental para poder estudiar el análisis numérico: el error.
Primero se habla sobre la historia de los métodos numéricos, algunos personajes ilustres que dieron
paso al descubrimiento y aplicación de estos métodos, al igual que la importancia de su aplicación

1.1 1.1 Introducción histórica de los métodos numéricos


La historia de los métodos numéricos se remonta a varios siglos atrás, con los primeros intentos
de resolver problemas matemáticos utilizando técnicas aproximadas. A lo largo de los años, los
métodos numéricos han evolucionado y se han refinado, desempeñando un papel fundamental en
la ingeniería.
<img src="Imágenes/al-khwarizmi.jpeg" alt="Portada A. Baldor">
Durante la Edad Media, los matemáticos árabes hicieron importantes contribuciones al campo del
análisis numérico. Al-Khwarizmi (el mismo que sale en la portada del libro de Álgebra de A.
Baldor) desarrolló métodos algebraicos para resolver ecaciones lineales y cuadráticas, sentando las
bases para los métodos numéricos posteriores.
En el siglo XVII, Isaac Newton y Gottfried Wilhelm Leibniz desarrollaron el cálculo difer-
encial e integral, proporcionando una nueva base matemática para abordar problemas numéricos.
Estos avances teóricos allanaron el camino para el desarrollo de métodos numéricos más sofisticados.
Durante el siglo XVIII y XIX, se produjeron avances significativos en los métodos numéricos. Por
ejemplo, el matemático suizo Leonhard Euler formuló métodos para la resolución numérica de
ecuaciones diferenciales ordinarias. Estos métodos permitieron a los ingenieros resolver problemas
prácticos en campos como la mecánica y la hidráulica.
<div style="flex: 50%;">
<img src="Imágenes/newton.jpeg" alt="Isaac Newton" style="width: 100%;">
</div>
<div style="flex: 50%;">
<img src="Imágenes/leibniz.jpeg" alt="Gottfried W. Leibniz" style="width: 100%;">
</div>
Los métodos numéricos se utilizan en ingeniería para abordar problemas complejos que no pueden
resolverse mediante métodos analíticos exactos. Estos métodos permiten aproximar soluciones
numéricas a problemas matemáticos mediante técnicas de discretización y cálculos iterativos.

1
Algunos de los campos de aplicación de los métodos numéricos en la ingeniería son los siguientes:
- Análisis de estructuras - Dinámica de fluidos - Electromagnetismo - Optimización - Simulación y
modelado - Electrosmótica
Los métodos numéricos han evolucionado a lo largo de la historia y se han convertido en una
herramienta esencial en la ingeniería. Estos métodos proporcionan soluciones aproximadas a prob-
lemas matemáticos complejos y son ampliamente utilizados en diversas áreas para mejorar el diseño,
análisis y comprensión de sistemas físicos.

1.2 1.4 Tipos de error: Inherentes, de redondeo y opr truncamiento. Errores


absoluto y relativo.
Un método numérico es un proceso matemático iterativo cuyo objetivo es encontrar la aproximación
a una solución específica con un cierto margen de error.
A diferencia de la obtención de soluciones exactas mediante métodos analíticos, los métodos numéri-
cos requieren de una aproximación a la solución real del problema, ya sea porque no existe una
solución exacta o porque resulta más conveniente utilizar métodos numéricos. Esta solución aprox-
imada de la solución real del problema debe arrojar soluciones cada vez más cercanas al valor
real.
Es importante que la información anterior es válida para los métodos numéricos de obtención de
raíces o valores concretos y no los métodos paso a paso (resolución de ecuaciones diferenciales, por
ejemplo). Para los métodos paso a paso, lo que se hace es definir un valor de espaciamiento “h” en
el que se obtendran valores. Conforme uno se aleja de las condiciones iniciales del problema, las
aproximaciones tienden a hacerse menos y menos exactas y precisas. Lo que se hace en esos casos
es reducir el valor del espaciamiento y aumentar el número de iteraciones en consecuencia.

1.2.1 Tipos de error


Bajo la perspectiva del análisis numérico, se puede decir que los errores son la diferencia que existe
entre un valor que corresponde a la solución real y un valor aproximado. Estas diferencias se
pueden deben a diversos factores y dependiendo del origen de estos es la categoría con las que se
les identifica. A continuación se explica en qué consisten las categorías de error.

Error inherente Los errores inherentes son aquellos que se producen al leer los datos de algún
dispositivo de medición, al transmitirla o reproducirla. Ocurren debido a la imprecisión de los
instrumentos o a errores humanos.

Error por truncamiento Los errores por truncamiento son aquellos que ocurren cuando un
valor tiene un número infinito o muy grande de dígitos. Computacionalmente es imposible realizar
cálculos con ese tipo de valores, por lo que una de las técnicas utilizadas para manejar este problema
es tomar una cantidad finita de cifras significativas e ignorar el resto.

Error por redondeo El error por redondeo sucede por la misma razón que el error por
truncamiento. La diferencia radica en que la cifra significativa se iguala a cierto valor cercano, de
tal forma que se reduzca el error. Algunas de las formas de redondeo son hacia abajo y hacia arriba,
aunque la más utilizada es el redondeo estándar.

2
1.2.2 Cálculo de error
Ya se habló de la definición de error y los tipos que existen, sin embargo no se ha hablado de cómo
obtenerlos. A continuación se explican 2 formas de obtener el error de una aproximación.

Error absoluto El error absoluto es la diferencia entre el valor real y el valor aproximado.
Dado que no interesa si esta diferencia es mayor o menor, simplemente se toma su magnitud y se
ignora el signo. Se define mediante la siguiente expresión
𝑒𝑎 = |𝑣𝑟𝑒𝑎𝑙 − 𝑣𝑎𝑝𝑟𝑜𝑥 |

Error relativo El error relativo es una medida utilizada para cuantificar la precisión de una
aproximación en relación al valor real. Resulta útil para evaluar la precisión de un resultado,
especialmente cuando se trabaj con valores cercanos a cero o cuando el valor de referencia tiene
un orden de magnitud grande. La forma de calcular el error relativo es a partir de la siguiente
ecuación.

𝑒𝑎 |𝑣𝑟𝑒𝑎𝑙 − 𝑣𝑎𝑝𝑟𝑜𝑥 |
𝑒𝑟 = =
𝑣𝑟𝑒𝑎𝑙 𝑣𝑟𝑒𝑎𝑙

Es importante mencionar que para conocer el error es necesario conocer el valor real. Sin embargo,
para los métodos de aproximaciones sucesivas se puede utilizar la aproximación anterior como punto
de referencia para calcular el error, ya que cada iteración debería aproximarse más a la solución
real.
<img src="Imágenes/precision_exactitud.png" alt="Diferencia entre precisión y exactitud">

Ejemplo de error por truncamiento y por redondeo A continuación se muestra la imple-


mentación del método numérico de Euler para la resolución de una ecuación diferencial ordinaria
de primer orden. En la tabla que se imprime se pueden ver los valores de la solución analítica, la
solución aproximada truncando a 3 decimales y la solución aproximada redondeando a 3 decimales.
[4]: import numpy as np
import math as m

def f(x,y):
return x+2*x*y
def S(x):
return 1.1682*m.exp(x**2)-0.5

n=10
h=0.01

x=np.zeros(n)
error_round=np.zeros(n)
error_trunc=np.zeros(n)
y=np.zeros(n)
y_round=np.zeros(n)
y_trunc=np.zeros(n)

3
yS=np.zeros(n)

x[0]=0.5
error_round[0]=0
error_trunc[0]=0
y[0]=1
y_round[0] = 1
y_trunc[0] = 1
yS[0]=1

print('{:^12} {:^10} {:^10} {:^10} {:^10}'.format('y_analitica','y_round',␣


↪'%error', 'y_trunc', '%error'))

for i in range(1,n):
x[i]=x[i-1]+h

y[i]=y[i-1]+h*f(x[i-1],y[i-1])
y_round[i] = round(y_round[i-1]+h*f(x[i-1],y_round[i-1]), 3)
y_trunc[i] = m.trunc((y_trunc[i-1]+h*f(x[i-1],y_trunc[i-1]))*100)/100
yS[i]=S(x[i])

error_round[i] = abs(yS[i]-y_round[i])/y[i]*100
error_trunc[i] = abs(yS[i]-y_trunc[i])/y[i]*100

print('{:10.5f} {:10.3f} {:10.5f} {:10.3f} {:10.3f}'.format(yS[i],␣


↪y_round[i], error_round[i], y_trunc[i], error_trunc[i]))

y_analitica y_round %error y_trunc %error


1.01523 1.015 0.02219 1.010 0.515
1.03091 1.030 0.08857 1.020 1.059
1.04707 1.046 0.10245 1.030 1.632
1.06371 1.062 0.16133 1.040 2.231
1.08085 1.079 0.17156 1.050 2.858
1.09850 1.096 0.22766 1.060 3.509
1.11666 1.114 0.23885 1.070 4.185
1.13536 1.132 0.29664 1.080 4.885
1.15461 1.151 0.31312 1.090 5.607
Como se puede observar, el error por truncamiento es mucho mayor al truncamiento por redondeo.
Por lo que es mucho más conveniente redondear para reducir la acumulación de error en las aprox-
imaciones.

4
tema2_solucionesNumericas

June 16, 2023

1 Unidad 2. Solución numérica de ecuaciones algebraicas y


trascendentes
Se le dice solución de una ecuación al valor que debe tomar su variable independiente para que
al sustituirlo en el polinomio la variable dependiente sea 0. Para esto, la ecuación puede estar
expresada tanto de forma algebraica con la expresión general:

𝐹 (𝑥) = 𝑎(0) + 𝑎(1)𝑥 + 𝑎(2)𝑥2 + ... + 𝑎(𝑛)𝑥𝑛

Como de forma trascendente, en las cuales están incluídas las funciones trigonométricas, exponen-
ciales, logarítmicas, entre otras. Algunos ejemplos de ellas son los siguientes.

𝐹 (𝑥) = 𝑒𝑥
𝐺(𝑥) = 𝑠𝑒𝑛(𝑥)
𝐻(𝑥) = 𝑎𝑟𝑐𝑐𝑜𝑠(𝑥)

Como antecedente, sabemos que hay multiples formas de resolución y obtención de raíces exactas
para funciones expresadas de forma algebraica o polinómica, donde el método favorito es la fac-
torización, sea que la realicemos por división sintética, factorización por productos notables entre
otros métodos.
De la misma forma, sabemos que en las funciones algebraicas el número de raíces, sean reales,
complejas o repetidas son el grado de la ecuación. Sin embargo, las funciones trascendentales
no tienen esta facilidad de resolución, y algunas incluso no tienen una solución exacta, aún las
expresiones polinómicas al tener un grado muy alto se vuelve complicada y tardada su resolución,
debido a ello surgió la necesidad de desarrollar formas de aproximación de las mismas y facilitar la
obtención de sus raíces.

1.1 2.1. Métodos cerrados. Método de bisección y de interpolación lineal (regla


falsa). Interpretaciones geométricas de los métodos.
Uno de los métodos para encontrar las raíces de una función son los llamados métodos cerrados,
los cuales consisten tal cual en encerrar el valor de la raíz al reducir el radio al rededor del mismo,
para ello, es importante considerar una serie de características generales que tienen las funciones:
1) Al rededor de una raíz real, si se elijen dos puntos, uno con un valor de la variable independiente
menor que la raíz y otro mayor que la misma, se observará que sus correspondientes imágenes
tendrán signos contrarios. 2) Si la función analizada se observa que alrededor de la raíz los dos

1
puntos elegidos coinciden en signo, esto nos indica que dicha función tiene un número par de raíces
o no tiene ninguna. 3) Al haber un cambio de signo entre ambos puntos nos sugiere la función una
cantidad impar de raíces, nunca cero raíces si hablamos de funciones continuas.
Las reglas ya mencionadas tienen un par de excepciones, las cuales son que las funciones a observarse
deben de ser continuas y no deben ser asintóticas al eje de la variable independiente.
Cabe mencionar que los métodos cerrados se llaman así ya que siempre convergen, para las car-
acterísticas antes descritas. Los métodos abiertos, por su parte, no siempre convergen y requieren
que se cumplan condiciones más espacíficas para garantizar que se encuentre una solución.
Dentro de la categoría de Métodos Cerrados existen 2 en los cuales nos enfocaremos:

1.1.1 Método de Bisección


Este método resulta intuitivo, pues es un procedimiento que todos hemos realizado de forma con-
sciente o inconsciente para determinar un valor.
Para fines prácticos se propondrán las premisas de que: no se conoce la forma de la función para
aproximar por método gráfico pero se conoce un radio aproximado en el que la función presenta una
raíz, las funciones serán continuas y cortarán al menos una vez el eje de la variable independiente
“𝑥”.
Ya aclarado lo anterior, el método consiste en la selección de dos valores que al evaluar la función
en ellos obtengamos dos imágenes de signos contrarios. Con este par de valores, procedemos a
sumarlos y dividirlos entre 2. A este valor promedio se le denomina 𝑥𝑟
Posteriormente, se evalúa la función en 𝑥𝑟 y se realiza la comparación para saber si este valor es de
signo negativo, positivo o cero.
• Si es menor a cero se considerará este nuevo valor como el límite inferior del radio de análisis;
• si es mayor a cero se le seleccionará como el nuevo valor del límite superior
• Si tiene un valor de 0 se concluye que se llegó a la solución exacta del problema y se ha
encontrado la raíz.
Este último escenario es poco probable (como ya se explicó en la introducción de la unidad) así que
se selecciona una tolerancia que se adapte al error aceptable en el cálculo.
El algoritmo en lenguaje numérico se expresaría de la forma: Nombrando 𝑥𝑙 al límite inferior, 𝑥𝑢
al límite superior y 𝑥𝑟 a la aproximación por el método.
1) 𝑓(𝑥𝑙 )𝑓(𝑥𝑢 ) < 0 Comprobación de la diferencia de signo entre las imágenes de ambos valores
(𝑥𝑙 +𝑥𝑢 )
2) 𝑥𝑟 = 2 Determinación del valor aproximado a la raíz.
3) Se analiza la salida de la función al ser evaluada en 𝑥𝑟
• Si 𝑓(𝑥𝑟 ) < 0 se tomará a la aproximación 𝑥𝑟 como el nuevo 𝑥𝑙 y se repite el paso 2
• Si 𝑓(𝑥𝑟 ) > 0 se le asignará a “xr” como el nuevo valor para 𝑥𝑢 y se repite el paso 2
• Si 𝑓(𝑥𝑟 ) = 0 se dice que hemos llegado a la solución de la ecuación y se ha conlcuido la
obtención de raíces
A continuación, se muestra un gráfico para visualizar la interpretación geométrica del método de
bisección.

2
Como se puede observar, con cada iteración, la aproximación se va haciendo más precisa.
Ahora que se ha explicado queda únicamente probarlo, para lo cual seleccionaremos una serie de
funciones con una tolerancia de 0.001 y emplearemos un código desarrollado durante la clase y
escrito en Python.

Ejemplo
1) Determinar la raíz real positiva de 𝑥3 − 𝑥 − 1 haciendo uso del método de bisección en el
intervalo 0 ≤ 𝑥 ≤ 1.5
[8]: # Se importan las bibiliotecas necesarias
import matplotlib.pyplot as plt
import numpy as np

# Se define la función a la cual se le calculará la raíz


def f(x):
return x**3 - x -1

xold = 100
xr = 101
i = 0
xl = 0
xu = 1.5
tol = 0.001
iteraciones = 0

if (f(xl) * f(xu) > 0):


print("No hay raíz en el intervalo")
else:
while(abs(xr-xold)>tol):
i = i+1
xold = xr
xr = (xl+xu)/2
if (f(xl) * f(xr) < 0):
xu = xr
elif (f(xl) * f(xr) > 0):
xl = xr
else:
print("La raíz es: ", xr)
iteraciones += 1

print("La raíz aproximada es:", xr)


print("El número de iteraciones es:", iteraciones)

###########################################
# Para propositos de visualización, se grafica la función
x = np.linspace(-0.5,2,50)

3
y = [f(i) for i in x]

plt.plot(x, y,label='f(x)')
plt.axvline(x=0, color='r', linestyle='--', label='Abscisa inferior (x=0)')
plt.axvline(x=1.5, color='g', linestyle='--', label='Abscisa superior (x=1.5)')
plt.scatter(0.75, 0, color='b', label='Aproximación inicial')
plt.xlabel('x')
plt.ylabel('f(x)')
plt.title('f(x) = x**3 - x - 1 y método de bisección')
plt.legend()
plt.grid(True)
plt.show()

La raíz aproximada es: 1.324951171875


El número de iteraciones es: 11

1.1.2 Método de falsa posición:


Aunque el método de bisección es totalmente viable para la determinación de la raíz de una función
dentro de un intervalo, tiene como desventaja el número de iteraciones necesarias para llegar a la

4
solución de la misma, esto pues no necesariamente la raíz de una función se encuentra en la media
de los dos valores en una distribución perfectamente centrada, puede que la misma esté más cerca
de uno de los dos límites y, si este fuera el caso, podríamos realizar una gran cantidad de iteraciones
y aún así seguir sin encontrarla. Ante esta posibilidad se desarrolló un método que considerara no
solo la distribución en la variable independiente sino también en la dependiente.
El sustento de este método es la definición de una línea recta entre las dos imágenes de los valores
límites propuestos, y el punto en el que dicha recta se corte con el eje de la variable independiente
se considerará la nueva aproximación, a la cual se le saca una imagen y, de la misma forma que en
el método de bisección, se observa si la misma tiene signo positivo, negativo o es cero; respecto a
esto el criterio para proceder será el mismo que en bisección.
Debido a la forma en la que se aproxima la raíz de la función mediante una recta a este método
también se le llama “método de interpolación lineal”, pues si el lector una vez más ha sido observador
habrá notado este detalle, lo que estamos haciendo en el fondo es comparar ambos valores de la
variable dependiente, ambos de la variable independiente y a partir de ellos buscar un valor para
𝑦 = 0.
Ya que hay un gran parecido con el método de bisección el algoritmo a utilizar es el mismo, con el
detalle de que la ecuación de recurrencia cambia:
Nombrando 𝑥𝑙 al límite inferior, 𝑥𝑢 al límite superior y 𝑥𝑟 a la aproximación por el método.
1) 𝑓(𝑥𝑙 )𝑓(𝑥𝑢 ) < 0 Comprobación de la diferencia de signo entre las imágenes de ambos valores
2) 𝑥𝑟 = 𝑥𝑢 − [𝑓(𝑥𝑢 )(𝑥𝑙 − 𝑥𝑢 )/𝑓(𝑥𝑙 ) − 𝑓(𝑥𝑢 )] Determinación del valor aproximado a la raíz.
3) Se analiza la salida de la función al ser evaluada en 𝑥𝑟
• Si 𝑓(𝑥𝑟 ) < 0 se tomará a la aproximación 𝑥𝑟 como el nuevo 𝑥𝑙 y se repite el paso 2
• Si 𝑓(𝑥𝑟 ) > 0 se le asignará a 𝑥𝑟 como el nuevo valor para 𝑥𝑢 y se repite el paso 2
• Si 𝑓(𝑥𝑟 ) = 0 se dice que hemos llegado a la solución de la ecuación y se ha conlcuido la
obtención de raíces
A continuación se muestra un gráfico para visualizar la interpretación geométrica del método de
falsa posición.
Como se puede observar, la aproximación se hace más precisa haciendo uso de una recta secante
para obtener un valor cada vez más cercano de la variable independiente.
Para comprobar lo mencionado (la semejanza con la interpolación lineal) se le invita al lector a
realizar la comprobación usando sus conocimientos previos; basta con sustituir 𝑦2 por 𝑓(𝑥𝑢 ) y 𝑦1
por 𝑓(𝑥𝑙 ), esto por comodidad y costumbre en el uso de la expresión para cálculo de pendientes.
Por último, un ejemplo será ideal para observar las ventajas del método de falsa posición ante el
método de bisección, es decir, la convergencia más pronta.

Ejemplo
1) Determinar la raíz real positiva de 𝑥3 − 𝑥 − 1 haciendo uso del método de falsa posición en el
intervalo de 0 ≤ 𝑥 ≤ 1.5
[9]: def f(x):
return x**3 - x - 1

5
xl = 0
xu = 1.5
tol = 0.001
xr = 0
error = 100
iteracines = 0

while error>tol:
xrold = xr
xr = (xu - ((f(xu)*(xl-xu))/(f(xl)-f(xu))))
error = abs(xr-xrold)
if f(xl)*f(xr)<0:
xu = xr
xrold = xr
else:
xl = xr
xrold = xr
iteracines = iteracines + 1
print("La raíz aproximada es:", xr)
print("El número de iteraciones es:", iteracines)

#############################
# Para propositos de visualización, se grafica la función y la recta secante
x = np.linspace(-0.5,2,50)
y = [f(i) for i in x]

x1 = 0
y1 = f(x1)

x2= 1.5
y2 = f(x2)

plt.plot(x, y,label='f(x)')
plt.scatter([x1, x2], [y1, y2], color='b', label='Puntos de aproximación')
plt.plot([x1, x2], [y1, y2], color='r', linestyle='--', label='Recta secante')
plt.xlabel('x')
plt.ylabel('f(x)')
plt.title('f(x) = x**3 - x - 1 y método de falsa posición')
plt.legend()
plt.grid(True)
plt.show()

La raíz aproximada es: 1.3246654648913543


El número de iteraciones es: 6

6
1.2 Métodos abiertos. Método de aproximaciones sucesivas y método de
Newton-Raphson. Interpretaciones geométricas de los métodos y criterios
de convergencia.
Como se mencionó de forma anterior, un método cerrado es aquel en el que mediante la reducción
de los límites se encierra a la raíz de la función, así que es posible intuir lo que son los métodos
abiertos; se dice de los métodos donde, teniendo un solo valor inicial es posible determinar el valor
de la raíz mediante la aplicación de un método numérico; sin embargo, tienen una característica
que vale la pena considerar al evaluar usar un método abierto o uno cerrado y es que los métodos
abiertos tienen la posibilidad de divergir (cosa que no pasa en los métodos cerrados, siempre se llega
al valor de la raíz si se encuentra en el intervalo); por otro lado, si el método numérico converge
éste lo hará a una velocidad mucha mayor que un método cerrado, es decir con menos iteraciones.
Para este caso nos enfocaremos en dos métodos abiertos: Método de punto fijo o de aproximaciones
sucesivas y el Método de Newton Raphson, el cual a opinión personal de este humilde escritor es
mejor que el método de punto fijo, sin embargo se le deja al criterio del lector una opinión final.

1.2.1 Método de punto fijo


Es un método que consiste en la igualación de la función a la variable dependiente a la vez que se
suma la variable a la misma función, esto teniendo en mente las características de una función y

7
sus raíces además de reglas del álgebra básica: al evaluar la función en su raíz la misma vale 0 y si
se realiza una suma del mismo valor a ambos lados de la igualdad la operación no se verá afectada
pues es similar a sumar un 0. Parte de los requisitos para la correcta convergencia del método es
que el valor inicial con el que se evaluará la función (es decir, el primer valor para la primera imagen
y la primera aproximación) debe ser cercano a la raíz del polinomio, si se elije un valor muy lejano
aumentan las probabilidades de que el método no converga.
En este caso, nombraremos Xr a la aproximación a la raíz y xl a la aproximación anterior. El
algoritmo tiene la forma: 1) Definición de la función de la que se desea determinar las raíces. Para
nosotros y por practicidad será la función 𝑓(𝑥) 2) Proposición de la igualdad deseada. 𝑓(𝑥) = 0
3) Suma a ambos lado de la igualdad de la variable independiente. 𝑓(𝑥) + 𝑥 = 𝑥 4) Cálculo de la
primera aproximación a la raíz. 𝑓(𝑥𝑙 ) + 𝑥𝑙 = 𝑥𝑟 5) Si se ha llegado a la raíz se observará que 𝑓(𝑥𝑙)
valdrá cero por la característica ya mencionada, así que la expresión nos quedrá 𝑥𝑙 = 𝑥𝑟 , momento
en el que podremos afirmar que hemos llegado a la raíz y el método habrá convergido. Si 𝑥𝑙 es
distindo de 𝑥𝑟 entonces se proponde 𝑥𝑟 = 𝑥𝑙 y se vuelve a la ecuación de recurrencia pero ahora
utilizando el nuevo valor aproximado.
A continuación se presenta una gráfica para poder visualizar la interpretación geométrica del método
de punto fijo.
Con lo anterior, se procede a mostrar alguno ejemplos en donde el método de punto fijo converge.
<img src="Imágenes/pfijo_converge1.png" alt="convergenica1 punto fijo" style="width:50%;">
<img src="Imágenes/pfijo_converge2.png" alt="convergencia2 punto fijo" style="width:50%;">
Vale la pena mencionar las características de divergencia del método. De forma implícita al igualarlo
a una función 𝑔(𝑥) = 𝑥 lo que se hace es buscar el punto de intersección de ambas gráficas; punto
cuyo valor de las abscisas indica el valor de la raíz de la función; sin embargo, si la función del lado
izquierdo de la igualdad (𝑓(𝑥) + 𝑥) tiene una pendiente menor en valor absoluto al de la presente en
la función al otro lado de la igualdad (pendiente=1) el método divergirá y no se llegará al resultado
así nos encontremos con un valor inicial muy cercano a la raíz.
De forma sencilla, el método diverge si no se cumple la siguiente condición.
|𝑔′ (𝑥0 )| > 1

A continuación se muestran algunos ejemplos donde el método diverge.


<img src="Imágenes/pfijo_diverge1.png" alt="divergencia1 punto fijo" style="width:50%;">
<img src="Imágenes/pfijo_diverge2.png" alt="divergencia2 punto fijo" style="width:50%;">
Al igual que en los métodos cerrados, esto quedará mucho más claro con la realización de un
ejemplo.

Ejemplo
1) Utilizando el método de punto fijo determine el valor de la raíz de la función 𝑓(𝑥) = cos(𝑥) −
1
4 𝑥 + 1 en el punto 𝑥 = 1.5

[19]: import math

def g(x):
return math.cos(x) + (3/4)*x + 1

8
xa = 0
x = 1.5 # aproximación inicial
iteraciones = 0
while abs(x-xa)>0.0001:
xa = x # Se guarda el valor anterior de x
x = g(x) # Se calcula la g(x) = x
iteraciones += 1

print("La raíz aproximada es:", x)


print("El número de iteraciones es:", iteraciones)

#############################
# Para propositos de visualización, se grafica la función y la recta y=x
x = np.linspace(-0.5,2.5,50)
y = [g(i) for i in x]

x0 = 1.5
y0 = g(x0)

x1 = 1.5
y1 = 0

plt.plot(x, y,label='g(x)')
plt.plot(x, x, color='r', label='y=x')
plt.scatter([x0, x1], [y0, y1], color='b')
plt.plot([x0, x1], [y0, y1], color='b', linestyle='--', label='1a iteración')
plt.xlabel('x')
plt.ylabel('y')
plt.title('g(x) = cos(x) +(3/4)x + 1 y método de punto fijo')
plt.legend()
plt.grid(True)
plt.show()

La raíz aproximada es: 2.0733447393640634


El número de iteraciones es: 6

9
1.2.2 Método de Newton Raphson
A diferencia del método de Punto fijo, que se puede realizar sin la necesidad de un conocimiento
de cálculo, el Método de Newton Raphson emplea el concepto de derivada, el cual emplea ya que
su significado geométrico es la pendiente de la recta tangente, misma que utiliza para plantear una
trayectoria que, evaluada en cero, otorga una aproximación increíblemente buena al valor de la raíz.
Este método al igual que punto fijo requiere únicamente de un valor inicial y a partir de él inicia
las aproximaciones sucesivas; sus enormes ventajas ante Punto Fijo es que Newton Raphson casi
siempre converge (solamente no lo hace ante condiciones particulares, las cuales son: la función
no es diferenciable en el punto de partida o en el intervalo esperado, cuando la derivada se anula
en algún punto de la función, cuando la función presenta una raíz múltiple, cuando el valor incial
es muy lejano al valor de la raíz o dicho valor se encuentra en una zona con una pendiente muy
baja o muy alta), lo hace a un ritmo muy alto, además de que al ser un método de tipo cuadrático
el número de cifras significativas se duplica a cada iteración, por lo que a una tolerancia como
la utilizada en este análisis (0.001) basta con aplicar el método un máximo de 4 iteraciones para
alcanzar un valor muy parecido a la raíz.
Ya explicado el proceso se indicará la ecuación de recurrencia:
𝑥(𝑖 + 1) = 𝑥(𝑖) − [ 𝑓𝑓(𝑥 𝑖)
′ (𝑥 ) ] con 𝑖 = 0, 1, 2, .., 𝑛
𝑖

10
Como podemos ver, esta expresión dicha sería “el valor de la última aproximación es igual al valor
de la aproximación inmediata anterior menos el valor del cociente entre su imagen y la derivada
de su imagen (es decir, su pendiente o la recta tangente a la curva en ese punto)”; de ahí que otra
forma de llamar al método de Newton Raphson es “Método de las tangentes”.
A continuación se muestra una ilustración de la interpretación geométrica del método de punto fijo.
Cabe mencionar que este método numérico difícilmente diverge, y en caso de que lo hiciese, común-
mente basta con que se seleccione valor inicial para que el método converja. Sin embargo, hay
ocasiones en las que el método diverge. A continuación se ilustran algunos casos en los que este
método no converge.
<img src="Imágenes/new_raph_div1.png" alt="divergencia1 newton Raphson" style="width:50%;">
<img src="Imágenes/new_raph_div2.png" alt="divergencia2 newton Raphson" style="width:50%;">
Como se puede observar en estas ilustraciones, el método Newton-Raphson encuentra dificultad
para lidiar con las abscisas que es cercana o igual a un punto máximo o mínimo.
<img src="Imágenes/new_raph_div3.png" alt="divergencia3 newton Raphson" style="width:50%;">
<img src="Imágenes/new_raph_div4.png" alt="divergencia4 newton Raphson" style="width:50%;">
Con lo anterior, resulta conveniente implementar este método en python para la resolución de un
ejemplo y visualizar desde otra perspectiva el método.

Ejemplo
1) Obtener el valor de la raíz real positiva de la función 𝑓(𝑥) = 2𝑐𝑜𝑠(𝑥) − 𝑒𝑥 mediante el método
de Newton-Raphson en el punto 𝑥 = 1
[23]: import math
def f(x):
return 2*math.cos(x) - math.exp(x)

def df(x):
return -2*math.sin(x) - math.exp(x)

def newton_raphson(x0, tol):


xa = 0
x = x0
i = 0
while abs(x-xa)>tol:
i += 1
xa = x
x = x - (f(x)/df(x))
print("La raíz aproximada es:", x)
print("El número de iteraciones es:", i)

newton_raphson(1, 0.0001)

#############################
# Para propositos de visualización, se grafica la función y su pendiente

11
x = np.linspace(0,1.5,50)
y = [f(i) for i in x]

x0 = 1
y0 = f(x0)

m = df(x0)
tangente = m*(x-x0) + y0

x_interseccion = x0 - y0/m

plt.plot(x, y,label='f(x)')
plt.plot(x, tangente, color='r', label='Pendiente')
plt.scatter(x0, y0, color='b', label='1a iteración')
plt.scatter(x_interseccion, 0, color='g', label='1a aproximación')
plt.xlabel('x')
plt.ylabel('y')
plt.title('f(x) = 2cos(x) - e^x y método Newton-Raphson')
plt.legend()
plt.grid(True)
plt.show()

La raíz aproximada es: 0.53978516090182


El número de iteraciones es: 4

12
13
Unidad 3. Solución de sistemas de ecuaciones

June 16, 2023

1 Unidad 3. Solución numérica de sistemas de ecuaciones lineales


Dentro de la ingeniería nos encontramos con multiples fenómenos que buscamos entender, analizar
y solucionar para llegar a resultados útiles para nosotros, sean para determinar el valor de una
variable en un cierto instante o bajo ciertas condiciones de nuestro interés; para representar los
fenómenos de forma matemática hacemos uso de las ecuaciones o funciones, las cuales son reglas
de correspondencia que definen o indican la relación entre variables para un resultado concreto; sin
embargo, no siempre podemos hacer esto con una sola función pues un conjunto de variables podrían
tomar una infinidad de valores que, al sustituirlos en la función, nos dieran el resultado esperado;
dicho escenario no nos sirve pues buscamos valores concretos que nos lleven a una solución concreta,
así fue como surge la necesidad de proponer distintas relaciones que las variables deben cumplir y
así asignarles un valor concreto. al conjunto de funciones que establecen las relaciones existentes
entre variables se le llama “sistema de ecuaciones”, y a la obtención del valor de una variable o
un conjunto de variables a través de un conjunto de funciones resueltas de forma simultánea se
le llama solución de un Sistema de Ecuaciones. Una característica importante de los sistemas de
ecuaciones es que, para poder resolverlo y encontrar el valor de las variables, se debe tener el mismo
número de ecuaciones en el sistema que de variables en el mismo. Conocemos sistemas de ecuaciones
lineales desde la educación secundaria y preparatoria, al igual que conocemos métodos de solución
de los sistemas de ecuaciones (los métodos conocidos de forma previa es la resolución de sistemas
de ecuaciones por método de Gauss-Jordan, método de determinantes, método de sustitución de
variables, entre otros), sin embargo, hasta ahora hemos aprendido a resolver los sistemas mediante
métodos analíticos; a partir de ahora, se resolverán mediante métodos numéricos. Los métodos que
analizaremos serán: Método de descomposición LU (tanto el método desarrollado por Crout y por
Doolittle) y los Métodos iterativos de Jacobi y Gauss-Seidel.

1.1 Método de descomposición LU:


1.2 Métodos iterativos de Jacobi y Gauss-Seidel:
Como lo visto en capítulos anteriores de estas notas, un método iterativo o de aproximaciones
sucesivas es aquel en que, al aplicar una ecuación de recurrencia, el resultado será un valor o una
serie de valores aproximados a la solución de un sistema donde, al seguir repitiendo la aplicación de
la ecuación de recurrencia se irá obteniendo un valor cada vez más cercano al valor de las variables
analizadas; esta aproximación al valor real se puede analizar y apreciar mediante la convergencia
de los valores de las iteraciones (por lo que, al calcular la diferencia en valor absoluto entre el
último valor aproximado y el obtenido en la iteración inmediata anterior es menor que la tolerancia
asignada al cálculo).

1
1.2.1 Método de Jacobi:
El método de Jacobi lo trabajaremos para sistemas de ecuaciones lineales, es decir, ecuaciones cuyas
variables se encuentran elevadas a un grado 1 y multiplicadas por un término numérico; parte de
la idea de que es posible escribir todo sistema de ecuaciones como el producto de dos matrices y la
igualación a una tercera matriz, tal que las primeras dos matrices (las multiplicadas) serán la matriz
de valores numéricos que multiplican a las variables en cada ecuación a la que por practicidad se le
llamará “A”, la matriz de incógnitas (justamente es la que se busca resolver, es decir, encontrar los
valores de las incógnitas que resuelven el sistema) a la que se le nombrará “x” y la tercera matriz es
la matriz de términos independientes (todo valor numérico que no multiplica a ninguna variable)
a la que se le nombrará “b”. Ya planteado el sistema de ecuaciones y bien definidas sus matrices
se dice que la matriz A es igual a la suma de dos matrices, una matriz diagonal cuyos elementos
en su diagonal principal coinciden con la diagonal principal de la matriz A (se le llamará D”) y
una matriz R la cual posee todos los otros elementos de la matriz A con excepción de los presentes
en la diagonal principal. Ya teniendo a la matriz A expresada como la suma de dos matrices se le
sustituye en la expresión “Ax=b” (la cual representa el sistema de ecuaciones), y aplicando algebra
matricial se llega a la expresión: 𝑥 = (𝐷− 1)(𝑏) − (𝐷− 1)(𝑅𝑥)
Ya teniendo la expresión planteamos una ecuación de recurrencia al hacerle unos cambios:
𝑥( 𝑘 + 1) = (𝐷− 1)(𝑏) − (𝐷− 1)(𝑅(𝑥𝑘 )) con 𝑘 = 0, 1, 2, 3, ...., 𝑛
donde k será el número de iteración.
Por practicidad, definimos la primera iteración del método como x(0) = 0, todo expresado vecto-
rialmente, y a partir de ello se calculan el resto de aproximaciones para la resolución del sistema;
se considerará que el método ha llegado a la resolución correcta o deseada en el momento en que
la diferencia en valor absoluto entre la última aproximación y la inmediata anterior sea menor a la
tolerancia establecida para el error.
Al operar de forma matricial mediante la suma de dos matrices (una diagonal y una sin diagonal
principal) es de esperarse que el método de Jacobi tiene como condición de convergencia que
la matriz utilizada “A” debe ser una matriz diagonalmente dominante, es decir, el valor de los
elementos de la diagonal principal debe ser mayor que la suma del valor absoluto de los demás
elementos en el mismo renglón, de otra forma, así sea un sistema aparentemente sencillo o simple
como uno 3x3 el método no convergerá.
Todo quedará más claro al realizar un ejemplo:

Ejemplo:
1) Resolver por medio del método de Jacobi y utilizando una tolerancia de 0.001 el sistema de
ecuaciones:
3𝑥 + 𝑦 + 𝑧 = 8
−𝑥 + 4𝑦 + 𝑧 = 10
−2𝑥 + 𝑦 + 4𝑧 = 12
[5]: import numpy as np
A=np.matrix([[3,1,1],[-1,4,1],[-2,1,4]])
b=np.array([8,10,12])

2
def Jacobi(tol,kmax,xini):
n=len(b)
xnew=np.zeros(n)
xold=np.zeros(n)

xold[0]=xini[0][0]
xold[1]=xini[1][0]
xold[2]=xini[2][0]

error=10
k=0
print('{:^2} {:^10} {:^12} {:^12} {:^12}'.format(' i ', 'Error', 'x1',␣
↪'x2', 'x3'))

while(error>tol and k<kmax):


for i in range(0,n):
xnew[i]=0
for j in range(0,i):
xnew[i]=xnew[i]+A[i,j]*xold[j]
for j in range(i+1,n):
xnew[i]=xnew[i]+A[i,j]*xold[j]
xnew[i]=(b[i]-xnew[i])/A[i,i]
error=np.linalg.norm(xnew-xold)
k=k+1
xold[:]=xnew[:]
print('{:2} {:10.9f} ({:10.9f}, {:10.9f}, {:10.9f})'.format(k, error,␣
↪xnew[0], xnew[1], xnew[2]))

return xnew,error,k

xini=[[0],[0],[0]]
Jacobi(1e-6,100,xini)

i Error x1 x2 x3
1 4.728753653 (2.666666667, 2.500000000, 3.000000000)
2 1.967178606 (0.833333333, 2.416666667, 3.708333333)
3 1.117888402 (0.625000000, 1.781250000, 2.812500000)
4 0.541347312 (1.135416667, 1.953125000, 2.867187500)
5 0.252447230 (1.059895833, 2.067057292, 3.079427083)
6 0.146234324 (0.951171875, 1.995117188, 3.013183594)
7 0.059646488 (0.997233073, 1.984497070, 2.976806641)
8 0.036468046 (1.012898763, 2.005106608, 3.002492269)
9 0.015861837 (0.997467041, 2.002601624, 3.005172729)
10 0.008412447 (0.997408549, 1.998073578, 2.998083115)
11 0.004393467 (1.001281102, 1.999831359, 2.999185880)
12 0.001905040 (1.000327587, 2.000523806, 3.000682712)
13 0.001153318 (0.999597828, 1.999911219, 3.000032842)
14 0.000471506 (1.000018646, 1.999891246, 2.999821109)
15 0.000278157 (1.000095882, 2.000049384, 3.000036512)

3
16 0.000129219 (0.999971368, 2.000014842, 3.000035595)
17 0.000063006 (0.999983188, 1.999983943, 2.999981973)
18 0.000035317 (1.000011361, 2.000000304, 2.999995608)
19 0.000014598 (1.000001363, 2.000003938, 3.000005605)
20 0.000008974 (0.999996819, 1.999998940, 2.999999697)
21 0.000003792 (1.000000455, 1.999999281, 2.999998675)
22 0.000002100 (1.000000682, 2.000000445, 3.000000407)
23 0.000001052 (0.999999716, 2.000000069, 3.000000230)
24 0.000000473 (0.999999901, 1.999999872, 2.999999841)

[5]: (array([0.9999999 , 1.99999987, 2.99999984]), 4.733113772929621e-07, 24)

1.2.2 Método de Gauss-Seidel:


Matemáticamente es lo mismo que el método de Jacobi, la única diferencia se encuentra en que
se utiliza la aproximación obtenida en la variable anterior para el cálculo de la siguiente, por lo
que se tiene una expresión de la forma: x1^(k+1)=1/a11 (b1 - a12x2^k - a13x3^k - … - a1nxn^k
x2^(k+1)=1/a22 (b2 - a21x1^k+1 - a23x3^k - … - a2nxn^k
y así seguir sucesivamente tomando el valor de la iteración anterior y sustituyendolo en la aproxi-
mación siguiente. Las condiciones de convergencia son las mismas que en el método de Jacobi.

1.2.3 Ejemplo:
2) Resolver por medio del método de Jacobi y utilizando una tolerancia de 0.001 el sistema de
ecuaciones:
3𝑥 + 𝑦 + 𝑧 = 8
−𝑥 + 4𝑦 + 𝑧 = 10
−2𝑥 + 𝑦 + 4𝑧 = 12

1.3 3.2 Métodos de descomposición LU. Crout y Doolittle


Para un sistema de ecuaciones que se puede representar de la forma

𝐴𝑥̄ = 𝑏̄

Donde A es una matriz de coeficientes 𝑎𝑖𝑗 , 𝑥̄ es el vector de inccognitas y 𝑏̄ el vector de términos


independientes
El método de descomposición, como dice su nombre, se trata de descomponer la matriz de coe-
ficientes en una matriz triangular superior (U) y una matriz inferior (L). Este método sirve para
hallar matrices inversas y resolver matrices lineales.
La ventaja de Crout, a cmparación de otros métodos, es que este no tiene criterios de convergencia.
Siempre converge, por eso es el método más práctico
La descomposición LU funciona de la siguiente manera

[𝐴][𝑋] = 𝐵

4
[𝐴][𝑋] − 𝐵 = 0

[𝑈 ][𝑋] − 𝐷 = 0

[𝐿][𝑈 ][𝑋] − 𝐷 = [𝐴]𝑋 − 𝐵

[𝐿][𝑈 ] = [𝐴]

[𝐿][𝐷] = [𝐵]

1.3.1 Versión Crout


En la versión Crout, las matrices al descomponer quedan de la forma

𝑙11 0 0 0
⎡𝑙 𝑙 0 0 ⎤
𝐿 = ⎢ 21 22 ⎥
⎢ 𝑙31 𝑙32 𝑙33 0 ⎥
⎣𝑙𝑛1 𝑙𝑛2 𝑙𝑛3 𝑙𝑛4 ⎦

1 𝑢12 𝑢13 𝑢1𝑛


⎡0 1 𝑢 ⎤
23 𝑢2𝑛 ⎥
𝑈 =⎢
⎢0 0 1 𝑢3𝑛 ⎥
⎣0 0 0 1 ⎦

Fórmulas para generar las matrices de la versión Crout son


1ra Columna de L:

𝑙𝑖1 = 𝑎𝑖1

1er Renglón de U:
𝑎1𝑗
𝑢1𝑗 =
𝑙11

Columnas 2 ≤ 𝑗 ≤ 𝑛 − 1 de L:
𝑗−1
𝑙𝑖𝑗 = 𝑎𝑖𝑗 − ∑ 𝑙𝑖𝑘 𝑢𝑘𝑗
𝑘=1

Renglones 2 ≤ 𝑖 ≤ 𝑛 − 1 de U:
𝑖−1
𝑢𝑖𝑗 − ∑𝑘=1 𝑙𝑖𝑘 𝑢𝑘𝑗
𝑢𝑖𝑗 =
𝑙𝑖𝑖

5
Última Columna de L:
𝑛−1
𝑙𝑛𝑛 = 𝑎𝑛𝑛 − ∑ 𝑙𝑛𝑘 𝑢𝑘𝑛
𝑘=1

Fórmulas de sustitución hacia adelante y hacia atrás


Cálculo de {d} con sustitución hacia adelante:
𝑖−1
𝑏𝑖 − ∑𝑘=1 𝑙𝑖𝑘 𝑑𝑘
𝑑𝑖 =
𝑙𝑖𝑖

Cálculo de {x} con sustitución hacia atrás:


𝑛
𝑥𝑖 = 𝑑𝑖 − ∑ 𝑢𝑖𝑘 𝑥𝑘
𝑘=𝑖+1

Donde n es el orden de la matriz cuadrada

1.3.2 Versión Doolittle


En la versión Doolittle las matrices quedan de la forma:

1 0 0 0
⎡𝑙 1 0 0⎤
𝐿 = ⎢ 21 ⎥
⎢ 𝑙31 𝑙32 1 0⎥
⎣𝑙𝑛1 𝑙𝑛2 𝑙𝑛3 1⎦

𝑢11 𝑢12 𝑢13 𝑢1𝑛


⎡ 0 𝑢 ⎤
22 𝑢23 𝑢2𝑛 ⎥
𝑈 =⎢
⎢ 0 0 𝑢33 𝑢3𝑛 ⎥
⎣ 0 0 0 𝑢44 ⎦
Fórmulas para descomposición LU. Versión Doolittle
1er Renglón de U:

𝑢1𝑗 = 𝑎1𝑗

1ra columna de L
𝑎𝑖1
𝑙𝑖1 =
𝑢11

Renglones 2 ≤ 𝑖 ≤ 𝑛 − 1 de U:
𝑖−1
𝑢𝑖𝑗 = 𝑎𝑖𝑗 − ∑ 𝑙𝑖𝑘 𝑢𝑘𝑗
𝑘=1

Columnas 2 ≤ 𝑗 ≤ 𝑛 − 1 de L:
𝑗−1
𝑎𝑖𝑗 − ∑𝑘=1 𝑙𝑖𝑘 𝑢𝑘𝑗
𝑙𝑖𝑗 =
𝑢𝑗𝑗

6
Último Renglón de U:
𝑛−1
𝑢𝑛𝑛 = 𝑎𝑛𝑛 − ∑ 𝑙𝑛𝑘 𝑢𝑘𝑛
𝑘=1

Fórmulas para la sustitución adelante y sustitución hacia atrás


Cálculo de {d} con sustitución hacia adelante:
𝑖−1
𝑑𝑖 = 𝑏𝑖 − ∑ 𝑙𝑖𝑘 𝑑𝑘
𝑘=1

Cálculo de {x} con sustitución hacia atrás:


𝑛
𝑑𝑖 − ∑𝑘=𝑖+1 𝑢𝑖𝑘 𝑥𝑘
𝑥𝑖 =
𝑢𝑖𝑖

Donde n es el orden de la atriz cuadrada

7
Ejemplo con versión Crout
A continuación se resuelve un sistema de ecuaciones simultáneas de 3x3 mediante el método de
descomposición LU versión Crout.

Primero se define la matriz de coeficientes 'A'

A = [3 -6 7; 8 0 -5; 1 -2 6]

A = 3×3
3 -6 7
8 0 -5
1 -2 6

Después se define el vector de términos independientes 'b'

b = [4 19 5]

b = 1×3
4 19 5

N = length(b); %determina la dimensión de la matriz según la cantidad de datos ingresad

Se generan las matrices L y U para almacenar los valores.

L = zeros(N,N); %inicializa la matriz L y U


U = zeros(N,N);

Se genera la diagonal principal unitaria de la matriz U

for a = 1:N % es un contador que va de 1 a N


U(a,a) = 1;
end

Se generan los primeros valores para las matrices L y U

L(:,1) = A(:,1); %cuando el : se encuentra en el primer término de la matriz significa


U(1,:) = A(1,:)/L(1:1); % en esta posición significa "toda la columna", recorre la colu

Se continua generando los renglones de U y las columnas de L

for i = 2:N
for k = i:N
L(k,i)= A(k,i)-L(k,1:i-1)*U(1:i-1,i); % los contadores van asignando el número
end
for j = i+1:N
U(i,j) = (A(i,j)-L(i,1:i-1)*U(1:i-1,j))/L(i,i);
end
end

Se imprime la matriz 'L' obtenida

1
L

L = 3×3
3.0000 0 0
8.0000 16.0000 0
1.0000 0 3.6667

Se imprime la matriz 'U' obtenida

U %imprime las matrices

U = 3×3
1.0000 -2.0000 2.3333
0 1.0000 -1.4792
0 0 1.0000

Se genera el vector D.

D = zeros(N,1);
D(1) = b(1)/L(1,1);
for k=2:N
D(k) =(b(k) - L(k,1:k-1)*D(1:k-1))/L(k,k);
end

Se imprime la matriz D obtenida.

D = 3×1
1.3333
0.5208
1.0000

Se genera la matriz de incógnitas 'X'

X = zeros(N,1);
X(N) = D(N)/U(N,N);

for k= N-1 : -1 : 1
X(k) = D(k)-U(k,k+1:N)*X(k+1:N);
end

Se imprime la matriz de incógnitas 'X'

X = 3×1
3.0000
2.0000
1.0000

2
Ejemplo con versión Doolittle
A continuación se resuelve un ejemplo de sistema de ecuaciones simultáneas de 3x3 mediante la
descomposición LU versión Doolittle.

Primero se define la matriz de coeficientes "A"

A = [3 -6 7; 8 0 -5; 1 -2 6]

A = 3×3
3 -6 7
8 0 -5
1 -2 6

Posteriormente se definen los términos independientes

B = [4 19 5]

B = 1×3
4 19 5

Se generan las matrices L y U para almacenar los valores de acuerdo a las ecuaciones de recurrencia

N=length(A);
L=zeros(N,N);
U=zeros(N,N);

Se genera la diagonal principal unitaria de la matriz L

for a = 1:N
L(a,a) = 1;
end

Se generan los primeros valores para las matrices L y U

U(1,:) = A(1,:);
L(:,1)=A(:,1)/U(1,1);

Se continua generando los renglones de U y las columnas de L

for i = 2:N
for j = i:N
U(i,j)=A(i,j)-L(i,1:i-1)*U(1:i-1,j);
end

for k = i+1:N
L(k,i)=(A(k,i)-L(k,1:i-1)*U(1:i-1,i))/U(i,i);
end
end

1
Se genera el vector D

D=zeros(N,1);
D(1)=B(1)/L(1,1);

for k = 2:N
D(k)=B(k)-L(k,1:k-1)*D(1:k-1);
end

Se genera el vector de incógnitas 'X'

X=zeros(N,1);
X(N)=D(N)/U(N,N);

Se obtiene el valor de las incógnitas 'X'

for i = N-1 : -1 : 1
X(i)=(D(i)-U(i,i+1:N)*X(i+1:N))/U(i,i);
end

Matriz L:

L = 3×3
1.0000 0 0
2.6667 1.0000 0
0.3333 0 1.0000

Matriz U:

U = 3×3
3.0000 -6.0000 7.0000
0 16.0000 -23.6667
0 0 3.6667

Vector D:

D = 3×1
4.0000
8.3333
3.6667

Incógnitas:

X = 3×1

2
3
2
1

3
Unidad 4. Interpolación, derivación en integración numérica
En la vida diaria es poco común que los datos de cierto experimento fenómeno vengan en forma de una función. La mayoría de datos se representa como puntos con un comportamiento determinado. Al aplicar métodos numéricos podemos determinar con cierta exactitud la función que describe el fenómeno, e incluso determinar nuevos puntos, además de determinar su derivada e
integral en puntos definidos.

A continuación hablaremos de 2 métodos utilizados para resolver este tipo de problemas.

4.1 Interpolación con incrementos variables (Polinomio de Lagrange)


Llamamos interpolar a estimar valores intermedios en datos definidos por una serie de puntos. Dependiendo de la cantidad de puntos obtenidos, podemos determinar el orden y comportamiento de una función en forma de un polinomio.

Al determinar el polinomio estamos seguro de que existe uno y solo un polinomio que pasa através de todos los puntos dados de orden n, sabiendo que n+1 es el número de datos que tenemos disponibles.

En este caso hablamos de datos en intervalos variables, esto se refiere a que el espaciado entre los valores x n es diferente.

x y = F (x)

x0 y0

x1 = x0 + h0 y1

x2 = x1 + h1 y2

⋯ ⋯

xn = xn−1 + hn−1 yn

Donde:

h 0 ≠ h 1 ≠ h 2 ≠ ⋯ h n−1

Y sabemos que el polinomio de grado n es igual a

n n−1 n−2
y = b0 x + b1 x + b2 x + ⋯ + bn−1 x + bn

Para obtener dicho polinomio, se utiliza la expresión del polinomio de Lagrange:


n n
x − xj
y = ∑( ∏ )yi
xi − xj
i=0 j=0j≠i

Obteniendo una expresión de la forma

(x − x 1 )(x − x 2 )(x − x 3 ) ⋯ (x − x n )
y = y0 +
(x 0 − x 1 )(x 0 − x 2 )(x 0 − x 3 ) ⋯ (x 0 − x n )

(x − x 0 )(x − x 2 )(x − x 3 ) ⋯ (x − x n )
y1 +
(x 1 − x 0 )(x 1 − x 2 )(x 1 − x 3 ) ⋯ (x 1 − x n )

(x − x 0 )(x − x 1 )(x − x 2 ) ⋯ (x − x n−1 )


yn
(x n − x 0 )(x n − x 1 )(x n − x 2 ) ⋯ (x n − x n−1 )

Utilizando el polinomio podemos interpolar cualquier valor en la expresión

In [ ]:

In [1]: import numpy as np


import sympy as sym
import matplotlib.pyplot as plt
xi=np.array([1,1.5,2,2.5,3])
fi=np.array([0.97000,1.16065,1.45264,1.65136,1.76879])
n=len(xi)
x=sym.Symbol('x')
polinomio=0
for i in range(0,n):
N=1 #numerador
D=1 #denominador
for j in range(0,n):
if (i!=j):
N=N*(x-xi[j])
D=D*(xi[i]-xi[j])
F=(N/D)*fi[i] #fracción
polinomio=polinomio+F
polisimple=sym.expand(polinomio) # para la simplificación del polinomio

# Impresión de la solución
print("Polinomio de Lagrange")
print(polinomio)
print("Polinomio de Lagrange reducido:")
print(polisimple)

# Graficar la solución
Px=sym.lambdify(x,polinomio) #permite evaluar a x dentro del polinomio obtenido
muestras=25
xl=np.min(xi) #límite inferior de la gráfica para x
xu=np.max(xi) #límite superior de la gráfica para x
Pol_xi=np.linspace(xl,xu,muestras) # se genera 50 divisiones entre el límite inferior y superior p evaluar el pol.
Pol_yi=Px(Pol_xi) #se evalua el polinomio en el dominio generado en Pol_xi

plt.plot(xi,fi,'o') #se grafican los puntos de la función tabular


plt.plot(Pol_xi,Pol_yi)
plt.show()

Polinomio de Lagrange
0.646666666666667*(x - 3.0)*(x - 2.5)*(x - 2.0)*(x - 1.5) - 3.09506666666667*(x - 3.0)*(x - 2.5)*(x - 2.0)*(x - 1.0) + 5.81056*(x - 3.0)*(x - 2.5)*(x - 1.5)*(x - 1.0) - 4.40362666666667*(x - 3.0)*(x - 2.0)*(x - 1.5)*(x - 1.0) + 1.17919333333333*(x - 2.5)*(x - 2.0)*(x - 1.5)*(x - 1.0)
Polinomio de Lagrange reducido:
0.137726666666667*x**4 - 1.22356666666667*x**3 + 3.81498833333333*x**2 - 4.46325833333336*x + 2.70410999999999

4.3 Derivación Numérica. Extrapolación de Richardson


Cuando tenemos un número de puntos tal que dificulta extraer un polinomio, es mucho más práctico obtener la derivada de la función utilizando métodos numéricos. Al ser un resultado numérico, solo obtenemos la función derivada evaluada en un punto, si queremos obtener la función completa es necesario aplicar un método de polinomio interpolante o el polinomio de Lagrange y
después derivarla.

Partimos asumiendo que la función que se quiere derivar está tabulada y cuenta con incrementos constantes de la forma:

x y = F (x)

x0 y0

x1 = x0 + h y1

x2 = x0 + 2h y2

⋯ ⋯

xn = x0 + nh yn

k k 2
k j
F (x) = y0 + ( )Δy0 + ( )Δ y0 + ⋯ + ( )Δ y0
1 2 j

Obteniendo su primer derivada respecto a x

d d k k 2
k j
d
F (x) = [y0 + ( )Δy0 + ( )Δ y0 + ⋯ + ( )Δ y0 ]
dx dk 1 2 j dx

xk − x0
k =
h

y su derivada es

dk 1
=
dx h

Para calcular las combinaciones usamos

k k(k − 1)(k − 2)(k − 3) ⋯ (k − i + 1)


( ) =
i i!

Si sustituimos en la expresión de la primer derivada

d 1 d k(k − 1) k(k − 1)(k − 2) k


2 3 j
F (x) = [y0 + kΔy0 + Δ y0 + Δ y0 + ⋯ + ( )Δ y0 ]
dx h dk 2! 3! j

Y derivamos

2
d 1 2k − 1 3k − 6k + 2 k
2 3 j
F (x) = [Δy0 + Δ y0 + Δ y0 + ⋯ + ( )Δ y0 ]
dx h 2 6 j

Y para obtener la segunda derivada

2 2
d 1 d k
2 3 j
F (x) = [Δ y0 + (k − 1)Δ y0 + ⋯ + ( )Δ y0 ]
2 2 2
dx h dk j

El proceso se puede repetir las veces que sea necesario, pero para esto necesitamos aumentar el orden del polinomio interpolante.

Para facilitar la derivación numérica se tienen expresiones utilizando método de pivoteo para calcular la derivada evaluada en un punto determinado. Se les conocen como

Primer orden de interpolación

1

y = [−1 , 1] + er
0
h –––

Segundo orden de interpolación

1

y = [−3 , 4, −1] + er
0
2h –––

1

y = [−1, 0, 1] + er
1
2h –

1

y = [1, −4, 3] + er
2
2h –

1
′′
y = [1, −2 , 1] + er
1
h
2 –––

Tercer orden de interpolación

1

y = [−11 , 18, −9, 2] + er
0
6h –––––

1

y = [−2, −3 , 6, −1] + er
1
6h –––

1

y = [1, −6, 3, 2] + er
2
6h –

1

y = [−2, 9, −18, 11 ] + er
3
6h –
––

1
′′
y = [2, −5, 4 − 1] + er
0
h2 –

1
′′
y = [1, 2, 1, 0] + er
0
2 –
h

1
′′
y = [0, 1, −2 , 1] + er
0
h
2 –––

1
′′
y = [−1, 4, −5, 2] + er
0
h
2 –

Extrapolación de Richardson
Para reducir el error al usar polinomios interpolantes podemos reducir el valor de h, incrementar el orden de interpolación o usar Interpolación de Richardson utilizando dos aproximaciones de la derivada y obtener una tercera con un menor error.

El método consiste en calcular una aproximación utilizando una misma fórmula de derivación con dos tamaños de paso, siendo h2 < h1

1
D = Dh + (Dh − Dh )
2 2 1
h1
2
( ) − 1
h2

Donde Dh y Dh representan valores aproximados a subintervalos de aplitud h1 y h_2yD$ el valor obtenido mmediante la extrapolación de Richardson
1 2

Por ejemplo:

π π π
Estimar la derivada de f(x) = cos(x) en x =
8
utilizando los esquemas de derivación obtenidos de polinomio de segundo y tercer grados con paso h1 =
8
y h2 =
16

π
Sacando la primer derivada de un polinomio de segundo grado usando esquemas de derivación con h1 =
8

x y = cos(x)

x0 = 0 1

π
x1 = .924
8

π
x2 = .707
4

1

y = [−1, 0, 1] + er
1
2h –

1

y = π
[−1(1.000), (0)(0.924), 1(0.707)] + er
1
2[ ]
8


y = −0.373
1

π
Sacando la primer derivada de un polinomio de segundo grado usando esquemas de derivación con h2 =
16

x y = cos(x)

π
x0 = .981
16

π
x1 = .924
8


x2 = .831
16

1

y = π
[−1, 0, 1] + er
1

2( )
16

1

y = π
[−1(0.981), (0)(0.924), 1(0.831)] + er
1
2[ ]
16


y = −0.382
1

Usando extrapolación de Richardson

1
D = Dh + (Dh − Dh )
2 2 1
h1
2
( ) − 1
h2

1
D = −0.382 + π
(−0.382 + 0.373) = −0.385
8
2
( π ) − 1
16

−0.383 + 0.385
er = | | ∗ 100 = 0.5%
−0.383

In [ ]:
tema5_solucion_ecsDifs

June 16, 2023

1 Solución Numérica de Ecuaciones y Sistemas de Ecuaciones


Diferenciales
En este apunte de jupyter notebooks se documenta y explican los métodos Euler y Runge-Kutta
de segundo y cuarto orden. Los cuales son dos métodos numéricos paso a paso que resultan
particularmente útiles para encontrar soluciones a ecuaciones y sistemas de ecuaciones diferenciales.
La estructura de este apunte interactivo consiste en la explicación de la teoría necesaria para
comprender estos métodos y poder resolver ejercicios mediante estos dos métodos. Posteriormente
se explica la implementación de los métodos en Python.
Primero se explica el método de Euler, que cronológicamente fue el primero en ser desarrollado. De-
spués, se explican los métodos Runge-Kuta, desarrollado por los matemáticos Carl Runge (alemán)
y Martin Kuta (polaco).

1.1 Clasificación de Ecuaciones Diferenciales


Una ecuación diferencial contine derivadas de una o más variables dependientes respecto a una o
𝑑𝑦
más variables independientes. La expresión 𝑑𝑥 + 𝑥𝑦 = 0 es un ejemplo de ecuación diferencial.
Las ecuaciones diferenciales se clasifican por su tipo, orden, grado y linealidad. Clasificarlas cor-
rectamente es fundamental para poder proponer soluciones tanto aproximadas como analíticas.

1.1.1 Por su tipo


• Ordinarias: Es una ecuación que ocntiene derivadas de una o más variables dependientes
respecto a una sola variable independiente.

𝑑2 𝑦 𝑑𝑦
2
+4 − 10𝑦 = 0
𝑑𝑥 𝑑𝑥
𝑑3 𝑎 𝑑𝑏
3
+ + 𝑎𝑧𝑥 = 𝑒−2𝑥
𝑑𝑥 𝑑𝑥
• Parciales: Es una ecuación que contiene derivadas de una o más variables dependientes
respecto a dos o más variables independientes.

𝜕 2𝑢 𝜕 2𝑢
+
𝜕𝑥2 𝜕𝑥2
𝜕𝑢 𝜕𝑣 𝜕𝑤
+ + =0
𝜕𝑥 𝜕𝑦 𝜕𝑧

1
1.1.2 Por su orden
El orden de una ecuación diferencial es el orden de la mayor derivada de la ecuación.

𝑑2 𝑦 𝑑𝑦
2
+4 − 10𝑦 = 0
𝑑𝑥 𝑑𝑥

La ecuación diferencial anterior es una ecuación de segundo orden, ya que la derivada de mayor
orden es de orden 2.

1.1.3 Por su grado


El grado es el exponente al que está elevado el término en el que se encuentra la derivada de mayor
orden, siempre y cuando la variable dependiente y sus derivadas estén dadas de forma polinomial
en la ecuación (no como argumentos de funciones trascendentales, ni con exponentes fraccionarios).

𝑑2 𝑦 𝑑𝑦
2
+ 5( )3 − 4𝑦 = 𝑒𝑥
𝑑𝑥 𝑑𝑥

El grado de la ecuación anterior es 1, porque la derivada de mayor orden tiene exponente 1.

1.1.4 Por su linealidad


Una ecuación diferencial lienal es aquella en que la variable dependiente y sus derivadas solo apare-
cen en combinaciones aditivas de sus primeras potencias, es decir que vienen en forma polinomial
y su exponente debe ser 1. Además, los coeficientes dependen sólo de la variable independiente.

𝑑𝑛 𝑦 𝑑𝑛−1 𝑦 𝑑2 𝑦 𝑑𝑦
𝑎𝑛 (𝑥) 𝑛
+ 𝑎 𝑛 − 1(𝑥) 𝑛−1
+ ... + 𝑎 2 (𝑥) 2
+ 𝑎1 (𝑥) + 𝑎0 (𝑥)𝑦 = 𝐹 (𝑥)
𝑑𝑥 𝑑𝑥 𝑑𝑥 𝑑𝑥

Así se debe ver una ecuación diferencial lineal.

1.2 Método de Euler


El método Euler es uno de los métodos numéricos más sencillos e intuitivos para resolver ecuaciones
diferenciales de primer orden a partir de condiciones iniciales. Al igual que todos los métodos, paso
a paso, utiliza valores anteriores para hacer una predicción del punto siguiente y repite este proceso
para generar una sucesión de puntos que se aproxime a la curva solución.
Graficamente, esto se puede representar de la siguiente manera.
<img src="Imágenes/aproximacionEuler.png" alt="aproximaciones paso a paso">
El método de Euler hace uso de una expresión de recurrencia que se apoya de las condiciones
iniciales para poder hacer su aproximación a la curva solución.

𝑦𝑖+1 = 𝑦𝑖 + ℎ𝑓(𝑥𝑖 , 𝑦𝑖 )
𝑖 = 0, 1, 2, ...

2
Esta ecuación de recurrencia se puede leer como la aproximación del siguiente valor de 𝑦 es igual
al valor anterior de 𝑦 más el producto de espaciamiento ℎ por la función no diferencial 𝑓(𝑥, 𝑦).
Algunos aspectos importantes de esta definición es que ℎ es el espaciado que existe entre cada
aproximación. Por ejemplo, si 𝑥0 = 0 y el espaciamiento es ℎ = 0.1 el siguiente valor de 𝑥 sería
𝑥1 = 0.1, luego 𝑥2 = 0.2 y así sucesivamente. La aproximación mejora mientras el espaciamiento
sea menor.
Otro concepto fundamental es la función no diferencial. La función no diferencial es aquella que
resulta de despejar el término diferencial de una ecuación diferencial de primer orden.

𝑑𝑦
= 𝑓(𝑥, 𝑦)
𝑑𝑥

Con esto en mente, simplemente es cuestión de sustituir los valores que se conocen a partir de las
condiciones iniciales.

1.2.1 Implementación en Python


A continuación se implementa el método Euler en Python para ejemplificar los conceptos que se
discutieron.
[1]: # Primero se importan las bibliotecas necesarias
import numpy as np
import math
import matplotlib.pyplot as plt

# Se define la función no diferencial f(x,y)


def f(x,y):
return x+2*x*y

# Se define la solución exacta del problema


def S(x):
return 1.1682*math.exp(x**2)-0.5

# Se define el número de iteraciones (n) y el tamaño de paso (h)


n=6
h=0.1

# Se definen los arreglos para almacenar los valores de 'x', 'y' y 'yS'
x=np.zeros(n)
y=np.zeros(n)
yS=np.zeros(n)

# Se definen las condiciones iniciales


x[0]=0.5
y[0]=1
yS[0]=1

3
# Se implementa el método de Euler
print(x[0],y[0],yS[0])
for i in range(1,n):
x[i]=x[i-1]+h # Se calcula el valor de 'x' en la iteración␣
↪'i'

y[i]=y[i-1]+h*f(x[i-1],y[i-1]) # Se calcula el valor de 'y' con la ecuación␣


↪de recurrencia

yS[i]=S(x[i]) # Se calcula el valor de la solución exacta

print(round(x[i],5),round(y[i],5),round(yS[i],5)) # Se imprimen los valores de␣


↪'x', la solución aproximada y la solución analítica

plt.scatter(x,y)
plt.plot(x,yS, color='red')

0.5 1.0 1.0


1.0 2.38368 2.6755

[1]: [<matplotlib.lines.Line2D at 0x10ad48760>]

Como se pudo ver en este ejemplo, se partió de la función no diferenciable y las condiciones iniciales
para generar una aproximación. La linea roja representa la solución analítica, mientras que los
puntos azules son los que se calcularon con el método Euler.

4
Como se puede observar, el método Euler tiende a empeorar su predicción con cada predicción.
Esto se debe a que el error se acumula con cada iteración. Si se reduce el tamaño de paso y se
aumentan las iteraciones se puede ver cómo mejora la aproximación.
[2]: n=50
h=0.01

# Se definen los arreglos para almacenar los valores de 'x', 'y' y 'yS'
x=np.zeros(n)
y=np.zeros(n)
yS=np.zeros(n)

# Se definen las condiciones iniciales


x[0]=0.5
y[0]=1
yS[0]=1

print(x[0],y[0],yS[0])
for i in range(1,n):
x[i]=x[i-1]+h # Se calcula el valor de 'x' en la iteración␣
↪'i'

y[i]=y[i-1]+h*f(x[i-1],y[i-1]) # Se calcula el valor de 'y' con la ecuación␣


↪de recurrencia

yS[i]=S(x[i]) # Se calcula el valor de la solución exacta

print(round(x[i],5),round(y[i],5),round(yS[i],5)) # Se imprimen los valores de␣


↪'x', la solución aproximada y la solución analítica

plt.scatter(x,y)
plt.plot(x,yS, color='red')

0.5 1.0 1.0


0.99 2.58071 2.61293

[2]: [<matplotlib.lines.Line2D at 0x10ae59f40>]

5
2 Métodos de Runge-Kutta
Los métodos de Runge-Kutta consisten en obtener una ecuación similar a la ecuación de recurrencia
del método de Euler

𝑦𝑖+1 = 𝑦𝑖 + ℎ𝑓(𝑥𝑖 , 𝑦𝑖 )
𝑖 = 0, 1, 2, ...

De forma general se escribe de la siguiente forma.

𝑦𝑖+1 = 𝑦𝑖 + (𝑤1 𝑘1 + 𝑤2 𝑘2 + ... + 𝑤𝑛 𝑘𝑛 )


𝑖 = 0, 1, 2, ...

Donde 𝑘1 = ℎ𝑓(𝑥𝑖 , 𝑦𝑖 )
𝑘2 = ℎ𝑓(𝑥𝑖 + 𝑎1 ℎ, 𝑦𝑖 + 𝑏1,1 𝑘1 )
𝑘3 = ℎ𝑓(𝑥𝑖 + 𝑎3 ℎ, 𝑦𝑖 + 𝑏2,1 𝑘1 + 𝑏2,2 𝑘2 )
...
𝑘𝑛 = ℎ𝑓(𝑥𝑖 + 𝑎𝑛−1 ℎ, 𝑦𝑖 + 𝑏𝑛−1,1 𝑘1 + 𝑏𝑛−1,2 𝑘2 + ... + 𝑏𝑛−1 , 𝑛 − 1𝑘𝑛−1 )

6
La ventaja de los métodos de Runge-Kutta con respecto a los métodos de Euler y Euler-Gauss
consiste en que los primeros tienen un orden de error menor. Con respecto a la aproximación a
través de polinomios de Taylor, tienen la ventaja de que no requieren valuar ninguna derivada, sino
únicamente la función 𝑓(𝑥, 𝑦).

2.1 Método Runge-Kutta de 2º Orden


Para este método, como ya se mencionó anteriormente, se utiliza una ecuación de recurrencia y dos
constantes, las cuales se obtienen a partir de la función no diferencial 𝑓(𝑥, 𝑦).
1
𝑦𝑖+1 = 𝑦𝑖 + (𝑘1 + 𝑘2 )
2

donde

𝑘1 = ℎ𝑓(𝑥𝑖 , 𝑦𝑖 )
𝑘2 = ℎ𝑓(𝑥𝑖 + ℎ, 𝑦𝑖 + 𝑘1 )

2.2 Método Runge-Kutta de 4º Orden


Para el método de Runge-Kutta de 4º orden se agregan más constantes para la aproximación,
mismas que se calculan a partir de la función no diferencial 𝑓(𝑥, 𝑦).

1
𝑦𝑖+1 = 𝑦𝑖 + (𝑘1 + 2𝑘2 + 2𝑘3 + 𝑘4 )
6
𝑖 = 0, 1, 2, ...

donde

𝑘1 = ℎ𝑓(𝑥𝑖 , 𝑦𝑖 )
ℎ 𝑘
𝑘2 = ℎ𝑓(𝑥𝑖 + , 𝑦𝑖 + 1 )
2 2
ℎ 𝑘
𝑘3 = ℎ𝑓(𝑥𝑖 + , 𝑦𝑖 + 2 )
2 2
𝑘4 = ℎ𝑓(𝑥𝑖 + ℎ, 𝑦𝑖 + 𝑘3 )

2.2.1 Ejemplo
Ya que se explicaron las ecuaciones de recurrencia de los métodos de Runge-Kutta de segundo y
cuarto orden, se puede proceder a resolver un ejemplo y comparar los resultados obtenidos por los
2 métodos.
[31]: import numpy as np
import matplotlib.pyplot as plt
import math

def f(x,y):

7
return x+2*x*y
def S(x):
return 1.1682*math.exp(x**2)-0.5

n = 15
h = 0.1

def analitica(n,h):
yS=np.zeros(n)
x=np.zeros(n)
x[0]=0.5
yS[0]=1.0
for i in range(1,n):
x[i] = x[i-1]+h
yS[i] = S(x[i])
return yS

def rk4(n,h, x_0, y_0):


x=np.zeros(n)
y=np.zeros(n)
k1=np.zeros(n)
k2=np.zeros(n)
k3=np.zeros(n)
k4=np.zeros(n)

x[0]=x_0
y[0]=y_0

for i in range(1,n):
x[i]=x[i-1]+h
k1[i]=h*f(x[i-1],y[i-1])
k2[i]=h*f(x[i-1]+(h/2),y[i-1]+(k1[i]/2))
k3[i]=h*f(x[i-1]+(h/2),y[i-1]+(k2[i]/2))
k4[i]=h*f(x[i-1]+h,y[i-1]+k3[i])
y[i]=y[i-1]+(1/6)*(k1[i]+2*k2[i]+2*k3[i]+k4[i])
return x,y

def rk2(n,h, x_0, y_0):


x=np.zeros(n)
y=np.zeros(n)
k1=np.zeros(n)
k2=np.zeros(n)

x[0]=x_0
y[0]=y_0

for i in range(1,n):

8
x[i]=x[i-1]+h
k1[i]=h*f(x[i-1],y[i-1])
k2[i]=h*f(x[i-1]+h,y[i-1]+k1[i])
y[i]=y[i-1]+(1/2)*(k1[i]+k2[i])
return x,y

x,y_rk4=rk4(n,h,0.5,1)
x,y_rk2=rk2(n,h,0.5,1)
yS=analitica(n,h)

plt.scatter(x,y_rk2, color='red', label='RK2')


plt.scatter(x,y_rk4, color='orange', label='RK4')
plt.plot(x,yS, label='Solución analítica')
plt.title("Comparación de métodos RK2 y RK4")
plt.legend()
plt.grid()
plt.show()

Como se puede observar en la gráfica anterior, el método RK4 se aproxima más a la solución original
que el método RK2.

9
Referencias
● Chapra, S. C. (2015). Métodos numéricos para ingenieros (7th ed.). McGraw-Hill
Education.
● Córtes, J. (2005). Aproximación numérica y errores. Facultad de Ingeniería.
● Iriarte, L. (2010). Métodos numéricos. Trillas.
● Sauer, T. (2011). Numerical Analysis (2nd ed.). Pearson Education.Capítulo 10: "Ordinary
Differential Equations".
● Press, W. H., Teukolsky, S. A., Vetterling, W. T., & Flannery, B. P. (2007). Numerical
Recipes: The Art of Scientific Computing (3rd ed.). Cambridge University Press. Capítulo
17: "Ordinary Differential Equations".
● Trefethen, L. N., & Bau III, D. (1997). Numerical Linear Algebra. Society for Industrial
and Applied Mathematics (SIAM). Capítulo 8: "Solving Linear Systems".
● Chapra, S. C. (2012). Applied Numerical Methods with MATLAB for Engineers and
Scientists (3rd ed.). McGraw-Hill. Capítulo 25: "Ordinary Differential Equations".

También podría gustarte