4.4 Trazadores lineales (Splines) grado1 con Python

[ Trazador Lineal ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
..


1. Trazadores lineales (Splines) grado1

Referencia: Chapra 18.6.1 p525
spline 01

El concepto de trazador se originó en la técnica de dibujo que usa una cinta delgada y flexible (spline) para dibujar curvas suaves a través de un conjunto de puntos.

La unión más simple entre dos puntos es una línea recta. El método crea un polinomio para cada par de puntos consecutivos en el intervalo, por lo que el resultado será una tabla de polinomios.

spline 02

Los trazadores de primer grado para un grupo de datos ordenados pueden definirse como un conjunto de funciones lineales.

f_0(x) = f(x_0) + m_0(x-x_0), x_0\leq x\leq x_1 f_1(x) = f(x_1) + m_1(x-x_1), x_1\leq x\leq x_2

...

f_n(x) = f(x_{n-1}) + m_{n-1}(x-x_{n-1}), x_{n-1}\leq x\leq x_n

donde

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

Observe que la expresión de f(x) para un tramo entre dos puntos es el polinomio de grado 1 realizado con diferencia finita avanzadas  o las diferencias divididas.

Las ecuaciones se pueden usar para evaluar la función en cualquier punto entre x0 y xn. Al localizar primero el intervalo dentro del cual está el punto, puede seleccionar el polinomio que corresponde a ese tramo.

[ Trazador Lineal ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
..


2. Ejercicio

Datos de los puntos como ejemplo para el algoritmo

xi = [0.1 , 0.2, 0.3, 0.4]
fi = [1.45, 1.8, 1.7, 2.0]

[ Trazador Lineal ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]

..


3. Desarrollo analítico

El método con trazadores lineales, permite plantear los bloques necesarios para manejar varios polinomios, uno por cada tramo entre dos puntos dentro del intervalo del problema

0.1 ≤ x≤ 0.2

m_0= \frac{1.8 - 1.45}{0.2-0.1} = 3.5 f_0(x) = 1.45 + 3.5(x-0.1)

0.2 ≤ x ≤ 0.3

m_1= \frac{1.7 - 1.8}{0.3-0.2} = -1 f_1(x) = 1.8 + -1(x-0.2)

0.3 ≤ x ≤ 0.4

m_2= \frac{2 - 1.7}{0.4-0.3} = 3 f_3(x) = 1.7 + 3(x-0.3)

El método permitirá disponer de un punto de partida para trazadores de mayor grado, por ejemplo los cúbicos.

spline 02

[ Trazador Lineal ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
..


4. Algoritmo en Python

Los polinomios de cada tramo se almacenan en una tabla, cada uno puede ser utilizado individualmente en su respectivo tramo, por ejemplo para realizar la gráfica de línea entre tramos.

# Trazador (spline) lineal, grado 1
import numpy as np
import sympy as sym

# INGRESO , Datos de prueba
xi = [0.1 , 0.2, 0.3, 0.4]
fi = [1.45, 1.8, 1.7, 2.0]

# PROCEDIMIENTO
# Vectores como arreglo, numeros reales
xi = np.array(xi,dtype=float)
fi = np.array(fi,dtype=float)
n = len(xi)

# trazador lineal, grado 1
x = sym.Symbol('x')
px_tabla = [] # por cada tramo

i = 0 # tramo inicial px = f0 +d1f*(x-x0)
while i<(n-1):
    # con 1ra diferencia finita avanzada
    d1f =(fi[i+1]-fi[i])/(xi[i+1]-xi[i])
    pxtramo = fi[i] + d1f*(x-xi[i])
    px_tabla.append(pxtramo)
    i = i + 1

# SALIDA
print('Polinomios por tramos: ')
for i in range(0,n-1,1):
    print('x = [',xi[i],',',xi[i+1],']')
    print('',px_tabla[i])

Se obtiene como resultado:

Polinomios por tramos: 
x = [ 0.1 , 0.2 ]
 3.5*x + 1.1
x = [ 0.2 , 0.3 ]
 2.0 - 1.0*x
x = [ 0.3 , 0.4 ]
 3.0*x + 0.8
>>> 

[ Trazador Lineal ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
..


5. Gráfica con Python

Para añadir la gráfica se añaden las instrucciones para:

  • evaluar el polinomio en cada tramo
  • concatenar los resultados de todos los tramos en los vectores xtraza, ytraza.
  • poner en la gráfica los puntos del problema y las líneas que genera cada polinomio
# GRAFICA --------------
import matplotlib.pyplot as plt

muestras = 5 # entre cada par de puntos
# Puntos para graficar cada tramo
xtraza = np.array([],dtype=float)
ytraza = np.array([],dtype=float)
i = 0
while i<(n-1): # cada tramo
    xtramo = np.linspace(xi[i],xi[i+1],muestras)
    # evalua polinomio del tramo
    pxt = sym.lambdify('x',px_tabla[i])
    ytramo = pxt(xtramo)
    # vectores de trazador en x,y
    xtraza = np.concatenate((xtraza,xtramo))
    ytraza = np.concatenate((ytraza,ytramo))
    i = i + 1

# Graficar
plt.plot(xi,fi,'o', label='puntos')
plt.plot(xtraza,ytraza, label='trazador')
plt.title('Trazadores lineales (splines)')
plt.xlabel('xi')
plt.ylabel('px(xi)')
plt.legend()
plt.show()

[ Trazador Lineal ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]

4.3 Interpolación polinómica de Lagrange con Python

[ Interpola Lagrange ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ] [ función ]
..


1. Interpolación polinómica de Lagrange

Referencia: Chapra 18.2 p516, Burden 3.1 p80, Rodríguez 6.2 p195

El polinomio de interpolación de Lagrange reformula el polinomio de interpolación de Newton evitando el cálculo de la tabla de diferencias divididas. El método de Lagrange tolera las diferencias entre distancias x de los puntos de muestra.

El polinomio de Lagrange se construye a partir de las fórmulas:

f_{n} (x) = \sum_{i=0}^{n} L_{i} (x) f(x_{i}) L_{i} (x) = \prod_{j=0, j \neq i}^{n} \frac{x-x_j}{x_i - x_j}

Donde una vez que se han seleccionado los puntos a usar que generan la misma cantidad de términos que puntos.

interpola Lagrange 01

[ Interpola Lagrange ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ] [ función ]
..


2. Ejercicio

Dados los 4 puntos en la tabla se requiere un polinomio de grado 3.

p(x) = a_0 x^3 + a_1 x^2 + a_2 x^1 + a_3
xi 0 0.2 0.3 0.4
fi 1 1.6 1.7 2.0

[ Interpola Lagrange ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ] [ función ]

..


3. Desarrollo analítico

Se calculan cuatro términos de Lagrange según las fórmulas,

término 1

L_{0} (x) = \frac{(x-0.2)(x-0.3)(x-0.4)}{(0-0.2)(0-0.3)(0-0.4)}

término 2

L_{1} (x) = \frac{(x-0)(x-0.3)(x-0.4)}{(0.2-0)(0.2-0.3)(0.2-0.4)}

término 3

L_{2} (x) = \frac{(x-0)(x-0.2)(x-0.4)}{(0.3-0)(0.3-0.2)(0.3-0.4)}

término 4

L_{3} (x) = \frac{(x-0)(x-0.2)(x-0.3)}{(0.4-0)(0.4-0.2)(0.4-0.3)}

se construye el polinomio usando la fórmula para fn(x) para cada valor fi,

p_3(x) = 1 L_{0} (x) + 1.6 L_{1} (x) + 1.7 L_{2} (x) + 2 L_{3} (x) p_3(x) = 1 \frac{(x-0.2)(x-0.3)(x-0.4)}{(0-0.2)(0-0.3)(0-0.4)} + + 1.6 \frac{(x-0)(x-0.3)(x-0.4)}{(0.2-0)(0.2-0.3)(0.2-0.4)} + 1.7 \frac{(x-0)(x-0.2)(x-0.4)}{(0.3-0)(0.3-0.2)(0.3-0.4)} + 2 \frac{(x-0)(x-0.2)(x-0.3)}{(0.4-0)(0.4-0.2)(0.4-0.3)}

La simplificación de la expresión del polinomio se puede dejar como tarea o realizarla con Sympy con la instrucción sym.expand().

p_3(x) = 41.6666 x^3 - 27.5 x^2 + 6.8333 x + 1

Una forma de verificar que el polinomio es correcto es usar un punto original xi[i] y comprobar que la evaluación tiene como resultado fi[i].

interpola lagrange 01

[ Interpola Lagrange ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ] [ función ]
..


3. Algoritmo en Python

En el algoritmo en Python, para construir las expresiones de cada término de Lagrange se usa la forma simbólica con Sympy, con los datos:

xi = [0, 0.2, 0.3, 0.4]
fi = [1, 1.6, 1.7, 2.0]

En cada término Li(x) se usan todos los elementos i , excepto el mismo elemento i, en el numerador y denominador de la expresión.

En polinomio se agrupan todos los términos multiplicados por su respectivo valor fi[i].

    valores de fi:  [1.  1.6 1.7 2. ]
divisores en L[i]:  [-0.024  0.004 -0.003  0.008]

Polinomio de Lagrange, expresiones
250.0*x*(x - 0.3)*(x - 0.2) +
400.0*x*(x - 0.4)*(x - 0.3) +
-566.666666666667*x*(x - 0.4)*(x - 0.2) +
-41.6666666666667*(x - 0.4)*(x - 0.3)*(x - 0.2)

Polinomio de Lagrange: 
41.6666666666667*x**3 - 27.5*x**2 + 6.83333333333334*x + 1.0

                  3         2                           
41.6666666666667⋅x  - 27.5⋅x  + 6.83333333333334⋅x + 1.0

Las expresiones del polinomio contiene los binomios en su forma básica, para resolver y simplificar las ecuaciones se usa polinomio.expand().

Para realizar la gráfica del polinomio es conveniente convertirlo a la forma lambda con Numpy, de esta forma se evalúa en una sola linea todos los puntos para el intervalo [a,b] en x.

# Interpolacion de Lagrange
# divisoresL solo para mostrar valores denominador
import numpy as np
import sympy as sym

# INGRESO , Datos de prueba
xi = [0, 0.2, 0.3, 0.4]
fi = [1, 1.6, 1.7, 2.0]

# PROCEDIMIENTO
# Vectores como arreglo, numeros reales
xi = np.array(xi,dtype=float)
fi = np.array(fi,dtype=float)
n = len(xi)

# Polinomio de Lagrange
x = sym.Symbol('x')
polinomio = 0*x   # sym.S.Zero en Sympy
divisorL = np.zeros(n, dtype = float)
for i in range(0,n,1):
    
    # Termino de Lagrange
    numerador = 1
    denominador = 1
    for j  in range(0,n,1):
        if (j!=i):
            numerador = numerador*(x-xi[j])
            denominador = denominador*(xi[i]-xi[j])
    terminoLi = numerador/denominador

    polinomio = polinomio + terminoLi*fi[i]
    divisorL[i] = denominador

polisimple = polinomio.expand() # simplifica los (x-xi)
px = sym.lambdify(x,polisimple) # evaluacién numérica

# SALIDA
print('    valores de fi: ',fi)
print('divisores en L[i]: ',divisorL)
print()
print('Polinomio de Lagrange, expresiones')
#print(polinomio)
terminos = sym.Add.make_args(polinomio)
n_term = len(terminos)
for i in range(0,n_term,1):
    if i<(n_term-1):
        print(terminos[i],'+')
    else:
        print(terminos[i])
print()
print('Polinomio de Lagrange: ')
print(polisimple)
print()
sym.pprint(polisimple)

[ Interpola Lagrange ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ] [ función ]
..


4. Gráfica del polinomio

Para la gráfica se añaden las siguientes instrucciones

# Gráfica --------------
import matplotlib.pyplot as plt
muestras = 21   # resolución gráfica

a = np.min(xi)  # intervalo [a,b]
b = np.max(xi)
xk = np.linspace(a,b,muestras)
yk = px(xk)

plt.plot(xi,fi,'o', label = '[xi,fi]')
plt.plot(xk,yk, label = 'p(x)')
plt.legend()
plt.xlabel('xi')
plt.ylabel('fi')
plt.title('Interpolación Lagrange')
plt.show()

[ Interpola Lagrange ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ] [ función ]
..


5.Función en librería Scipy.interpolate.lagrange

>>> import scipy as sp
>>> xi = [0,0.2,0.3,0.4]
>>> fi = [1,1.6,1.7,2.0]
>>> sp.interpolate.lagrange(xi,fi)
poly1d([ 41.66666667, -27.5       ,   6.83333333,   1.        ])
>>> 

[ Interpola Lagrange ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ] [ función ]

 

4.2.2 Interpolación por Diferencias divididas de Newton (Δx) con Python

[ Dif_Divididas_Newton] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
[ función ]
..


1. Diferencias divididas de Newton

Referencia: Chapra 18.1.3 p508, Rodríguez 6.7 p223, Burden 9Ed 3.3 p124.

El método se usa en el caso que los puntos en el "eje x" se encuentran espaciados de forma arbitraria y provienen de una función desconocida pero supuestamente diferenciable.

Diferencias Divididas de Newton interpolación polinómica

La n-ésima diferencia dividida finita es:

f[x_{n}, x_{n-1}, \text{...}, x_{1}, x_{0}] = \frac{f[x_{n}, x_{n-1}, \text{...}, x_{1}]- f[x_{n-1}, x_{n-2}, \text{...}, x_{0}]}{x_{n}-x_{0}}

Para lo cual se debe interpretar la tabla de diferencias divididas, también como una aproximación a una derivada:

i xi f[xi] Primero Segundo Tercero
0 x0 f[x0] f[x1,x0] f[x2,x1,x0] f[x3,x2,x1,x0]
1 x1 f[x1] f[x2,x1] f[x3,x2,x1]
2 x2 f[x2] f[x3,x2]
3 x2 f[x3]

En la fórmula del polinomio, las diferencias divididas sirven para evaluar los coeficientes de cada término adicional para aumentar el grado. Semejante al proceso realizado para  "diferencias finitas divididas":

f_n(x) = f(x_0)+(x-x_0)f[x_1,x_0] + + (x-x_0)(x-x_1)f[x_2,x_1,x_0] + \text{...}+ + (x-x_0)(x-x_1)\text{...}(x-x_{n-1})f[x_n,x_{n-1},\text{...},x_0]

Este polinomio de interpolación se conoce como de Newton en diferencias divididas.

[ Dif_Divididas_Newton] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
[ función ]
..


2. Ejercicio

Referencia: 1Eva_IIT2008_T3_MN Ganancia en inversión inversion Ganancia 01

Se dispone de los datos (x, f(x)), en donde x es un valor de inversión y f(x) es un valor de ganancia, ambos en miles de dólares:

inversión ganancia
3.2 5.12
3.8 6.42
4.2 7.25
4.5 6.85

Considere que los valores invertidos en materia prima para producción, dependen de la demanda de del producto en el mercado, motivo por el que los valores de inversión no guardan distancias equidistantes entre si.

[ Dif_Divididas_Newton] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
[ función ]

..


3. Desarrollo analítico

Se toman los datos de la tabla como arreglos para el algoritmo

xi = [3.2 , 3.8 , 4.2 , 4.5 ]
fi = [5.12, 6.42, 7.25, 6.85]

Con los datos se llena la tabla de diferencias divididas, donde por simplicidad, se escriben las operaciones en cada casilla. La última columna o cuarta diferencia dividida es cero por no disponer de datos para hacer el cálculo.

i xi f[xi] Primero Segundo Tercero
0 3.2 5.12 \frac{6.42-5.12}{3.8-3.2} =2.1667 \frac{2.075-2.1667}{4.2-3.2} =-0.0917 \frac{-4.869-(-0.0917)}{4.5-3.2} =-3.6749
1 3.8 6.42 \frac{7.25-6.42}{4.2-3.8} =2.075 \frac{-1.3333-2.075}{4.5-3.8} =-4.869
2 4.2 7.25 \frac{6.85-7.25}{4.5-4.2} =-1.3333
3 4.5 6.85

Las diferencias divididas de la primera fila son los valores usados para la expresión del polinomio de interpolación:

ddividida = tabla[0,3:] 
          = [ 2.1667, -0.0917, -3.6749, 0. ]

La expresión del polinomio inicia con el primer valor de la función f(x0).

p_3(x) = f_0 = 5.12

se calcula el primer término usando el factor de la primera diferencia dividida y se añade a la expresión del polinomio.

término = (x-x_0)f[x1,x0] = (x-3.2)2.1667 p_3(x) = 5.12 + 2.1667(x-3.2)

con el siguiente valor de ddividida[] se procede de manera semejante:

término = (x-x_0)(x-x_1)f[x1,x0] = = (x-3.2)(x-3.8)(-0.0917) p_3(x) = 5.12 + 2.1667(x-3.2)+ + (-0.0917)(x-3.2)(x-3.8)

Realizando el cálculo del tercer y último termino con diferencias divididas,  el polinomio obtenido es:

p_3(x) = 5.12 + 2.1667(x-3.2) + + (-0.0917)(x-3.2)(x-3.8) + + (-3.6749)(x - 3.2)(x - 3.8)(x - 4.2)

que simplificando al multiplicar entre los términos (x-xi):

p_3(x) = 184.7569 - 149.9208 x + 41.0673 x^2 - 3.6749 x^3

El polinomio en el intervalo de xi, en la gráfica muestra que pasa por todos los puntos.

Diferencias Divididas 02

[ Dif_Divididas_Newton] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
[ función ]
..


4. Algoritmo en Python

El algoritmo para  interpolación de Diferencias Divididas o Newton, considera reutilizar el procedimiento de cálculo de diferencias finitas incorporando la parte del denominador.

La creación de la expresión del polinomio también es semejante a la usada para diferencias finitas avanzadas.

Se incorpora la parte gráfica para observar los resultados en el intervalo xi, con el número de muestras = 101 para tener una buena resolución de la línea del polinomio.

# Diferencias Divididas de Newton -Interpolación
# Tarea: Verificar tamaño de vectores,
import numpy as np
import sympy as sym

# INGRESO
xi = [3.2 , 3.8 , 4.2 , 4.5 ]
fi = [5.12, 6.42, 7.25, 6.85]

# PROCEDIMIENTO
casicero = 1e-15  # redondear a cero
# Matrices como arreglo, numeros reales
xi = np.array(xi,dtype=float)
fi = np.array(fi,dtype=float)
n = len(xi)

# Tabla de Diferencias Divididas
tabla_etiq = ['i   ','xi  ','fi  ']
ki = np.arange(0,n,1) # filas
tabla = np.concatenate(([ki],[xi],[fi]),axis=0)
tabla = np.transpose(tabla)
dfinita = np.zeros(shape=(n,n),dtype=float)
tabla = np.concatenate((tabla,dfinita), axis=1)
n,m = np.shape(tabla)
# Calcula tabla de Diferencias Divididas
diagonal = n-1 # derecha a izquierda
j = 3  # inicia en columna 3
while (j < m): # columna
    tabla_etiq.append('F['+str(j-2)+']') # título columna
    i = 0 # cada fila de columna
    paso = j-2 # inicia en 1
    while (i < diagonal): # antes de diagonal
        tabla[i,j] = tabla[i+1,j-1]-tabla[i,j-1]

        denominador = (xi[i+paso]-xi[i])
        tabla[i,j] = tabla[i,j]/denominador

        if abs(tabla[i,j])<casicero: # casicero revisa
            tabla[i,j]=0
        i = i + 1
    diagonal = diagonal - 1
    j = j + 1
    
dDividida = tabla[0,3:] # diferencias Divididas

# polinomio con diferencias Divididas de Newton
x = sym.Symbol('x')
polinomio = fi[0] +0*x # en Sympy
for i in range(1,n,1):
    factor = dDividida[i-1]
    termino = 1
    for j in range(0,i,1):
        termino = termino*(x-xi[j])
    polinomio = polinomio + termino*factor

polisimple = polinomio.expand() # simplifica los (x-xi)
px = sym.lambdify(x,polisimple) # evaluación numérica

# SALIDA
np.set_printoptions(precision = 4)
print('Tabla Diferencia Dividida-Newton')
print([tabla_etiq])
print(tabla)
print('dDividida: ')
print(dDividida)
print('polinomio: ')
print(polinomio)
print('polinomio simplificado: ' )
print(polisimple)

El resultado del algoritmo se muestra a continuación:

Tabla Diferencia Dividida-Newton
[['i   ', 'xi  ', 'fi  ', 'F[1]', 'F[2]', 'F[3]', 'F[4]']]
[[ 0.      3.2     5.12    2.1667 -0.0917 -3.6749  0.    ]
 [ 1.      3.8     6.42    2.075  -4.869   0.      0.    ]
 [ 2.      4.2     7.25   -1.3333  0.      0.      0.    ]
 [ 3.      4.5     6.85    0.      0.      0.      0.    ]]
dDividida: 
[ 2.1667 -0.0917 -3.6749  0.    ]
polinomio: 
2.16666666666667*x - 3.67490842490842*(x-4.2)*(x-3.8)*(x-3.2)
 - 0.0916666666666694*(x - 3.8)*(x - 3.2) - 1.81333333333334
polinomio simplificado: 
-3.67490842490842*x**3 + 41.0673076923077*x**2 
- 149.920860805861*x + 184.756923076923
>>> 

[ Dif_Divididas_Newton] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
[ función ]
..


5. Gráfica de polinomio de interpolación

# GRAFICA  --------------
import matplotlib.pyplot as plt

titulo = 'Interpolación: Diferencias Divididas - Newton'
muestras = 21  # resolución gráfica

a = np.min(xi) # intervalo [a,b]
b = np.max(xi)
xk = np.linspace(a,b,muestras)
yk = px(xk)

plt.plot(xi,fi,'o', label='[xi,fi]')
plt.plot(xk,yk, label='p(x)')

try: # existen mensajes de error
    msj_existe = len(msj)
except NameError:
    msj = []
if len(msj)>0: # tramos con error
    untipo = msj[0][0]
    donde = msj[0][1] # indice error
    plt.plot(xi[donde:donde+2],fi[donde:donde+2],
             'ro',label=untipo)
    plt.plot(xi[donde:donde+2],fi[donde:donde+2],
             'r--')

plt.xlabel('xi')
plt.ylabel('fi')
plt.legend()
plt.title(titulo)
plt.show()

[ Dif_Divididas_Newton] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
[ función ]
..


6. Algoritmo como función

Se reordena el algoritmo para usar funciones, la gráfica es la misma que para el algoritmo sin funciones anterior

El bloque de salida es semejante al resultado anterior.

# Diferencias Divididas de Newton - Interpolación
# funciones diferencias_tabla
# Tarea: Verificar tamaño de vectores
import numpy as np
import math
import sympy as sym

def diferencias_tabla(xi,fi,tipo='finitas', vertabla=False,
                casicero = 1e-15, precision=4):
    '''Genera la tabla de diferencias finitas o divididas
      tipo = 'finitas, tipo = 'divididas'
    resultado en: tabla, titulo
    Tarea: verificar tamaño de vectores
    '''
    # revisa tipo de tabla
    tipolist = ['finitas','divididas']
    if not(tipo in tipolist):
        print('error de tipo, seleccione:',tipolist)
        return()
    
    prefijo = ['d','f']
    if tipo=='divididas':
        prefijo = ['F[',']']
    
    # Matrices como arreglo, numeros reales
    xi = np.array(xi,dtype=float)
    fi = np.array(fi,dtype=float)
    n = len(xi)

    # Tabla de Diferencias Finitas/Divididas
    tabla_etiq = ['i','xi','fi']
    ki = np.arange(0,n,1) # filas
    tabla = np.concatenate(([ki],[xi],[fi]),axis=0)
    tabla = np.transpose(tabla)
    dfinita = np.zeros(shape=(n,n),dtype=float)
    tabla = np.concatenate((tabla,dfinita), axis=1)
    n,m = np.shape(tabla)
    # Calular tabla de Diferencias Finitas/Divididas
    diagonal = n-1 # derecha a izquierda
    j = 3  # inicia en columna 3
    while (j < m): # columna
        tabla_etiq.append(prefijo[0]+str(j-2)+prefijo[1])
        # paso = j-2 # inicia en 1
        i = 0 # fila
        while (i < diagonal): # antes de diagonal
            tabla[i,j] = tabla[i+1,j-1]-tabla[i,j-1]

            if tipo=='divididas':
                # denominador = xi[i+paso]-xi[i]
                denominador = xi[i+j-2]-xi[i]
                tabla[i,j] = tabla[i,j]/denominador
                
            if abs(tabla[i,j])<casicero: # casicero revisa
                    tabla[i,j]=0
            i = i + 1
        diagonal = diagonal - 1
        j = j + 1
            
    if vertabla==True:
        np.set_printoptions(precision)
        print('Tabla de Diferencias',tipo)
        print([tabla_etiq])
        print(tabla)
    return([tabla, tabla_etiq])

# PROGRAMA ---------------------

# INGRESO
xi = [3.2, 3.8, 4.2, 4.5]
fi = [5.12, 6.42, 7.25, 6.85]

tipo_tabla = 'divididas'

# PROCEDIMIENTO
casicero = 1e-12
# Vectores como arreglo, numeros reales
xi = np.array(xi,dtype=float)
fi = np.array(fi,dtype=float)
n = len(xi)

tabla,tabla_etiq = diferencias_tabla(xi,fi,
                        tipo=tipo_tabla,
                        vertabla=True,casicero = casicero)

dfinita = tabla[0,3:] # diferencias divididas
h = xi[1] - xi[0]     # suponer tramos equidistantes

# polinomio con diferencias Finitas Avanzadas/ divididas
x = sym.Symbol('x')
polinomio = fi[0] +0*x # sym.S.Zero en Sympy
for i in range(1,n,1):
    factor = dfinita[i-1] # diferencias divididas
    if tipo_tabla=='finitas': # diferencias Finitas Avanzadas
        denominador = math.factorial(i)*(h**i)
        factor = factor/denominador
    termino = 1
    for j in range(0,i,1):
        termino = termino*(x-xi[j])
    polinomio = polinomio + termino*factor

polisimple = polinomio.expand() # simplifica los (x-xi)
px = sym.lambdify(x,polisimple) # evaluacion numerica

# SALIDA
print('d'+tipo_tabla+': ')
print(dfinita)
print('Tramo h:',h)
print('polinomio: ')
#print(polinomio)
terminos = sym.Add.make_args(polinomio)
n_term = len(terminos)
for i in range(0,n_term,1):
    if i<(n_term-1):
        print(terminos[i],'+')
    else:
        print(terminos[i])
print()
print('polinomio simplificado: ' )
print(polisimple)

La gráfica se realiza con el bloque de la sección anterior


[ Dif_Divididas_Newton] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
[ función ]

4.2.1 Interpolación por Diferencias finitas avanzadas (h) con Python

Dif_Finitas avanzadas ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
[ función ]
..


1. Interpolación por Diferencias finitas avanzadas

Referencia: Rodríguez 6.6.4 p221, Burden 9Ed p129

Se usa en interpolación cuando los puntos en el "eje x" se encuentran igualmente espaciados, la diferencia entre puntos consecutivos xi es una constante denominada h.

h = xi+1 - xi

diferencias finitas Avanzadas GIF animado
Una relación entre derivadas y diferencias finitas se establece mediante: f^{(n)}(z) = \frac{\Delta ^{n} f_{0}}{h^{n}} para algún z en el intervalo [x0,xn].

\frac{\Delta ^{n} f_{0}}{h^{n}} es una aproximación para la n-ésima derivada f(n)

El polinomio de interpolación se puede construir por medio de diferencias finitas avanzadas con las siguiente fórmula:

p_n (x) = f_0 + \frac{\Delta f_0}{h} (x - x_0) + + \frac{\Delta^2 f_0}{2!h^2} (x - x_0)(x - x_1) + + \frac{\Delta^3 f_0}{3!h^3} (x - x_0)(x - x_1)(x - x_2) + \text{...} + \frac{\Delta^n f_0}{n!h^n} (x - x_0)(x - x_1) \text{...} (x - x_{n-1})

Observe que en la medida que se toman más puntos de muestra, el grado del polinomio puede ser mayor.

polinomio de diferencias finitas avanzadas

Dif_Finitas avanzadas ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
[ función ]
..


2. Ejercicio

Se toman los datos del ejercicio de diferencias finitas , observando que se requiere que el tamaño de paso h  sea constante entre los puntos consecutivos xi.

xi = [0.1, 0.2, 0.3, 0.4]
fi = [1.45, 1.6, 1.7, 2.0]
xi 0.1 0.2 0.3 0.4
fi 1.45 1.6 1.7 2.0

Para éste ejercicio se comprueba que h = 0.1

Dif_Finitas avanzadas ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
[ función ]
..


3. Desarrollo Analítico

Se inicia el cálculo de la distancia entre puntos xi, comprobando que debe ser constante h, además que los valores xi deben encontrarse ordenados de forma ascendente:

h = xi[1] - xi[0] = 0.2-0.1 = 0.1
h = xi[2] - xi[1] = 0.3-0.2 = 0.1
h = xi[3] - xi[2] = 0.4-0.3 = 0.1

Se usan los resultados previos del ejercicio con diferencias finitas:

Tabla Diferencia Finita: 
[['i', 'xi', 'fi', 'd1f', 'd2f', 'd3f', 'd4f']]
[[ 0.    0.1   1.45  0.15 -0.05  0.25  0.  ]
 [ 1.    0.2   1.6   0.1   0.2   0.    0.  ]
 [ 2.    0.3   1.7   0.3   0.    0.    0.  ]
 [ 3.    0.4   2.    0.    0.    0.    0.  ]]

Como los tamaños de paso en xi son constantes, h=0.1, es posible usar el método. Para la construcción del polinomio, se usan los valores de diferencias finitas de la primera fila desde la columna 3 en adelante.

dfinita = tabla[0,3:] = [0.15, -0.05, 0.25, 0.]

Se empieza con el valor del primer punto de la función f(0.1), j=0.

p3(x) = f0 = 1.45

para añadirle el término j=1, más el factor por calcular:

factor = \frac{\Delta^1 f_0}{1!h^1} = \frac{0.15}{1!(0.1)} = 1.5

completando el término como:

término = factor(x-x_0) = 1.5(x-0.1) p_3(x) = 1.45 + 1.5(x-0.1)

Para el segundo término j=2, se repite el proceso:

factor = \frac{\Delta^2 f_0}{2!h^2} = \frac{-0.05}{2!(0.1)^2} = -2.5 término = factor(x-x_0) = -2.5(x-0.1)(x-0.2) p_3(x)= 1.45 + 1.5(x-0.1) +(-2.5)(x-0.1)(x-0.2)

Finalmente, añadiendo el término j=3 cuyo cálculo es semejante a los anteriores, se deja como tarea.

El resultado del método es:

p_3(x)= 1.45 + 1.5(x-0.1) -2.5(x-0.1)(x-0.2) + + 41.667(x - 0.3)(x - 0.2)(x - 0.1)

Se puede seguir simplificando la respuesta, por ejemplo usando solo el término de grado con 1.5(x-0.1) se tiene que:

p_3(x) = 1.3 + 1.5x -2.5(x-0.1)(x-0.2)+ + 41.667(x - 0.3)(x - 0.2)(x - 0.1)

Seguir simplificando la expresión en papel, se deja como tarea.

La gráfica del polinomio obtenido comprueba que pasa por cada uno de los puntos dados para el ejercicio.

interpolación polinómica

Dif_Finitas avanzadas ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
[ función ]
..


4. Algoritmo en Python

El polinomio se construye usando el ejercicio de diferencias finitas.

Para construir la expresión del polinomio añadiendo los términos de la fórmula, se define la variable simbólica x con Sympy.

Para simplificar el polinomio resultante con las expresiones de multiplicación, se utiliza la instrucción sym.expand().

En caso de requerir evaluar la fórmula con un vector de datos se la convierte a la forma lambda para evaluación numérica.

Se añaden las instrucciones para realizar la gráfica en el intervalo [a,b] dado por los valores xi, definiendo el número de muestras = 21 que son suficientes para que la gráfica se observe sin distorsión.

Se añade un bloque para la revisión que los puntos xi sean equidistantes y se encuentren en orden.

# Diferencias finitas Avanzadas - Interpolación
# Tarea: Verificar tamaño de vectores
import numpy as np
import math
import sympy as sym

# INGRESO
xi = [0.1, 0.2, 0.3, 0.4]
fi = [1.45, 1.6, 1.7, 2.0]

# PROCEDIMIENTO
casicero = 1e-15
# Vectores como arreglo, numeros reales
xi = np.array(xi,dtype=float)
fi = np.array(fi,dtype=float)
n = len(xi)

msj = [] # mensajes de error

# xi revisar orden ascendente
tramos  = np.diff(xi,1)  # diferencias en xi
donde = -1  # suponer ordenados
i = 0
while i<(n-1) and donde<0:
    if tramos[i]<0:
        donde = i+1
        msj.append(['sin orden en',donde])
    i = i+1

# xi tramos desiguales o no equidistantes
if (n-1)>=2: # len(tramos)>=2
    dtramos = np.diff(tramos,1) # cero o menores a casicero
    errado  = np.max(np.abs(dtramos)) # |error| mayor
    donde = -1 # suponer equidistantes
    if errado>=casicero:  # no equidistantes
        donde = np.argmax(np.abs(dtramos))+1
        msj.append(['no equidistantes',donde])


# Tabla de Diferencias Finitas
tabla_etiq = ['i','xi','fi']
ki = np.arange(0,n,1) # filas
tabla = np.concatenate(([ki],[xi],[fi]),axis=0)
tabla = np.transpose(tabla)
dfinita = np.zeros(shape=(n,n),dtype=float)
tabla = np.concatenate((tabla,dfinita), axis=1)
n,m = np.shape(tabla)
# Calular tabla de Diferencias Finitas
diagonal = n-1 # derecha a izquierda
j = 3  # inicia en columna 3
while (j < m): # columna
    tabla_etiq.append('d'+str(j-2)+'f')
    i = 0 # fila
    while (i < diagonal): # antes de diagonal
        tabla[i,j] = tabla[i+1,j-1]-tabla[i,j-1]

        if abs(tabla[i,j])<casicero: # casicero revisa
                tabla[i,j]=0
        i = i + 1
    diagonal = diagonal - 1
    j = j + 1

dfinita = tabla[0,3:] # diferencias finitas
h = xi[1] - xi[0]     # suponer tramos equidistantes

# polinomio con diferencias Finitas Avanzadas
x = sym.Symbol('x')
polinomio = fi[0] +0*x # en Sympy
for i in range(1,n,1):
    denominador = math.factorial(i)*(h**i)
    factor = dfinita[i-1]/denominador
    termino = 1
    for j in range(0,i,1):
        termino = termino*(x-xi[j])
    polinomio = polinomio + termino*factor

polisimple = polinomio.expand() # simplifica los (x-xi)
px = sym.lambdify(x,polisimple) # evaluación numérica

# SALIDA
if len(msj)>0: # mensajes de error
    print('Revisar tramos, d1x:',tramos)
    for unmsj in msj:
        print('Tramos',unmsj[0],
              'desde i:',unmsj[1])
else:
    print('Tramo h:',h)
print('Tabla Diferencia Finita')
print([tabla_etiq])
print(tabla)
print('dfinita: ')
print(dfinita)
print('polinomio: ')
print(polinomio)
print('polinomio simplificado: ' )
print(polisimple)

teniendo como resultado:

Tramo h: 0.1
Tabla Diferencia Finita
[['i', 'xi', 'fi', 'd1f', 'd2f', 'd3f', 'd4f']]
[[ 0.    0.1   1.45  0.15 -0.05  0.25  0.  ]
 [ 1.    0.2   1.6   0.1   0.2   0.    0.  ]
 [ 2.    0.3   1.7   0.3   0.    0.    0.  ]
 [ 3.    0.4   2.    0.    0.    0.    0.  ]]
dfinita: 
[ 0.15 -0.05  0.25  0.  ]
polinomio: 
1.5*x + 41.6666666666667*(x - 0.3)*(x - 0.2)*(x - 0.1) - 2.50000000000001*(x - 0.2)*(x - 0.1) + 1.3
polinomio simplificado: 
41.6666666666667*x**3 - 27.5*x**2 + 6.83333333333335*x + 0.999999999999999

el polinomio de puede evaluar como px(valor) una vez que se convierte a la forma lambda para usar con Numpy:

>>> px(0.1)
1.4500000000000004
>>> px(0.2)
1.6000000000000025
>>> px0.3)
1.7000000000000042
>>> 

Dif_Finitas avanzadas ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
[ función ]
..


5. Gráfica de polinomio de interpolación

Las instrucciones son semejantes a las presentadas en polinomio de interpolación. Se añade instrucciones para el caso en que los puntos no sean equidistantes en el eje de las x.

Como el bloque de gráfica se usa en otros métodos de la unidad, se incorpora la verificación de lista de errores 'msj' usando el bloque 'try-except'.

# GRAFICA --------------
import matplotlib.pyplot as plt

titulo = 'Interpolación: Diferencias Finitas Avanzadas'
muestras = 21  # resolución gráfica

a = np.min(xi) # intervalo [a,b]
b = np.max(xi)
xk = np.linspace(a,b,muestras)
yk = px(xk)

plt.plot(xi,fi,'o', label='[xi,fi]')
plt.plot(xk,yk, label='p(x)')

try: # existen mensajes de error
    msj_existe = len(msj)
except NameError:
    msj = []
    
if len(msj)>0: # tramos con error
    untipo = msj[0][0]
    donde = msj[0][1] # indice error
    plt.plot(xi[donde:donde+2],fi[donde:donde+2],
             'ro',label=untipo)
    plt.plot(xi[donde:donde+2],fi[donde:donde+2],
             'r--')
    
plt.xlabel('xi')
plt.ylabel('fi')
plt.legend()
plt.title(titulo)
plt.show()

diferencias finitas avanzadas 01

Tarea: se recomienda realizar las gráficas comparativas entre métodos, debe mostrar la diferencia con los métodos que requieren el tamaño de paso equidistante h, y los que no lo requieren. Permite detectar errores de selección de método para interpolación.

Dif_Finitas avanzadas ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
[ función ]
..


6. Algoritmo como función

Se reordena el algoritmo para usar funciones, la gráfica es la misma que para el algoritmo sin funciones anterior

El bloque de salida es semejante al resultado anterior. Las funciones pueden ser reutilizadas de ser necesarias en el siguiente método de interpolación.

Tabla de Diferencias finitas
[['i', 'xi', 'fi', 'd1f', 'd2f', 'd3f', 'd4f']]
[[ 0.    0.1   1.45  0.15 -0.05  0.25  0.  ]
 [ 1.    0.2   1.6   0.1   0.2   0.    0.  ]
 [ 2.    0.3   1.7   0.3   0.    0.    0.  ]
 [ 3.    0.4   2.    0.    0.    0.    0.  ]]
xi_ascendente: True
tramos xi Equidistantes: True
Tramo h: 0.1
polinomio: 
1.30000000000000 +
1.5*x +
-2.50000000000001*(x - 0.2)*(x - 0.1) +
41.6666666666667*(x - 0.3)*(x - 0.2)*(x - 0.1)

polinomio simplificado: 
41.6666666666667*x**3 - 27.5*x**2 + 6.83333333333335*x + 0.999999999999999

Instrucciones en Python

# Diferencias finitas Avanzadas - Interpolación
# funciones dif_finitas
# Tarea: Verificar tamaño de vectores
import numpy as np
import math
import sympy as sym

def revisa_orden(xi,orden='up'):
    ''' Revisa orden en vector xi.
      orden='up': orden ascendente
      orden='down' : orden descendente
    resultado:
      True:  xi ordenado,
      False: xi desordenado y dónde.
    '''
    # Vectores como arreglo, numeros reales
    xi = np.array(xi,dtype=float)
    n = len(xi)
    
    msj = [] # mensajes de error
    # xi revisar orden ascendente o descendente
    tramos  = np.diff(xi,1)  # diferencias en xi
    ordenado = True  # suponer ordenados
    k = 1 # 1: ascendente
    if orden=='down':
        k = -1 # -1: descendente
    donde = -1
    i = 0
    while i<(n-1) and donde<0:
        if k*tramos[i]<0:
            ordenado = False
            donde = i+1
            msj.append(['sin orden',donde])
        i = i+1
    return([ordenado,msj])

def tramos_equidistantes(xi, casicero = 1e-15):
    ''' Revisa tamaños de paso h en vector xi.
    True:  h son equidistantes,
    False: h tiene tamaño de paso diferentes y dónde.
    '''
    # Vectores como arreglo, numeros reales
    xi = np.array(xi,dtype=float)
    n = len(xi)
    msj = [] # mensajes de error
    
    # revisa tamaños de paso desiguales o no equidistantes
    h_iguales = True
    if (n-1)>=2: # al menos dos tramos
        dtramos = np.diff(xi,2) # cero o menores a casicero
        errado  = np.max(np.abs(dtramos)) # |error| mayor
        if errado>=casicero:  # no equidistantes
            h_iguales=False
            donde = np.argmax(np.abs(dtramos))+1
            msj.append(['no equidistantes',donde])

    return([h_iguales,msj])

def diferencias_tabla(xi,fi,tipo='finitas', vertabla=False,
                casicero = 1e-15, precision=4):
    '''Genera la tabla de diferencias finitas o divididas
      tipo = 'finitas, tipo = 'divididas'
    resultado en: tabla, titulo
    Tarea: verificar tamaño de vectores
    '''
    # revisa tipo de tabla
    tipolist = ['finitas','divididas']
    if not(tipo in tipolist):
        print('error de tipo, seleccione:',tipolist)
        return()
    
    prefijo = ['d','f']
    if tipo=='divididas':
        prefijo = ['F[',']']
    
    # Matrices como arreglo, numeros reales
    xi = np.array(xi,dtype=float)
    fi = np.array(fi,dtype=float)
    n = len(xi)

    # Tabla de Diferencias Finitas/Divididas
    tabla_etiq = ['i','xi','fi']
    ki = np.arange(0,n,1) # filas
    tabla = np.concatenate(([ki],[xi],[fi]),axis=0)
    tabla = np.transpose(tabla)
    dfinita = np.zeros(shape=(n,n),dtype=float)
    tabla = np.concatenate((tabla,dfinita), axis=1)
    n,m = np.shape(tabla)
    # Calular tabla de Diferencias Finitas/Divididas
    diagonal = n-1 # derecha a izquierda
    j = 3  # inicia en columna 3
    while (j < m): # columna
        tabla_etiq.append(prefijo[0]+str(j-2)+prefijo[1])
        # paso = j-2 # inicia en 1
        i = 0 # fila
        while (i < diagonal): # antes de diagonal
            tabla[i,j] = tabla[i+1,j-1]-tabla[i,j-1]

            if tipo=='divididas':
                # denominador = xi[i+paso]-xi[i]
                denominador = xi[i+j-2]-xi[i]
                tabla[i,j] = tabla[i,j]/denominador
                
            if abs(tabla[i,j])<casicero: # casicero revisa
                    tabla[i,j]=0
            i = i + 1
        diagonal = diagonal - 1
        j = j + 1
            
    if vertabla==True:
        np.set_printoptions(precision)
        print('Tabla de Diferencias',tipo)
        print([tabla_etiq])
        print(tabla)
    return([tabla, tabla_etiq])

# PROGRAMA ---------------------

# INGRESO
xi = [0.1, 0.2, 0.3, 0.4]
fi = [1.45, 1.6, 1.7, 2.0]

tipo_tabla = 'finitas'

# PROCEDIMIENTO
casicero = 1e-12
# Vectores como arreglo, numeros reales
xi = np.array(xi,dtype=float)
fi = np.array(fi,dtype=float)
n = len(xi)

xi_ascendente,msj = revisa_orden(xi)
h_iguales,msj_h = tramos_equidistantes(xi, casicero = casicero)
if len(msj_h)>0:
    msj.extend(msj_h)   
tabla,titulo = diferencias_tabla(xi,fi,tipo=tipo_tabla,
                                 vertabla=True, casicero = casicero)

dfinita = tabla[0,3:] # diferencias finitas
h = xi[1] - xi[0]     # suponer tramos equidistantes

# polinomio con diferencias Finitas Avanzadas/ divididas
x = sym.Symbol('x')
polinomio = fi[0] +0*x # sym.S.Zero en Sympy
for i in range(1,n,1):
    factor = dfinita[i-1] # diferencias divididas
    if tipo_tabla=='finitas': # diferencias Finitas Avanzadas
        denominador = math.factorial(i)*(h**i)
        factor = factor/denominador
    termino = 1
    for j in range(0,i,1):
        termino = termino*(x-xi[j])
    polinomio = polinomio + termino*factor

polisimple = polinomio.expand() # simplifica los (x-xi)
px = sym.lambdify(x,polisimple) # evaluacion numerica

# SALIDA
print('xi_ascendente:',xi_ascendente)
print('tramos xi Equidistantes:',h_iguales)
if len(msj)>0: # mensajes de error
    print('Revisar tramos, d1x:',np.diff(xi,1))
    for unmsj in msj:
        print('Tramos',unmsj[0],
              'desde i:',unmsj[1])
else:
    print('Tramo h:',h)

print('d'+tipo_tabla+': ')
print(dfinita)
print('polinomio: ')
#print(polinomio)
terminos = sym.Add.make_args(polinomio)
n_term = len(terminos)
for i in range(0,n_term,1):
    if i<(n_term-1):
        print(terminos[i],'+')
    else:
        print(terminos[i])
print()
print('polinomio simplificado: ' )
print(polisimple)

La gráfica se genera con el mismo bloque de instrucciones de la sección gráfica.

Dif_Finitas avanzadas ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
[ función ]

4.2 Diferencias finitas, tabla con Python

[ Dif_Finitas ] [ ejercicio ] [ analítico ] [ algoritmo ] [ gráfica ] [ función ]
..


1. Diferencias finitas

Referencia: Rodríguez 6.5 p211, Chapra 18.1.3 p509

Establece relaciones simples entre los puntos dados que describen la función f(x). Las tabla de diferencias finitas es un elemento base para varios métodos de interpolación, por lo que se trata como un tema inicial.

diferencias finitas 01 animado
[ Dif_Finitas ] [ ejercicio [ algoritmo ] [ gráfica ] [ función ]
..


2. Ejercicio

Los datos provienen del ejercicio para el polinomio de interpolación con Vandermonde. Se modifica el primer par ordenado a [0.1, 1.45].

xi = [0.1, 0.2, 0.3, 0.4] 
fi = [1.45, 1.6, 1.7, 2.0]
xi 0.1 0.2 0.3 0.4
fi 1.45 1.6 1.7 2.0

[ Dif_Finitas ] [ ejercicio ] [ analítico ] [ algoritmo ] [ gráfica ] [ función ]
..


3. Desarrollo analítico

La tabla de diferencias finitas se construye tomando los datos y sus índices como parte de las primeras columnas.

i xi fi Δ1fi Δ2fi Δ3fi Δ4fi
0 0.1 1.45 0.15 -0.05 0.25 0.
1 0.2 1.6 0.1 0.2 0. 0.
2 0.3 1.7 0.3 0. 0. 0.
3 0.4 2.0 0. 0. 0. 0.

Cada casilla de diferencia finita Δjfi se obtiene restando los dos valores consecutivos de la columna anterior. Por ejemplo, para la primera columna:

\Delta ^1 f_0 = 1.6-1.45 = 0.15 \Delta ^1 f_1 = 1.7-1.6 = 0.1 \Delta ^1 f_2 = 2.0-1.7 = 0.3

y la siguiente Δ1f3 no es posible calcular.

Siguiendo el mismo procedimiento se calculan los valores de la siguiente columna Δ2fi como las diferencias de la columna anterior.

\Delta ^2 f_0 = 0.1-0.15 = -0.05 \Delta ^2 f_1 = 0.3-0.1 = 0.2

finalmente Δ3fi

\Delta ^3 f_0 = 0.2-(-0.05) = 0.25

Si la función f(x) de donde provienen los datos es un polinomio de grado n, entonces la n-ésima diferencia finita será una constante, y las siguientes diferencias se anularán.

[ Dif_Finitas ] [ ejercicio ] [ analítico ] [ algoritmo ] [ gráfica ] [ función ]
..


4. Algoritmo en Python

Para crear la tabla de diferencias finitas, las primeras columnas requieren concatenar los valores de los índices ixi y fi.

xi = [0.10, 0.2, 0.3, 0.4]
fi = [1.45, 1.6, 1.7, 2.0]

Los índices i se crean en un vector ki, pues la variable i es usada como fila en matrices, por lo que evitamos confundirlas al usar la variable.

A la matriz con las tres columnas descritas, se le añade a la derecha una matriz de nxn para calcular las diferencias.

Se calculan las diferencias para cada columna, realizando la operación entre los valores de cada fila. Considere que no se realizan cálculos desde la diagonal hacia abajo en la tabla, los valores quedan como cero.

Al final se muestra el título y el resultado de la tabla.

# Diferencias finitas: [tabla_etiq,tabla]
# Tarea: verificar tamaño de vectores
import numpy as np

# INGRESO, Datos de prueba
xi = [0.10, 0.2, 0.3, 0.4]
fi = [1.45, 1.6, 1.7, 2.0]

# PROCEDIMIENTO
casicero = 1e-15  # redondear a cero
# Matrices como arreglo, numeros reales
xi = np.array(xi,dtype=float)
fi = np.array(fi,dtype=float)
n = len(xi)

# Tabla de Diferencias Finitas
tabla_etiq = ['i','xi','fi']
ki = np.arange(0,n,1) # filas
tabla = np.concatenate(([ki],[xi],[fi]),axis=0)
tabla = np.transpose(tabla)
dfinita = np.zeros(shape=(n,n),dtype=float)
tabla = np.concatenate((tabla,dfinita), axis=1)
n,m = np.shape(tabla)
# Calular tabla de Diferencias Finitas
diagonal = n-1 # derecha a izquierda
j = 3  # inicia en columna 3
while (j < m): # columna
    tabla_etiq.append('d'+str(j-2)+'f') 
    i = 0 # fila
    while (i < diagonal): # antes de diagonal
        tabla[i,j] = tabla[i+1,j-1]-tabla[i,j-1]
        
        if abs(tabla[i,j])<casicero: # casicero revisa
                tabla[i,j]=0
        i = i + 1
    diagonal = diagonal - 1
    j = j + 1

# SALIDA
print('Tabla Diferencia Finita: ')
print([tabla_etiq])
print(tabla)

el resultado de aplicar el algoritmo es:

Tabla Diferencia Finita: 
[['i', 'xi', 'fi', 'd1f', 'd2f', 'd3f', 'd4f']]
[[ 0.    0.1   1.45  0.15 -0.05  0.25  0.  ]
 [ 1.    0.2   1.6   0.1   0.2   0.    0.  ]
 [ 2.    0.3   1.7   0.3   0.    0.    0.  ]
 [ 3.    0.4   2.    0.    0.    0.    0.  ]]
>>> 

[ Dif_Finitas ] [ ejercicio ] [ analítico ] [ algoritmo ] [ gráfica ] [ función ]
..


5. Gráfica de puntos con líneas horizontales

Para tener una referencia visual sobre las primeras diferencias finitas, en una gráfica se trazan las líneas horizontales que pasan por cada punto. Para las segundas diferencias se debe incluir en la gráfica las primeras diferencias finitas vs xi repitiendo el proceso. Las líneas de distancia se añadieron con un editor de imágenes.

diferencias finitas 01 graf

Las instrucciones adicionales al algoritmo anterior para añadir la gráfica son:

# Gráfica --------------
import matplotlib.pyplot as plt

titulo = 'Diferencia Finita'

for i in range(0,n,1):
    plt.axhline(fi[i],ls='--', color='yellow')
    if i<(n-1):
        plt.annotate("", xytext=(xi[i+1], fi[i]),
                     xy=(xi[i+1], fi[i+1]),
                     arrowprops=dict(arrowstyle="<->"),
                     color='magenta')
        plt.annotate("Δ1f["+str(i)+"]",
                     xy=(xi[i+1], fi[i+1]),
                     xytext=(xi[i+1],(fi[i]+fi[i+1])/2))
                     
plt.plot(xi,fi,'o', label = 'Puntos')
plt.legend()
plt.xlabel('xi')
plt.ylabel('fi')
plt.title(titulo)
plt.show()

[ Dif_Finitas ] [ ejercicio ] [ analítico ] [ algoritmo ] [ gráfica ] [ función ]
..


6. Diferencias finitas como función en Numpy.diff

np.diff(vector,orden)calcula la diferencia de elementos consecutivos a lo largo de un eje específico de una matriz. Incluye un parámetro de orden para la n-esima diferencia finita. Ejemplo:

>>> import numpy as np
>>> xi = [0.10, 0.2, 0.3, 0.4]
>>> fi = [1.45, 1.6, 1.7, 2.0]
>>> d1f = np.diff(fi,1)
>>> d1f
array([0.15, 0.1 , 0.3 ])
>>> d2f = np.diff(fi,2)
>>> d2f
array([-0.05,  0.2 ])
>>> d1x = np.diff(xi,1)
>>> d1x
array([0.1, 0.1, 0.1])
>>> d2x = np.diff(xi,n=2)
>>> d2x
array([-2.77555756e-17,  5.55111512e-17])

Δ2xi es un valor casicero que se debe incluir en el algoritmo para redondear a cero cuando sea por ejemplo 10-15. Asunto que ya fue incluido desde Método de Gauss en la unidad de sistemas de ecuaciones

Referenciahttps://numpy.org/doc/stable/reference/generated/numpy.diff.html

[ Dif_Finitas ] [ ejercicio ] [ analítico ] [ algoritmo ] [ gráfica ] [ función ]

4.1 Polinomio de interpolación con Vandermonde en Python

Interpolación: [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ] [ Función ]
..


1. Ejercicio

Referencia: Rodríguez 6.1 p191, Chapra 18.3 p520

Unir n puntos en el plano usando un polinomios de interpolación se puede realizar con un polinomio de grado (n-1).

interpolación polinómica, matriz de Vandermonde

xi 0 0.2 0.3 0.4
fi 1 1.6 1.7 2.0

Por ejemplo, dados los 4 puntos en la tabla [xi,fi] se requiere generar un polinomio de grado 3 de la forma:

p(x) = a_0 x^3 + a_1 x^2 + a_2 x^1 + a_3

Interpolación: [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ] [ Función ]
..


2. Desarrollo Analítico

Es posible generar un sistema de ecuaciones para p(x) haciendo que pase por cada uno de los puntos o coordenadas.
Por ejemplo si se toma el primer punto con xi=0 y fi=1 se establece la ecuación:

p(0) = 1 a_0 (0)^3 + a_1 (0)^2 + a_2 (0)^1 + a_3 = 1 p(0.2) = 1.6 a_0 (0.2)^3 + a_1 (0.2)^2 + a_2 (0.2)^1 + a_3 = 1.6

Note que ahora las incógnitas son los coeficientes ai. Luego se continúa con los otros puntos seleccionados hasta completar las ecuaciones necesarias para el grado del polinomio seleccionado.

a_0 (0.0)^3 + a_1 (0.0)^2 + a_2 (0.0)^1 + a_3 = 1.0 a_0 (0.2)^3 + a_1 (0.2)^2 + a_2 (0.2)^1 + a_3 = 1.6 a_0 (0.3)^3 + a_1 (0.3)^2 + a_2 (0.3)^1 + a_3 = 1.7 a_0 (0.4)^3 + a_1 (0.4)^2 + a_2 (0.4)^1 + a_3 = 2.0

El sistema obtenido se resuelve con alguno de los métodos conocidos para la Solución a sistemas de ecuaciones, que requieren escribir las ecuaciones en la forma de matriz A y vector B, desarrollar la matriz aumentada, pivotear por filas, etc.

\begin{pmatrix} 0.0^3 & 0.0^2 & 0.0^1 & 1 \\ 0.2^3 & 0.2^2 & 0.2^1 & 1 \\ 0.3^3 & 0.3^2 & 0.3^1 & 1 \\ 0.4^3 & 0.4^2 & 0.4^1 & 1 \end{pmatrix} . \begin{pmatrix} a_0 \\ a_1 \\ a_2 \\ a_3 \end{pmatrix} = \begin{pmatrix} 1.0 \\ 1.6 \\ 1.7 \\ 2.0 \end{pmatrix}

La matriz A también se conoce como Matriz Vandermonde D, que se construye observando que los coeficientes se elevan al exponente referenciado al índice columna pero de derecha a izquierda. La última columna tiene valores 1 por tener como coeficiente el valor de xi0.

Para enfocarnos en la interpolación, en la solución se propone usar un algoritmo o función en Python, obteniendo el siguiente resultado:

los coeficientes del polinomio: 
[ 41.66666667 -27.5          6.83333333   1.        ]

a partir del cual que se construye el polinomio con los valores obtenidos.

Polinomio de interpolación p(x): 
41.66666666667*x**3 - 27.5*x**2 + 6.83333333333*x + 1.0

que re-escrito es,

p(x) = 41.6666 x^3 - 27.5 x^2 + 6.8333 x + 1

Se realiza  la gráfica de p(x) dentro del intervalo de los datos del ejercicio y se obtiene la línea contínua que pasa por cada uno de los puntos.

interpola polinomio 02

Interpolación: [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ] [ Función ]
..


3. Algoritmo en Python

Para desarrollar el ejercicio, se realiza un bloque para construir la matriz A y vector B, usando los vectores que representan los puntos de muestra de la función o experimento.

xi = [0,0.2,0.3,0.4]
fi = [1,1.6,1.7,2.0]

Observe que en éste caso los datos se dan en forma de lista y se deben convertir hacia arreglos.

La matriz A o Matriz Vandermonde D, se construye observando que los coeficientes del vector xi se elevan al exponente referenciado al índice columna pero de derecha a izquierda.

Construir el polinomio consiste en resolver el sistema de ecuaciones indicado en la sección anterior. Para simplificar la solución del sistema, se usa Numpy, que entrega el vector solución que representan los coeficientes del polinomio de interpolación.

Para construir la expresión del polinomio, se usa la forma simbólica con Sympy, de forma semejante a la usada para construir el polinomio de Taylor.
Para mostrar el polinomio de una manera más fácil de interpretar se usa la instrucción sym.pprint(), usada al final del algoritmo.

# El polinomio de interpolación y Vandermonde
import numpy as np
import sympy as sym

# INGRESO
xi = [0,0.2,0.3,0.4]
fi = [1,1.6,1.7,2.0]

# PROCEDIMIENTO
# Matrices como arreglo, numeros reales
xi = np.array(xi,dtype=float)
fi = np.array(fi,dtype=float)
n = len(xi)
# Matriz Vandermonde D
D = np.zeros(shape=(n,n),dtype=float)
for i in range(0,n,1):
    for j in range(0,n,1):
        potencia = (n-1)-j # Derecha a izquierda
        D[i,j] = xi[i]**potencia

# Resuelve sistema de ecuaciones A.X=B
coeficiente = np.linalg.solve(D,fi)

# Polinomio en forma simbólica
x = sym.Symbol('x')
polinomio = 0*x   # sym.S.Zero
for i in range(0,n,1):
    potencia = (n-1)-i   # Derecha a izquierda
    termino = coeficiente[i]*(x**potencia)
    polinomio = polinomio + termino

# polinomio para evaluación numérica
px = sym.lambdify(x,polinomio)

# SALIDA
print('Matriz Vandermonde: ')
print(D)
print('los coeficientes del polinomio: ')
print(coeficiente)
print('Polinomio de interpolación: ')
print(polinomio)
print('\n formato pprint')
sym.pprint(polinomio)

con lo que se obtiene el siguiente resultado:

Matriz Vandermonde: 
[[0.    0.    0.    1.   ]
 [0.008 0.04  0.2   1.   ]
 [0.027 0.09  0.3   1.   ]
 [0.064 0.16  0.4   1.   ]]
los coeficientes del polinomio: 
[ 41.66666667 -27.5 6.83333333 1. ]
Polinomio de interpolación: 
41.66666666667*x**3 - 27.5*x**2 + 6.83333333333*x + 1.0

formato pprint
                  3         2                           
41.66666666667*x  - 27.5*x  + 6.83333333333*x + 1.0

Interpolación: [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ] [ Función ]
..


4. Gráfica del polinomio

Para facilitar la evaluación numérica del polinomio, se convierte el polinomio a la forma Lambda px. La gráfica se realiza con un número de muestras suficientes para suavizar la curva dentro del intervalo [a,b], por ejemplo 21, con lo que se comprueba que la curva pase por todos los puntos de la tabla xi,fi dados en el ejercicio.

Instrucciones adicionales al algoritmo para la gráfica:

# GRAFICA
import matplotlib.pyplot as plt

muestras = 21  # muestras = tramos+1

a = np.min(xi) # intervalo [a,b]
b = np.max(xi)
xk = np.linspace(a,b,muestras)
yk = px(xk)

# Usando evaluación simbólica
##yk = np.zeros(muestras,dtype=float)
##for k in range(0,muestras,1):
##    yin[k] = polinomio.subs(x,xk[k])

plt.plot(xi,fi,'o', label='[xi,fi]')
plt.plot(xk,yk, label='p(x)')
plt.xlabel('xi')
plt.ylabel('fi')
plt.legend()
plt.title(polinomio)
plt.show()

Interpolación: [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ] [ Función ]
..


5. Vandermonde como función en Numpy.vander

La matriz de Vandermonde se puede crear con Numpy usando la instrucción np.vander(xi,len(xi)).

>>> import numpy as np
>>> xi = [0. , 0.2, 0.3, 0.4]
>>> fi = [1. , 1.6, 1.7, 2. ]
>>> D = np.vander(xi,len(xi))
>>> D
array([[0.   , 0.   , 0.   , 1.   ],
       [0.008, 0.04 , 0.2  , 1.   ],
       [0.027, 0.09 , 0.3  , 1.   ],
       [0.064, 0.16 , 0.4  , 1.   ]])
>>> coeficiente = np.linalg.solve(D,fi)
>>> coeficiente
array([ 41.66667, -27.5    ,   6.83333,   1.     ])

Interpolación: [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ] [ Función ]

3.9.2 Método de Gauss-Jordan para matriz Inversa con Python

matriz inversa: [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ Función ]
..


1. Ejercicio

Referencia: Chapra 10.2 p292, Burden 6.3 p292, Rodríguez 4.2.5 Ejemplo 1 p118
Obtener la inversa de una matriz usando el método de Gauss-Jordan, a partir de la matriz:

\begin{pmatrix} 4 & 2 & 5 \\ 2 & 5 & 8 \\ 5 & 4 & 3 \end{pmatrix}
A = [[4,2,5],
     [2,5,8],
     [5,4,3]]

matriz inversa: [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ Función ]
..


2. Desarrollo analítico

Para el procedimiento, se crea la matriz aumentada de A con la identidad I.

AI = A|I

\begin{pmatrix} 4 & 2 & 5 & 1 & 0 & 0\\ 2 & 5 & 8 & 0 & 1 & 0 \\ 5 & 4 & 3 & 0 & 0 & 1 \end{pmatrix}
[[  4.    2.    5.   1.    0.    0. ]
 [  2.    5.    8.   0.    1.    0. ]
 [  5.    4.    3.   0.    0.    1. ]]

Con la matriz aumentada AI  se repiten los procedimientos aplicados en el método de Gauss-Jordan:

  • pivoteo parcial por filas
  • eliminación hacia adelante
  • eliminación hacia atrás

De la matriz aumentada resultante, se obtiene la inversa A-1 en la mitad derecha de AI, lugar que originalmente correspondía a la identidad.

el resultado buscado es:

la matriz inversa es:
[[ 0.2        -0.16470588  0.10588235]
 [-0.4         0.15294118  0.25882353]
 [ 0.2         0.07058824 -0.18823529]]
\begin{pmatrix} 0.2 & -0.16470588 & 0.10588235 \\ -0.4 & 0.15294118 & 0.25882353 \\ 0.2 & 0.07058824 & -0.18823529 \end{pmatrix}

Verifica resultado

El resultado se verifica realizando la operación producto punto entre A y la inversa, que debe resultar la matriz identidad.

A.A-1 = I

El resultado de la operación es una matriz identidad. Observe que los valores del orden de 10-15 o menores se consideran como casi cero o cero.

 A.inversa = identidad
[[ 1.00000000e+00 -1.38777878e-17 -1.38777878e-16]
 [ 2.22044605e-16  1.00000000e+00 -2.22044605e-16]
 [ 5.55111512e-17 -9.71445147e-17  1.00000000e+00]]

matriz inversa: [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ Función ]
..


3. Algoritmo en Python

El algoritmo que describe el proceso en python:

# Matriz Inversa con Gauss-Jordan
# AI es la matriz aumentada A con Identidad
import numpy as np

# INGRESO
A = [[4,2,5],
     [2,5,8],
     [5,4,3]]

# PROCEDIMIENTO
# B = matriz_identidad
n,m = np.shape(A)
identidad = np.identity(n)

np.set_printoptions(precision=4) # 4 decimales en print
casicero = 1e-15  # redondear a cero

# Matrices como arreglo, numeros reales
A = np.array(A,dtype=float)
B = np.array(identidad,dtype=float)

def pivoteafila(A,B,vertabla=False):
    '''
    Pivoteo parcial por filas, entrega matriz aumentada AB
    Si hay ceros en diagonal es matriz singular,
    Tarea: Revisar si diagonal tiene ceros
    '''
    A = np.array(A,dtype=float)
    B = np.array(B,dtype=float)
    # Matriz aumentada
    nB = len(np.shape(B))
    if nB == 1:
        B = np.transpose([B])
    AB  = np.concatenate((A,B),axis=1)
    
    if vertabla==True:
        print('Matriz aumentada')
        print(AB)
        print('Pivoteo parcial:')
    
    # Pivoteo por filas AB
    tamano = np.shape(AB)
    n = tamano[0]
    m = tamano[1]
    
    # Para cada fila en AB
    pivoteado = 0
    for i in range(0,n-1,1):
        # columna desde diagonal i en adelante
        columna = np.abs(AB[i:,i])
        dondemax = np.argmax(columna)
        
        # dondemax no es en diagonal
        if (dondemax != 0):
            # intercambia filas
            temporal = np.copy(AB[i,:])
            AB[i,:] = AB[dondemax+i,:]
            AB[dondemax+i,:] = temporal

            pivoteado = pivoteado + 1
            if vertabla==True:
                print(' ',pivoteado,
                      'intercambiar filas: ',i,
                      'con', dondemax+i)
    if vertabla==True:
        if pivoteado==0:
            print('  Pivoteo por filas NO requerido')
        else:
            print(AB)
    return(AB)

def gauss_eliminaAdelante(AB,vertabla=False,
                          lu=False,casicero = 1e-15):
    ''' Gauss elimina hacia adelante
    tarea: verificar términos cero
    '''
    tamano = np.shape(AB)
    n = tamano[0]
    m = tamano[1]
    if vertabla==True:
        print('Elimina hacia adelante:')
    for i in range(0,n,1):
        pivote = AB[i,i]
        adelante = i+1
        if vertabla==True:
            print(' fila i:',i,' pivote:', pivote)
        for k in range(adelante,n,1):
            if (np.abs(pivote)>=casicero):
                factor = AB[k,i]/pivote
                AB[k,:] = AB[k,:] - factor*AB[i,:]
                for j in range(0,m,1): # casicero revisa
                    if abs(AB[k,j])<casicero:
                        AB[k,j]=0
                if vertabla==True:
                    print('  fila k:',k,
                          ' factor:',factor)
            else:
                print('  pivote:', pivote,'en fila:',i,
                      'genera division para cero')
        if vertabla==True:
            print(AB)
    respuesta = np.copy(AB)
    if lu==True: # matriz triangular A=L.U
        U = AB[:,:n-1]
        respuesta = [AB,L,U]
    return(respuesta)

def gauss_eliminaAtras(AB, vertabla=False,
                       inversa=False,
                       casicero = 1e-14):
    ''' Gauss-Jordan elimina hacia atras
    Requiere la matriz triangular inferior
    Tarea: Verificar que sea triangular inferior
    '''
    tamano = np.shape(AB)
    n = tamano[0]
    m = tamano[1]
    
    ultfila = n-1
    ultcolumna = m-1
    if vertabla==True:
        print('Elimina hacia Atras:')
        
    for i in range(ultfila,0-1,-1):
        pivote = AB[i,i]
        atras = i-1  # arriba de la fila i
        if vertabla==True:
            print(' fila i:',i,' pivote:', pivote)
            
        for k in range(atras,0-1,-1):
            if np.abs(AB[k,i])>=casicero:
                factor = AB[k,i]/pivote
                AB[k,:] = AB[k,:] - factor*AB[i,:]
                
                # redondeo a cero
                for j in range(0,m,1): 
                    if np.abs(AB[k,j])<=casicero:
                        AB[k,j]=0
                if vertabla==True:
                    print('  fila k:',k,
                          ' factor:',factor)
            else:
                print('  pivote:', pivote,'en fila:',i,
                      'genera division para cero')
        AB[i,:] = AB[i,:]/AB[i,i] # diagonal a unos
        
        if vertabla==True:
            print(AB)
    
    respuesta = np.copy(AB[:,ultcolumna])
    if inversa==True: # matriz inversa
        respuesta = np.copy(AB[:,n:])
    return(respuesta)

AB = pivoteafila(A,identidad,vertabla=True)
AB = gauss_eliminaAdelante(AB,vertabla=True)
A_inversa = gauss_eliminaAtras(AB,inversa=True,
                               vertabla=True)
A_verifica = np.dot(A,A_inversa)
# redondeo a cero
for i in range(0,n,1):
    for j in range(0,m,1): 
        if np.abs(A_verifica[i,j])<=casicero:
            A_verifica[i,j]=0

# SALIDA
print('A_inversa: ')
print(A_inversa)
print('verifica A.A_inversa = Identidad:')
print(A_verifica)

el resultado buscado es:

Matriz aumentada
[[4. 2. 5. 1. 0. 0.]
 [2. 5. 8. 0. 1. 0.]
 [5. 4. 3. 0. 0. 1.]]
Pivoteo parcial:
  1 intercambiar filas:  0 con 2
[[5. 4. 3. 0. 0. 1.]
 [2. 5. 8. 0. 1. 0.]
 [4. 2. 5. 1. 0. 0.]]
Elimina hacia adelante:
 fila i: 0  pivote: 5.0
  fila k: 1  factor: 0.4
  fila k: 2  factor: 0.8
[[ 5.   4.   3.   0.   0.   1. ]
 [ 0.   3.4  6.8  0.   1.  -0.4]
 [ 0.  -1.2  2.6  1.   0.  -0.8]]
 fila i: 1  pivote: 3.4
  fila k: 2  factor: -0.3529411764705883
[[ 5.      4.      3.      0.      0.      1.    ]
 [ 0.      3.4     6.8     0.      1.     -0.4   ]
 [ 0.      0.      5.      1.      0.3529 -0.9412]]
 fila i: 2  pivote: 5.0
[[ 5.      4.      3.      0.      0.      1.    ]
 [ 0.      3.4     6.8     0.      1.     -0.4   ]
 [ 0.      0.      5.      1.      0.3529 -0.9412]]
Elimina hacia Atras:
 fila i: 2  pivote: 5.0
  fila k: 1  factor: 1.3599999999999999
  fila k: 0  factor: 0.6
[[ 5.      4.      0.     -0.6    -0.2118  1.5647]
 [ 0.      3.4     0.     -1.36    0.52    0.88  ]
 [ 0.      0.      1.      0.2     0.0706 -0.1882]]
 fila i: 1  pivote: 3.4
  fila k: 0  factor: 1.1764705882352942
[[ 5.      0.      0.      1.     -0.8235  0.5294]
 [ 0.      1.      0.     -0.4     0.1529  0.2588]
 [ 0.      0.      1.      0.2     0.0706 -0.1882]]
 fila i: 0  pivote: 5.0
[[ 1.      0.      0.      0.2    -0.1647  0.1059]
 [ 0.      1.      0.     -0.4     0.1529  0.2588]
 [ 0.      0.      1.      0.2     0.0706 -0.1882]]
A_inversa: 
[[ 0.2    -0.1647  0.1059]
 [-0.4     0.1529  0.2588]
 [ 0.2     0.0706 -0.1882]]
verifica A.A_inversa = Identidad:
[[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]]

Observe que el algoritmo se pude reducir si usan los procesos de Gauss-Jordan como funciones.

Tarea: Realizar el algoritmo usando una función creada para Gauss-Jordan

matriz inversa: [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ Función ]
..


4. Función en Numpy

La función en la librería Numpy es np.linalg.inv():

>>> np.linalg.inv(A)
array([[ 0.2       , -0.16470588,  0.10588235],
       [-0.4       ,  0.15294118,  0.25882353],
       [ 0.2       ,  0.07058824, -0.18823529]])
>>> 

matriz inversa: [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ Función ]

3.9.1 Método de Gauss - determinante de matriz con Python

[ Determinante ] [ Ejercicio ] [ Algoritmo ] [ Función ]
..


1. Determinante de matriz

Referencia: Rodríguez 4.3.9 p129, Burden 6.4 p296, Chapra 9.1.2 p250.

El determinante de una matriz cuadrada triangular superior también puede calcularse como el producto de los coeficientes de la diagonal principal, considerando el número de cambios de fila del pivoteo k.

det(A) = (-1)^k \prod_{i=1}^n a_{i,i}

Si observamos que en las secciones anteriores se tiene desarrollado los algoritmos  para obtener la matriz triangular superior en el método de Gauss, se usan como punto de partida para obtener los resultados del cálculo del determinante.

[ Determinante ] [ Ejercicio ] [ Algoritmo ] [ Función ]
..


2. Ejercicio

Referencia: 1Eva_IIT2010_T2 Sistema ecuaciones, X0 = unos, Chapra Ejemplo 9.12 p277

Calcular el determinante de la matriz A del sistema A.X=B dado por

A= \begin{pmatrix} 0.4 & 1.1 & 3.1 \\ 4 & 0.15 & 0.25 \\2 & 5.6 & 3.1 \end{pmatrix} B= [7.5, 4.45, 0.1]

El  algoritmo para ejercicio se convierte en una extensión de los algoritmos anteriores.

A = [[0.4, 1.1 ,  3.1],
     [4.0, 0.15, 0.25],
     [2.0, 5.6 , 3.1]]
B = [7.5, 4.45, 0.1]

[ Determinante ] [ Ejercicio ] [ Algoritmo ] [ Función ]
..


3. Algoritmo en Python

El algoritmo parte de lo realizado en método de Gauss, indicando que la matriz a procesar es solamente A. Se mantienen los procedimientos de "pivoteo parcial por filas" y " eliminación hacia adelante"

Para contar el número de cambios de filas, se añade un contador de  pivoteado dentro del condicional para intercambio de filas.

Para el resultado del operador multiplicación, se usan todas las casillas de la diagonal al acumular las multiplicaciones.

# Determinante de una matriz A
# convirtiendo a diagonal superior 

import numpy as np

# INGRESO
A = [[0.4, 1.1 ,  3.1],
     [4.0, 0.15, 0.25],
     [2.0, 5.6 , 3.1]]
B = [7.5, 4.45, 0.1]

# PROCEDIMIENTO
# Matrices como arreglo, numeros reales
A = np.array(A,dtype=float)
B = np.array(B,dtype=float)

# Matriz aumentada AB
B_columna = np.transpose([B])
AB  = np.concatenate((A,B_columna),axis=1)
AB0 = np.copy(AB) # copia de AB

# Pivoteo parcial por filas
tamano = np.shape(AB)
n = tamano[0]
m = tamano[1]

# Para cada fila en AB
pivoteado = 0 # contador para cambio fila

for i in range(0,n-1,1):
    # columna desde diagonal i en adelante
    columna = abs(AB[i:,i])
    dondemax = np.argmax(columna)
    
    if (dondemax !=0): # NO en diagonal
        # intercambia filas
        temporal = np.copy(AB[i,:])
        AB[i,:]  = AB[dondemax+i,:]
        AB[dondemax+i,:] = temporal
        
        pivoteado = pivoteado + 1 # cuenta cambio fila
        
# Actualiza A y B pivoteado
AB1 = np.copy(AB)

# eliminación hacia adelante
for i in range(0,n-1,1):
    pivote   = AB[i,i]
    adelante = i + 1
    for k in range(adelante,n,1):
        factor  = AB[k,i]/pivote
        AB[k,:] = AB[k,:] - AB[i,:]*factor

# calcula determinante
multiplica = 1
for i in range(0,n,1):
    multiplica = multiplica*AB[i,i]
determinante = ((-1)**pivoteado)*multiplica

# SALIDA
print('Pivoteo parcial por filas')
print(AB1)
print('Cambios de fila, pivoteado: ',pivoteado)
print('eliminación hacia adelante')
print(AB)
print('determinante: ')
print(determinante)

Se aplica la operación de la fórmula planteada para el método, y se presenta el resultado:

Pivoteo parcial por filas
[[4.   0.15 0.25 4.45]
 [2.   5.6  3.1  0.1 ]
 [0.4  1.1  3.1  7.5 ]]
Cambios de fila, pivoteado:  2
eliminación hacia adelante
[[ 4.          0.15        0.25        4.45      ]
 [ 0.          5.525       2.975      -2.125     ]
 [ 0.          0.          2.49076923  7.47230769]]
determinante: 
55.04599999999999

[ Determinante ] [ Ejercicio ] [ Algoritmo ] [ Función ]
..


4. Algoritmo como función en Numpy

El resultado se puede verificar usando la función de Numpy:

>>> np.linalg.det(A)
55.04599999999999

[ Determinante ] [ Ejercicio ] [ Algoritmo ] [ Función ]

3.8 Matrices triangulares A=L.U con Python

[ Matriz A=L.U ] [ Ejercicio ] [ Algoritmo LU ] [ Función LU ] ||
[ Solución LY=B , UX=Y ]

..


1. Matrices triangulares A=L.U

Referencia: Chapra 10.1 p284. Burden 6.5 p298

Una matriz A puede separarse en dos matrices triangulares que entre ellas tienen la propiedad:  A = L.U:

  • L de tipo triangular inferior
  • U de tipo triangular superior

Matriz LU 01

La matriz U se obtiene después de aplicar el proceso de eliminación hacia adelante del método de Gauss.

La matriz L contiene los factores usados en el proceso de eliminación hacia adelante del método de Gauss, escritos sobre una matriz identidad en las posiciones donde se calcularon.

[ Matriz A=L.U ] [ Ejercicio ] [ Algoritmo LU ] [ Función LU ] ||
[ Solución LY=B , UX=Y ]
..


2. Ejercicio

Referencia: Chapra Ejemplo 10.1 p285

Presente las matrices LU de la matriz A siguiente:

A= \begin{pmatrix} 3 & -0.1 & -0.2 \\ 0.1 & 7 & -0.3 \\0.3 & -0.2 & 10 \end{pmatrix}
A = [[ 3. , -0.1, -0.2],
     [ 0.1,  7. , -0.3],
     [ 0.3, -0.2, 10. ]]

B = [7.85,-19.3,71.4]

El resultado del "pivoteo por filas" y "eliminación hacia adelante" se tiene:

Pivoteo parcial por filas
[[  3.    -0.1   -0.2    7.85]
 [  0.1    7.    -0.3  -19.3 ]
 [  0.3   -0.2   10.    71.4 ]]

de donde la última columna es el nuevo vector B

eliminación hacia adelante
Matriz U: 
[[ 3.         -0.1        -0.2       ]
 [ 0.          7.00333333 -0.29333333]
 [ 0.          0.         10.01204188]]

y guardando los factores del procedimiento de "eliminación hacia adelante " en una matriz L que empieza con la matriz identidad se obtiene:

matriz L: 
[[ 1.          0.          0.        ]
 [ 0.03333333  1.          0.        ]
 [ 0.1        -0.02712994  1.        ]]

[ Matriz A=L.U ] [ Ejercicio ] [ Algoritmo LU ] [ Función LU ] ||
[ Solución LY=B , UX=Y ]
..


3. Algoritmo LU en Python

Realizado a partir del algoritmo de la sección método de Gauss y modificando las partes necesarias para el algoritmo.

Para éste algoritmo, se procede desde el bloque de pivoteo por filas", continuando con el algoritmo de "eliminación hacia adelante" del método de Gauss.  Procedimientos que dan como resultado la matriz U.

La matriz L requiere iniciar con una matriz identidad,  y el procedimiento requiere que al ejecutar "eliminación hacia adelante" se almacene cada factor con el que se multiplica la fila para hacer cero. El factor se lo almacena en la matriz L, en la posición de dónde se determinó el factor.

El resultado obtenido con el algoritmo es:

Matriz aumentada
[[  3.    -0.1   -0.2    7.85]
 [  0.1    7.    -0.3  -19.3 ]
 [  0.3   -0.2   10.    71.4 ]]
Pivoteo parcial:
  Pivoteo por filas NO requerido
matriz L: 
[[ 1.      0.      0.    ]
 [ 0.0333  1.      0.    ]
 [ 0.1    -0.0271  1.    ]]
Matriz U: 
[[ 3.     -0.1    -0.2   ]
 [ 0.      7.0033 -0.2933]
 [ 0.      0.     10.012 ]]
>>> 

Donde se puede verificar que L.U = A usando la instrucción np.dot(L.U):

>>> np.dot(L,U)
array([[ 3. , -0.1, -0.2],
       [ 0.1,  7. , -0.3],
       [ 0.3, -0.2, 10. ]])
>>> 

Instrucciones en Python

# Matrices L y U
# Modificando el método de Gauss
import numpy as np

# PROGRAMA ------------
# INGRESO
A = [[ 3. , -0.1, -0.2],
     [ 0.1,  7. , -0.3],
     [ 0.3, -0.2, 10. ]]

B = [7.85,-19.3,71.4]

# PROCEDIMIENTO
np.set_printoptions(precision=4) # 4 decimales en print
casicero = 1e-15  # redondear a cero

def pivoteafila(A,B,vertabla=False):
    '''
    Pivotea parcial por filas
    Si hay ceros en diagonal es matriz singular,
    Tarea: Revisar si diagonal tiene ceros
    '''
    A = np.array(A,dtype=float)
    B = np.array(B,dtype=float)
    # Matriz aumentada
    nB = len(np.shape(B))
    if nB == 1:
        B = np.transpose([B])
    AB  = np.concatenate((A,B),axis=1)
    
    if vertabla==True:
        print('Matriz aumentada')
        print(AB)
        print('Pivoteo parcial:')
    
    # Pivoteo por filas AB
    tamano = np.shape(AB)
    n = tamano[0]
    m = tamano[1]
    
    # Para cada fila en AB
    pivoteado = 0
    for i in range(0,n-1,1):
        # columna desde diagonal i en adelante
        columna = np.abs(AB[i:,i])
        dondemax = np.argmax(columna)
        
        # dondemax no es en diagonal
        if (dondemax != 0):
            # intercambia filas
            temporal = np.copy(AB[i,:])
            AB[i,:] = AB[dondemax+i,:]
            AB[dondemax+i,:] = temporal

            pivoteado = pivoteado + 1
            if vertabla==True:
                print(' ',pivoteado,
                      'intercambiar filas: ',i,
                      'con', dondemax+i)
    if vertabla==True:
        if pivoteado==0:
            print('  Pivoteo por filas NO requerido')
        else:
            print(AB)
    return(AB)

AB = pivoteafila(A,B,vertabla=True)

tamano = np.shape(AB)
n = tamano[0]
m = tamano[1]

AB0 = np.copy(AB)

L = np.identity(n,dtype=float) # Inicializa L

# eliminacion hacia adelante
for i in range(0,n-1,1):
    pivote = AB[i,i]
    adelante = i+1
    for k in range(adelante,n,1):
        factor = AB[k,i]/pivote
        AB[k,:] = AB[k,:] - AB[i,:]*factor
        
        L[k,i] = factor # llena L

U = np.copy(AB[:,:n])

# SALIDA
print('matriz L: ')
print(L)
print('Matriz U: ')
print(U)

Si se requiere una respuesta unificada en una variable, se puede convertir en una arreglo de matrices [L,U].
Las matrices L y U se pueden leer como L=LU[0] y U=LU[1]

LU = np.array([L,U])

# SALIDA
print('triangular inferior L')
print(LU[0])
print('triangular superior U')
print(LU[1])

[ Matriz A=L.U ] [ Ejercicio ] [ Algoritmo LU ] [ Función LU ] ||
[ Solución LY=B , UX=Y ]

..


4. Algoritmo como Función de Python

El resultado a obtener es:

Matriz aumentada
[[  3.    -0.1   -0.2    7.85]
 [  0.1    7.    -0.3  -19.3 ]
 [  0.3   -0.2   10.    71.4 ]]
Pivoteo parcial:
  Pivoteo por filas NO requerido
Elimina hacia adelante:
 fila 0 pivote:  3.0
   factor:  0.03333333333333333  para fila:  1
   factor:  0.09999999999999999  para fila:  2
 fila 1 pivote:  7.003333333333333
   factor:  -0.027129938124702525  para fila:  2
 fila 2 pivote:  10.012041884816753
[[  3.      -0.1     -0.2      7.85  ]
 [  0.       7.0033  -0.2933 -19.5617]
 [  0.       0.      10.012   70.0843]]
matriz L: 
[[ 1.      0.      0.    ]
 [ 0.0333  1.      0.    ]
 [ 0.1    -0.0271  1.    ]]
Matriz U: 
[[  3.      -0.1     -0.2      7.85  ]
 [  0.       7.0033  -0.2933 -19.5617]
 [  0.       0.      10.012   70.0843]]
>>>

Instrucciones en Python

# Matrices L y U
# Modificando el método de Gauss
import numpy as np

def pivoteafila(A,B,vertabla=False):
    '''
    Pivoteo parcial por filas, entrega matriz aumentada AB
    Si hay ceros en diagonal es matriz singular,
    Tarea: Revisar si diagonal tiene ceros
    '''
    A = np.array(A,dtype=float)
    B = np.array(B,dtype=float)
    # Matriz aumentada
    nB = len(np.shape(B))
    if nB == 1:
        B = np.transpose([B])
    AB  = np.concatenate((A,B),axis=1)
    
    if vertabla==True:
        print('Matriz aumentada')
        print(AB)
        print('Pivoteo parcial:')
    
    # Pivoteo por filas AB
    tamano = np.shape(AB)
    n = tamano[0]
    m = tamano[1]
    
    # Para cada fila en AB
    pivoteado = 0
    for i in range(0,n-1,1):
        # columna desde diagonal i en adelante
        columna = np.abs(AB[i:,i])
        dondemax = np.argmax(columna)
        
        # dondemax no es en diagonal
        if (dondemax != 0):
            # intercambia filas
            temporal = np.copy(AB[i,:])
            AB[i,:] = AB[dondemax+i,:]
            AB[dondemax+i,:] = temporal

            pivoteado = pivoteado + 1
            if vertabla==True:
                print(' ',pivoteado,
                      'intercambiar filas: ',i,
                      'con', dondemax+i)
    if vertabla==True:
        if pivoteado==0:
            print('  Pivoteo por filas NO requerido')
        else:
            print(AB)
    return(AB)

def gauss_eliminaAdelante(AB, vertabla=False,lu=False,casicero = 1e-15):
    ''' Gauss elimina hacia adelante, a partir de,
    matriz aumentada y pivoteada.
    Para respuesta en forma A=L.U usar lu=True entrega[AB,L,U]
    '''
    tamano = np.shape(AB)
    n = tamano[0]
    m = tamano[1]
    L = np.identity(n,dtype=float) # Inicializa L
    if vertabla==True:
        print('Elimina hacia adelante:')
    for i in range(0,n,1):
        pivote = AB[i,i]
        adelante = i+1
        if vertabla==True:
            print(' fila',i,'pivote: ', pivote)
        for k in range(adelante,n,1):
            if (np.abs(pivote)>=casicero):
                factor = AB[k,i]/pivote
                AB[k,:] = AB[k,:] - factor*AB[i,:]
                for j in range(0,m,1): # casicero revisa
                    if abs(AB[k,j])<casicero:
                        AB[k,j]=0

                L[k,i] = factor # llena L
                
                if vertabla==True:
                    print('   factor: ',factor,' para fila: ',k)
            else:
                print('  pivote:', pivote,'en fila:',i,
                      'genera division para cero')
    respuesta = AB
    if vertabla==True:
        print(AB)
    if lu==True:
        U = AB[:,:n-1]
        respuesta = [AB,L,U]
    return(respuesta)

# PROGRAMA ------------------------
# INGRESO
A = [[ 3. , -0.1, -0.2],
     [ 0.1,  7. , -0.3],
     [ 0.3, -0.2, 10. ]]

B = [7.85,-19.3,71.4]

# PROCEDIMIENTO
np.set_printoptions(precision=4) # 4 decimales en print
casicero = 1e-15  # redondear a cero

AB = pivoteafila(A,B,vertabla=True)

AB = gauss_eliminaAdelante(AB,vertabla=True, lu=True)
L = AB[1]
U = AB[0]

# SALIDA
print('matriz L: ')
print(L)
print('Matriz U: ')
print(U)

Función en Scipy

Luego del resultado o definida la matriz a, la instrucción en la librería Scipy es:

>>> import scipy as sp
>>> [L,U] =sp.linalg.lu(A,permute_l=True)
>>> L
array([[ 1.        ,  0.        ,  0.        ],
       [ 0.03333333,  1.        ,  0.        ],
       [ 0.1       , -0.02712994,  1.        ]])
>>> U
array([[ 3.        , -0.1       , -0.2       ],
       [ 0.        ,  7.00333333, -0.29333333],
       [ 0.        ,  0.        , 10.01204188]])
>>> 

[ Matriz A=L.U ] [ Ejercicio ] [ Algoritmo LU ] [ Función LU ] ||
[ Solución LY=B , UX=Y ]
..


5. Solución con LY=B , UX=Y

Referencia: Chapra 10.2 p287, pdf312

Se plantea resolver el sistema de ecuaciones usando matrices triangulares

A = \begin{pmatrix} 3 & -0.1 & -0.2 \\ 0.1 & 7 & -0.3 \\0.3 & -0.2 & 10 \end{pmatrix} B = [7.85,-19.3,71.4]

Para encontrar la solución al sistema de ecuaciones, se plantea resolver:
- sustitución hacia adelante de LY=B
- sustitución hacia atrás para UX=Y

Al algoritmo de la sección anterior se añade los procedimientos correspondientes con los que se obtiene la solución:

Matriz aumentada
[[  3.    -0.1   -0.2    7.85]
 [  0.1    7.    -0.3  -19.3 ]
 [  0.3   -0.2   10.    71.4 ]]
Pivoteo parcial:
  Pivoteo por filas NO requerido
matriz L: 
[[ 1.          0.          0.        ]
 [ 0.03333333  1.          0.        ]
 [ 0.1        -0.02712994  1.        ]]
Matriz U: 
[[ 3.         -0.1        -0.2       ]
 [ 0.          7.00333333 -0.29333333]
 [ 0.          0.         10.01204188]]
B1 :
[[  7.85]
 [-19.3 ]
 [ 71.4 ]]
Y Sustitución hacia adelante
[[  7.85      ]
 [-19.56166667]
 [ 70.08429319]]
X Sustitución hacia atras
[ 3.  -2.5  7. ]
>>>

Instrucciones en Python

# Solución usando Matrices L y U
# continuación de ejercicio anterior
import numpy as np

# PROGRAMA ------------
# INGRESO
A = [[ 3. , -0.1, -0.2],
     [ 0.1,  7. , -0.3],
     [ 0.3, -0.2, 10. ]]

B = [7.85,-19.3,71.4]

# PROCEDIMIENTO
np.set_printoptions(precision=4) # 4 decimales en print
casicero = 1e-15  # redondear a cero

def pivoteafila(A,B,vertabla=False):
    '''
    Pivoteo parcial por filas, entrega matriz aumentada AB
    Si hay ceros en diagonal es matriz singular,
    Tarea: Revisar si diagonal tiene ceros
    '''
    A = np.array(A,dtype=float)
    B = np.array(B,dtype=float)
    # Matriz aumentada
    nB = len(np.shape(B))
    if nB == 1:
        B = np.transpose([B])
    AB  = np.concatenate((A,B),axis=1)
    
    if vertabla==True:
        print('Matriz aumentada')
        print(AB)
        print('Pivoteo parcial:')
    
    # Pivoteo por filas AB
    tamano = np.shape(AB)
    n = tamano[0]
    m = tamano[1]
    
    # Para cada fila en AB
    pivoteado = 0
    for i in range(0,n-1,1):
        # columna desde diagonal i en adelante
        columna = np.abs(AB[i:,i])
        dondemax = np.argmax(columna)
        
        # dondemax no es en diagonal
        if (dondemax != 0):
            # intercambia filas
            temporal = np.copy(AB[i,:])
            AB[i,:] = AB[dondemax+i,:]
            AB[dondemax+i,:] = temporal

            pivoteado = pivoteado + 1
            if vertabla==True:
                print(' ',pivoteado,
                      'intercambiar filas: ',i,
                      'con', dondemax+i)
    if vertabla==True:
        if pivoteado==0:
            print('  Pivoteo por filas NO requerido')
        else:
            print(AB)
    return(AB)

AB = pivoteafila(A,B,vertabla=True)

tamano = np.shape(AB)
n = tamano[0]
m = tamano[1]

AB0 = np.copy(AB)
B1 = np.copy(AB[:,m-1])

# Matrices L y U
L = np.identity(n,dtype=float) # Inicializa L
# eliminacion hacia adelante
for i in range(0,n-1,1):
    pivote = AB[i,i]
    adelante = i+1
    for k in range(adelante,n,1):
        factor = AB[k,i]/pivote
        AB[k,:] = AB[k,:] - AB[i,:]*factor
        
        L[k,i] = factor # llena L

U = np.copy(AB[:,:n])

# Resolver LY = B
B1  = np.transpose([B1])
AB =np.concatenate((L,B1),axis=1)

# sustitución hacia adelante
Y = np.zeros(n,dtype=float)
Y[0] = AB[0,n]
for i in range(1,n,1):
    suma = 0
    for j in range(0,i,1):
        suma = suma + AB[i,j]*Y[j]
    b = AB[i,n]
    Y[i] = (b-suma)/AB[i,i]

Y = np.transpose([Y])

# Resolver UX = Y
AB =np.concatenate((U,Y),axis=1)

# sustitución hacia atrás
ultfila = n-1
ultcolumna = m-1
X = np.zeros(n,dtype=float)

for i in range(ultfila,0-1,-1):
    suma = 0
    for j in range(i+1,ultcolumna,1):
        suma = suma + AB[i,j]*X[j]
    b = AB[i,ultcolumna]
    X[i] = (b-suma)/AB[i,i]

# SALIDA
print('matriz L: ')
print(L)
print('Matriz U: ')
print(U)
print('B1 :')
print(B1)
print('Y Sustitución hacia adelante')
print(Y)
print('X Sustitución hacia atras')
print(X)

[ Matriz A=L.U ] [ Ejercicio ] [ Algoritmo LU ] [ Función LU ] ||
[ Solución LY=B , UX=Y ]