Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Métodos Numéricos Con Jupyter Notebook
Métodos Numéricos Con Jupyter Notebook
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.
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">
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
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
4
tema2_solucionesNumericas
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
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:
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
xold = 100
xr = 101
i = 0
xl = 0
xu = 1.5
tol = 0.001
iteraciones = 0
###########################################
# 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()
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()
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.
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
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
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
#############################
# 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()
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)
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()
12
13
Unidad 3. Solución de sistemas de ecuaciones
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'))
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)
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
𝐴𝑥̄ = 𝑏̄
[𝐴][𝑋] = 𝐵
4
[𝐴][𝑋] − 𝐵 = 0
[𝑈 ][𝑋] − 𝐷 = 0
[𝐿][𝑈 ] = [𝐴]
[𝐿][𝐷] = [𝐵]
𝑙11 0 0 0
⎡𝑙 𝑙 0 0 ⎤
𝐿 = ⎢ 21 22 ⎥
⎢ 𝑙31 𝑙32 𝑙33 0 ⎥
⎣𝑙𝑛1 𝑙𝑛2 𝑙𝑛3 𝑙𝑛4 ⎦
𝑙𝑖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
1 0 0 0
⎡𝑙 1 0 0⎤
𝐿 = ⎢ 21 ⎥
⎢ 𝑙31 𝑙32 1 0⎥
⎣𝑙𝑛1 𝑙𝑛2 𝑙𝑛3 1⎦
𝑢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
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.
A = [3 -6 7; 8 0 -5; 1 -2 6]
A = 3×3
3 -6 7
8 0 -5
1 -2 6
b = [4 19 5]
b = 1×3
4 19 5
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
1
L
L = 3×3
3.0000 0 0
8.0000 16.0000 0
1.0000 0 3.6667
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
D = 3×1
1.3333
0.5208
1.0000
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
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.
A = [3 -6 7; 8 0 -5; 1 -2 6]
A = 3×3
3 -6 7
8 0 -5
1 -2 6
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);
for a = 1:N
L(a,a) = 1;
end
U(1,:) = A(1,:);
L(:,1)=A(:,1)/U(1,1);
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
X=zeros(N,1);
X(N)=D(N)/U(N,N);
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.
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
n n−1 n−2
y = b0 x + b1 x + b2 x + ⋯ + bn−1 x + bn
(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 )
In [ ]:
# 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
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
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
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
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
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
1
′
y = [−1 , 1] + er
0
h –––
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 –––
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
3π
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
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
𝑑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.
𝑑2 𝑦 𝑑𝑦
2
+ 5( )3 − 4𝑦 = 𝑒𝑥
𝑑𝑥 𝑑𝑥
𝑑𝑛 𝑦 𝑑𝑛−1 𝑦 𝑑2 𝑦 𝑑𝑦
𝑎𝑛 (𝑥) 𝑛
+ 𝑎 𝑛 − 1(𝑥) 𝑛−1
+ ... + 𝑎 2 (𝑥) 2
+ 𝑎1 (𝑥) + 𝑎0 (𝑥)𝑦 = 𝐹 (𝑥)
𝑑𝑥 𝑑𝑥 𝑑𝑥 𝑑𝑥
𝑦𝑖+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.
# 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)
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'
plt.scatter(x,y)
plt.plot(x,yS, color='red')
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)
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'
plt.scatter(x,y)
plt.plot(x,yS, color='red')
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, ...
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 𝑓(𝑥, 𝑦).
donde
𝑘1 = ℎ𝑓(𝑥𝑖 , 𝑦𝑖 )
𝑘2 = ℎ𝑓(𝑥𝑖 + ℎ, 𝑦𝑖 + 𝑘1 )
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
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
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)
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".