5.4.1 Cuadratura con dos puntos – Experimento con Python

[ Cuadratura de Gauss ] [ Experimento 2 puntos ] [ Experimento algoritmo ]
..


Cuadratura con dos puntos – Experimento

Para visualizar el concepto de Cuadratura de Gauss de dos puntos considere lo siguiente:

Se tiene un corte transversal del un recipiente rectangular lleno de líquido limitado en x entre [-1,1], al que se le ha depositado encima otro recipiente con perfil f(x) = x2 hasta que reposan sus extremos en x[-1,1].

La altura de ambos recipientes es la misma.

La superficie entre f(x) y el eje x es el integral de f(x) en el intervalo.

Si suponemos que la figura es el corte transversal de una vasija y la parte en amarillo es líquida, la vasija ha desplazado el líquido que ocupa ahora el «área» mostrada en la gráfica que corresponde al integral de f(x)=x2. entre [-1,1].

Ahora, suponga que se perfora el perfil de f(x) en dos puntos equidistantes cercanos  x=0. Los orificios permitirían el desplazamiento del liquido al interior de f(x) que dejando pasar suficiente tiempo, permitiría tener todo el líquido en el recipiente rectangular entre [-1,1] como una línea horizontal.

Podría medir la altura que tiene el líquido y que tiene un equivalente en un punto f(x1). Debería encontrar el valor de x1 que permite disponer del mismo valor entre el área debajo de f(x) y el rectángulo del corte transversal amarillo ahora formado.

Se usa el resultado analítico del integral restando el área del rectángulo obtenido al evaluar la función f(x) entre [0,1], teniendo un problema de búsqueda de raíces. Obtenemos el valor de x1.

Se muestra que el área bajo f(x) es equivalente al área del rectángulo conformado.

Si  utilizamos el desplazamiento horizontal desde el centro para un punto encontrado como un «factor», tendremos que el área del rectángulo se mantendría equivalente, y el desplazamiento proporcional a la mitad del intervalo si se varía el intervalo de observación. Este factor coincide con el factor de Cuadratura de Gauss de dos puntos.

funcion  fx:   x**2
Integral Fx:   x**3/3
I analitico:   0.6666666666666666
I aproximado:  0.6666666666654123
desplaza centro:   0.5773502691890826
factor desplaza:   0.5773502691890826
Factor CuadGauss:  0.5773502691896258
erradoFactor:    1.2545520178264269e-12
error integral:  1.2543299732215019e-12

El error del integral es del orden de 10-12


Cambiamos la figura geométrica a un trapecio generado por la recta que pasa por los puntos xi desplazados desde el centro.

Usamos la función f(x) = x2 + x + 1, observaremos si los resultado son equivalentes.

La figura al inicio del experimento será:

Luego de realizar realizar el mismo cálculo anterior usando un equivalente a trapecio se tiene:

con valores numéricos:

funcion  fx:   x**2 + x + 1
Integral Fx:   x**3/3 + x**2/2 + x
I analitico:   2.6666666666666665
I aproximado:  2.6666666666654124
desplaza centro:   0.5773502691890826
factor desplaza:   0.5773502691890826
Factor CuadGauss:  0.5773502691896258
erradoFactor:    1.2545520178264269e-12
error integral:  1.2541079286165768e-12

El error del integral es también del orden de 10-12, además observe que el factor de cuadratura de Gauss se mantiene.

[ Cuadratura de Gauss ] [ Experimento 2 puntos ] [ Experimento algoritmo ]


Tarea

Realice el experimento usando un polinomio de grado superior y observe los errores para el integral y las diferencia con el coeficiente de Cuadratura de 2 puntos.

[ Cuadratura de Gauss ] [ Experimento 2 puntos ] [ Experimento algoritmo ]

..


Algoritmo con Python

Para resumir la cantidad de instrucciones, se usa el método de la bisección desde la librería Scipy y el subgrupo de funciones de optimización.

Los cálculos para realizar las gráficas se tratan en un bloque luego de mostrar los resultado principales.

# Integración: Cuadratura de Gauss de dos puntos
# modelo con varios tramos entre [a,b]
# para un solo segmento.
import numpy as np
import matplotlib.pyplot as plt
import sympy as sym
import scipy.optimize as op

# INGRESO
x = sym.Symbol('x')

# fx = (x)**2
fx = x**2 + x + 1
# fx = 0.2 + 25.0*x-200*(x**2)+675.0*(x**3)-900.0*(x**4)+400.0*(x**5)

a = -1
b = 1
muestras = 51
tolera = 1e-12
iteramax = 100

# PROCEDIMIENTO
# Desarrollo analítico con Sympy
Fx = sym.integrate(fx,x)
Fxn = sym.lambdify('x',Fx,'numpy')
Fiab = Fxn(b)-Fxn(a)

# Busca igualar trapecio con Integral analitico
fxn = sym.lambdify('x',fx,'numpy')
base = b-a
mitad = base/2
xc = (a+b)/2  # centro
diferencia = lambda x: Fiab-base*(fxn(xc-x)+fxn(xc+x))/2
desplazado =  op.bisect(diferencia,0,mitad,
                        xtol=tolera,maxiter=iteramax)
factor = desplazado/mitad

# Integral aproximando con trapecio
x0 = xc - factor*mitad
x1 = xc + factor*mitad
Faprox = base*(fxn(x0)+fxn(x1))/2

# Integral cuadratura Gauss
xa = xc + mitad/np.sqrt(3)
xb = xc - mitad/np.sqrt(3)
FcuadG = base*(fxn(xa)+fxn(xb))/2
erradofactor = np.abs(FcuadG - Faprox)
erradoIntegral = np.abs(Fiab-Faprox)
# SALIDA
print('funcion  fx:  ', fx)
print('Integral Fx:  ', Fx)
print('I analitico:  ', Fiab)
print('I aproximado: ', Faprox)
print('desplaza centro:  ', desplazado)
print('factor desplaza:  ', factor)
print('Factor CuadGauss: ', 1/np.sqrt(3))
print('erradoFactor:   ', erradofactor)
print('error integral: ', erradoIntegral) 

# Grafica
# Para GRAFICAR 
# Para gráfica f(x)
xi = np.linspace(a,b,muestras)
fi = fxn(xi)

# Para gráfica Trapecio
m = (fxn(x1)-fxn(x0))/(x1-x0)
trapeciof = lambda x: fxn(x0)+m*(x-x0)
trapecioi = trapeciof(xi)

# Areas Trapecio para cada punto que busca
k = int(muestras/2)
xicg = xi[k:muestras-1]
Fcg = [base*(fxn(xi[k+0])+fxn(xi[k-0]))/2]
for i in range(1,k,1):
    untrapecio = base*(fxn(xi[k+i])+fxn(xi[k-i]))/2
    Fcg.append(untrapecio)

# Punto buscado
Fiaprox = base*(fxn(x1)+fxn(x0))/2

Fi = Fxn(xi)-Fxn(a)

# Areas de curvas y trapecio

plt.subplot(211) # Grafica superior
plt.xlim(a,b)
plt.plot(xi, fi, label='f(x)')
# Solo fi
# plt.fill_between(xi,0, fi,
#                label='integral fi',
#                 color='yellow')
# usando cuadratura
plt.fill_between(xi,0, trapecioi,
                 label='Cuadratura 2 puntos',
                 color='yellow')
plt.axvline(x0,color='white')
plt.axvline(x1,color='white')
plt.plot([x0,x1],[fxn(x0),fxn(x1)],
         'ro', label='x0,x1')
plt.axvline(0,color='black')
plt.xlabel('x')
plt.ylabel('f(x) y Cuadratura de 2 puntos')
plt.legend()

# Valores de integrales
plt.subplot(212) # Grafica inferior
plt.xlim(a,b)
plt.axhline(Fiab, label='F[a,b]')
# plt.plot(xi,Fi,label='F(x)')
plt.plot(xicg,Fcg,color='orange',label='Aprox Trapecio')

plt.axvline(x1,color='yellow')
plt.axvline((1/np.sqrt(3))*(b-a)/2 + xc ,color='magenta')
plt.plot(x1,Fiaprox,'ro', label='x0,x1')
plt.axvline(0,color='black')

plt.xlabel('x')
plt.legend()
plt.ylabel('Integrando')
plt.show()

[ Cuadratura de Gauss ] [ Experimento 2 puntos ] [ Experimento algoritmo ]

5.4 Cuadratura de Gauss con Python

[ Cuadratura de Gauss ] [ Ejercicio ] [Algoritmo/función Python]

..


Cuadratura de Gauss

Referencia: Chapra 22.3 p655, Rodríguez 7.3 p294, Burden 4.7 p168

La cuadratura de Gauss aproxima el integral de una función en un intervalo [a,b] centrado en cero mediante un cálculo numérico con menos operaciones y evaluaciones de la función. Se representa como una suma ponderada:

I \cong \Big( \frac{b-a}{2} \Big)\Big(c_0f(x_a) + c_1f(x_b)\Big)

regla Cuadratura Gauss 01

para la fórmula de dos puntos con referencia a una función centrada en cero y ancho unitario a cada lado, se tiene:

c_0 = c_1 = 1, x_0 = -\frac{1}{\sqrt{3}}, x_1 = \frac{1}{\sqrt{3}}

Para un intervalo de evaluación desplazado en el eje x se requiere convertir los puntos al nuevo intervalo. Se desplaza el punto cero al centro del intervalo [a,b] y se corrige el desplazamiento hacia la izquierda y derecha del centro con x0 y x1.

regla Cuad Gauss 02

x_a = \frac{b+a}{2} - \frac{b-a}{2}\Big(\frac{1}{\sqrt{3}} \Big) x_b = \frac{b+a}{2} + \frac{b-a}{2}\Big(\frac{1}{\sqrt{3}} \Big)

con lo que el resultado aproximado del integral se convierte en:

I \cong \frac{b-a}{2}(f(x_a) + f(x_b))

cuya fórmula es semejante a una mejor aproximación de un trapecio, cuyos promedios de alturas son puntos internos de [a,b], concepto mostrado en la gráfica.

[ Cuadratura de Gauss ] [ Ejercicio ] [Algoritmo/función Python]

..


Ejercicio

Para el ejercicio y comparación de resultado con los otros métodos, se realiza el cálculo para un tramo en el intervalo [a,b].

\int_1^3 \sqrt{x} \sin(x) dx x_a = \frac{3+1}{2} - \frac{3-1}{2}\Big(\frac{1}{\sqrt{3}}\Big)= 1.4226 x_b = \frac{3+1}{2} + \frac{3-1}{2}\Big(\frac{1}{\sqrt{3}} \Big) = 2.5773 f(1.4226) = \sqrt{1.4226} \sin(1.4226) = 1.1796 f(2.5773) = \sqrt{2.5773} \sin(2.5773) = 0.8585

con lo que el resultado aproximado del integral se convierte en:

I \cong \frac{3-1}{2}(1.1796 + 0.8585) = 2.0382

que usando instrucciones de Python para obtener los valores:

>>> import numpy as np
>>> (3+1)/2-(3-1)/2*(1/np.sqrt(3))
1.4226497308103743
>>> (3+1)/2+(3-1)/2*(1/np.sqrt(3))
2.5773502691896257
>>> fx = lambda x: np.sqrt(x)*np.sin(x)
>>> fx(1.4226)
1.1796544827404145
>>> fx(2.5773)
0.8585957175067221
>>> ((3-1)/2)*(fx(1.4226)+fx(2.5773))
2.0382

el resultado se puede mejorar aumentando el número de tramos en el intervalo [a,b]. Por ejemplo, el resultado usando 4 tramos el resultado es semejante al usar el método del trapecio con 128 tramos, lo que muestra el ahorro en cálculos entre los métodos

Integral:  2.05357719003
>>> 

Concepto:

Ejercicio:

[ Cuadratura de Gauss ] [ Ejercicio ] [Algoritmo/función Python]

..


Algoritmo con Python

Para el ejercicio anterior, usando al 4 segmentos y en cada uno aplicando Cuadratura de Gauss, el integral resultante es:

[xa,xb,f(xa),f(xb)]
[1.1056624327025935, 1.3943375672974065, 0.9397945621004238, 1.1624843542124732]
[xa,xb,f(xa),f(xb)]
[1.6056624327025935, 1.8943375672974065, 1.2663772374235445, 1.3049381813350678]
[xa,xb,f(xa),f(xb)]
[2.1056624327025935, 2.3943375672974065, 1.2484263248036183, 1.0516320347815513]
[xa,xb,f(xa),f(xb)]
[2.6056624327025935, 2.8943375672974065, 0.8242800855599416, 0.4163759798980186]
Integral:  2.0535771900286597

Instrucciones en Python usando la Cuadratura de Gauss de dos puntos para una función f(x):

# Integración: Cuadratura de Gauss de dos puntos
# modelo con varios tramos entre [a,b]
import numpy as np
import matplotlib.pyplot as plt

# cuadratura de Gauss de dos puntos
def integraCuadGauss2p(fx,a,b, vertabla=False):
    ''' funcion fx, intervalo[a,b]
        vertabla=True para ver resultados parciales 
    '''
    x0 = -1/np.sqrt(3)
    x1 = -x0
    xa = (b+a)/2 + (b-a)/2*(x0)
    xb = (b+a)/2 + (b-a)/2*(x1)
    area = ((b-a)/2)*(fx(xa) + fx(xb))
    if vertabla==True:
        print('[xa,xb,f(xa),f(xb)],area')
        print([xa,xb,fx(xa),fx(xb)],area)
    return(area)

# INGRESO
fx = lambda x: np.sqrt(x)*np.sin(x)

# intervalo de integración
a = 1
b = 3
tramos = 4

# PROCEDIMIENTO
muestras = tramos+1
xi = np.linspace(a,b,muestras)
area = 0
for i in range(0,muestras-1,1):
    deltaA = integraCuadGauss2p(fx,xi[i],xi[i+1],vertabla=True)
    area = area + deltaA
# SALIDA
print('Integral: ', area)

Gráfica por tramos

La gráfica que complementa el resultado anterior, se realiza añadiendo las instrucciones presentadas a continuación.

Considere que la gráfica es útil con pocos tramos en el intervalo[a,b]

regla Cuad Gauss 04

# GRAFICAR por cada Segmento/tramo
# para concepto con 'pocos' segmentos
subtramo = xi
muestrastramo = 10
x0 = -1/np.sqrt(3) 
x1 = 1/np.sqrt(3)

# gráficas de todos los tramos
aj = [] ; bj = []
xj = [] ; fj = []
recta = []

for i in range(0,tramos,1):
    ai = subtramo[i]
    bi = subtramo[i+1]
    
    xk = np.linspace(ai,bi,muestrastramo)
    fk = fx(xk)
    
    xj = xj + list(xk)
    fj = fj + list(fk)

    # puntos xa y xb por tramo
    xa = (bi+ai)/2 + (bi-ai)/2*(x0)
    xb = (bi+ai)/2 + (bi-ai)/2*(x1)
    
    aj.append(xa)
    bj.append(xb)
    
    # Recta entre puntos x0 y x1 por tramo
    m = (fx(xb)-fx(xa))/(xb-xa)
    b0 = fx(xa) - m*xa
    linea = b0 + m*xk
    recta = recta + list(linea)

# Marcadores 'o' de xa y xb por tramos
puntox = np.concatenate((aj,bj))
puntoy = fx(puntox)

# Trazado de lineas
plt.plot(xj,recta, label = 'grado 1', color = 'tab:orange')
plt.fill_between(xj,0,recta, color='tab:olive')
plt.plot(xj,fj, label='f(x)', color = 'blue')

# Verticales para dividir los tramos
for i in range(0,len(subtramo),1):
    plt.axvline(subtramo[i], color='tab:gray')
    
# Marcadores de puntos xa y xb por tramos
for j in range(0,len(aj),1):
    plt.axvline(aj[j], color='w')
    plt.axvline(bj[j], color='w')
plt.plot(puntox,puntoy, 'o', color='g')

plt.title('Integral: Cuadratura Gauss')
plt.xlabel('x')
plt.ylabel('f(x)')
plt.legend()

plt.show()

[ Cuadratura de Gauss ] [ Ejercicio ] [Algoritmo/función Python]


Referencia: Tabla 22.1 Chapra p661

Para un integral con intervalo centrado en el origen.

I \cong c_0 f(x_0) + c_1f(x_1) + … + c_{n-1}f(x_{n-1})
Factores usados en las fórmulas de Gauss-Legendre.
Puntos Factor de ponderación Argumentos de la función Error de truncamiento
2 c0 = 1.0000000
c1 = 1.0000000
x0 = – 0.577350269
x1 = 0.577350269
≅ f(4)(x )
3 c0 = 0.5555556
c1 = 0.8888889
c2 = 0.5555556
x0 = – 0.774596669
x1 = 0.0
x2 = 0.774596669
≅ f(6)(x )
4 c0 = 0.3478548
c1 = 0.6521452
c2 = 0.6521452
c3 = 0.3478548
x0 = – 0.861136312
x1 = – 0.339981044
x2 = 0.339981044
x3 = 0.861136312
≅f (8)(x )

5.3 Regla de Simpson 3/8 con Python

[ Simpson 3/8 ] [ Ejercicio ] [Algoritmo /función Python]
..


Regla de Simpson 3/8

Referencia: Chapra 21.2.1 p.631, Burden 4.2 p147, Rodríguez 7.1.8 p288

I\cong \frac{3h}{8}[f(x_0)+3f(x_1) +3 f(x_2)+f(x_3)]

regla de Simpson 3/8

Es el resultado cuando para el integral se utiliza el resultado de una interpolación con polinomio de tercer grado.

I = \int_a^b f(x) \delta x I \cong \int_a^b f_3 (x) \delta x

Al desarrollar la fórmula de la Regla de Simpson de 3/8 para un segmento con tres tramos con distancia h:

I\cong \frac{3h}{8}[f(x_0)+3f(x_1) +3 f(x_2)+f(x_3)]

siendo el tramo de un segmento [a,b]

h=\frac{b-a}{3}

Error de Truncamiento

error_{truncamiento} = -\frac{3}{80} h^5 f^{(4)} (z)

donde z se encuentra entre [a,b]

[ Simpson 3/8 ] [ Ejercicio ] [Algoritmo /función Python]

..


Ejercicio

Para integrar la función en el intervalo [1,3] con 6, 18, 24 y 72 tramos,

f(x)= \sqrt {(x)} \sin(x) 1 \leq x \leq 3

Para el ejercicio planteado en la regla de trapecio, usando seis tramos, se aplica el método cada tres tramos.

f(x)= \sqrt {(x)} \sin(x) 1 \leq x \leq 3

tramos = 6

h = \frac{3-1}{6} = \frac{1}{3} I\cong \frac{3}{8}\Big(\frac{1}{3}\Big)[f(1)+3f\Big(1+\frac{1}{3}\Big) +3f\Big(1+\frac{2}{3}\Big) + f(2)] + + \frac{3}{8}\Big(\frac{1}{3}\Big)[f(2)+3f\Big(2+\frac{1}{3}\Big) +3f\Big(2+\frac{2}{3}\Big)+ f(3)] f(1)= \sqrt {(1)} \sin(1) = 0.8414 f(4/3)= \sqrt {(4/3)} \sin(4/3) = 1.1222 f(5/3)= \sqrt {(5/3)} \sin(5/3) = 1.2850 f(2)= \sqrt {(2)} \sin(2) = 1.2859 f(7/3)= \sqrt {(7/3)} \sin(7/3) = 1.1045 f(8/3)= \sqrt {(8/3)} \sin(8/3) = 0.7467 f(3)= \sqrt {(3)} \sin(3) = 0.2444 I\cong \frac{3}{24}[0.8414+3(1.1222)+3(1.2850)+1.2859] + \frac{3}{24}[1.2859+3(1.1045)+3(0.7467)+0.2444] I\cong 2.0542

las muestras para el ejercicio son:

>>> import numpy as np
>>> fx = lambda x: np.sqrt(x)*np.sin(x)
>>> xi = [1, 1+1/3, 1+2/3, 2, 1+4/3, 1+5/3, 3]
>>> fx(xi)
array([0.84147098, 1.12229722, 1.28506615, 1.28594075,
       1.10453193, 0.74672307, 0.24442702]
>>> I1=(3/8)*(1/3)*(fx(1)+3*fx(1+1/3)+3*fx(1+2/3)+fx(2))
>>> I2=(3/8)*(1/3)*(fx(2)+3*fx(1+4/3)+3*fx(1+5/3)+fx(3))
>>> I1+I2
2.0542043270535757
>>>

[ Simpson 3/8 ] [ Ejercicio ] [Algoritmo /función Python]

..


Algoritmo en Python

A partir del algoritmo para Simpson de 1/3, se realizan modificaciones para obtener el algoritmo de Simpson de 3/8:

Resultados de algoritmo

tramos:  6
Integral con Simpson 3/8:  2.0542043270535757

Caso: f(x) es una expresión matemática

# Integración Simpson 3/8
import numpy as np
import matplotlib.pyplot as plt

def integrasimpson38(fx,a,b,tramos):
    ''' integral con método de Simpson 3/8
        tramos debe ser múltiplo de 3
    '''
    # Validar cantidad de tramos múltiplo de 3
    esmult3 = tramos%3
    if esmult3 != 0:
        suma = 'tramos debe ser múltiplo de 3'
    # Regla de Simpson 3/8
    if esmult3 == 0:
        h = (b-a)/tramos
        xi = a
        suma = 0
        for i in range(0,tramos,3):
            unS38 = (3/8)*h*(fx(xi)+3*fx(xi+h)+3*fx(xi+2*h)+fx(xi+3*h))
            suma = suma + unS38
            xi = xi + 3*h
    return(suma)

# PROGRAMA -----------------
# INGRESO
fx = lambda x: np.sqrt(x)*np.sin(x)

# intervalo de integración
a = 1
b = 3
tramos = 6

# PROCEDIMIENTO
Area = integrasimpson38(fx,a,b,tramos)

# SALIDA
print('tramos: ',tramos)
print('Integral con Simpson 3/8: ',Area)
if type(Area)==str:
    print('  Revisar errores...')

Gráfica

Se puede observar mejor lo expresado usando la gráfica para observar los tramos, muestras, por segmento.

Se puede cambiar el número de tramos en el algoritmo anterior, considerando que deben ser múltiplo de 3.

reglas Simpson 3/8Instrucciones en Python adicionales al algoritmo anterior

# GRAFICA
# fx muestras por tramo
muestras = tramos + 1
xi = np.linspace(a,b,muestras)
fi = fx(xi)
fi0 = np.zeros(muestras) # linea base

# fx suave aumentando muestras
muestrasfxSuave = 4*tramos + 1
xk = np.linspace(a,b,muestrasfxSuave)
fk = fx(xk)

# Relleno
for i in range(0,muestras-2,3):
    relleno = 'lightgreen'
    if (i/3)%2==0: # i/3 multiplo 2
        relleno ='lightblue'
    xktramo = xk[i*4:(i+3)*4+1]
    fktramo = fk[i*4:(i+3)*4+1]
    plt.fill_between(xktramo,fktramo,fktramo*0,color=relleno)

# Funcion f(x)
plt.plot(xk,fk, label='f(x)')
plt.plot(xi,fi,'o', label='f(xi)')
         
# Divisiones entre Simpson 3/8
for i in range(0,muestras,1):
    tipolinea = 'dotted'
    if i%3==0: # multiplo de 3
        tipolinea = 'dashed'
    plt.vlines(xi[i],fi0[i],fi[i],
                 linestyle=tipolinea)

plt.axhline(0)
plt.xlabel('x')
plt.ylabel('f')
plt.title('Integral: Regla de Simpson 3/8')
plt.legend()
plt.show()

Caso: x[i], f[i] son muestras

xi = [1.        , 1.33333333, 1.66666667, 2.        ,
      2.33333333, 2.66666667, 3.        ]
fi = [0.84147098, 1.12229722, 1.28506615, 1.28594075,
      1.10453193, 0.74672307, 0.24442702]
tolera = 1e-7

Instrucciones en Python

# Integración Simpson 1/3
# Usando una muestras xi,fi
import numpy as np
import matplotlib.pyplot as plt

def integrasimpson38_fi(xi,fi,tolera = 1e-10):
    ''' sobre muestras de fi para cada xi
        integral con método de Simpson 3/8
        respuesta es np.nan para tramos desiguales,
        no hay suficientes puntos.
    '''
    n = len(xi)
    i = 0
    suma = 0
    while not(i>=(n-3)):
        h  = xi[i+1]-xi[i]
        h1 = (xi[i+2]-xi[i+1])
        h2 = (xi[i+3]-xi[i+2])
        dh = abs(h-h1)+abs(h-h2)
        if dh<tolera:# tramos iguales
            unS38 = fi[i]+3*fi[i+1]+3*fi[i+2]+fi[i+3]
            unS38 = (3/8)*h*unS38
            suma = suma + unS38
        else:  # tramos desiguales
            suma = 'tramos desiguales'
        i = i + 3
    if (i+1)<n: # incompleto, tramos por calcular
        suma = 'tramos incompletos, faltan '
        suma = suma +str(n-(i+1))+' tramos'
    return(suma)

# PROGRAMA -----------------
# INGRESO
xi = [1.        , 1.33333333, 1.66666667, 2.        ,
      2.33333333, 2.66666667, 3.        ]
fi = [0.84147098, 1.12229722, 1.28506615, 1.28594075,
      1.10453193, 0.74672307, 0.24442702]
tolera = 1e-7

# PROCEDIMIENTO
n = len(xi)-1
Area = integrasimpson38_fi(xi,fi,tolera)

# SALIDA
print('tramos: ',n)
print('Integral con Simpson 3/8: ',Area)
if type(Area)==str:
    print('  Revisar errores')

[ Simpson 3/8 ] [ Ejercicio ] [Algoritmo /función Python]

5.2 Regla de Simpson 1/3 con Python

[ Simpson 1/3 ] [ Ejercicio ] [Algoritmo Python] [ función Python]
..


La regla de Simpson 1/3

Referencia: Chapra 21.2.1 p631, Burden 4.3 p144, Rodríguez 7.1.4 p281

I\cong \frac{h}{3}[f(x_0)+4f(x_1) + f(x_2)]

regla Simpson 1/3

Es el resultado cuando se realiza una interpolación con polinomio de segundo grado.

I = \int_a^b f(x) \delta x \cong \int_a^b f_2 (x) \delta x

Se puede obtener usando un polinomio de Lagrange de segundo grado:

I = \int_{x_0}^{x_2} \bigg[ \frac{(x-x_1)(x-x_2)}{(x_0-x_1)(x_0-x_2)} f(x_0) + + \frac{(x-x_0)(x-x_2)}{(x_1-x_0)(x_1-x_2)} f(x_1) + + \frac{(x-x_0)(x-x_1)}{(x_2-x_0)(x_2-x_1)} f(x_2) \bigg] \delta x

que simplificando tiene como resultado para un solo tramo:

I\cong \frac{h}{3}[f(x_0)+4f(x_1) + f(x_2)]

siendo h el tamaño de paso, donde para la expresión el divisor debe ser par, múltiplo de 2, al cubrir todo el intervalo [a,b]. En caso de usar valores de muestras xi, fi, el valor de h debe ser constante.

h=\frac{b-a}{2}

Error de truncamiento

la cota del error de truncamiento se estima como O(h5)

error_{trunca} = -\frac{h^5}{90} f^{(4)}(z)

para un valor de z dentro del intervalo [a,b] de integración.

para cuantificar el valor, se puede usar la diferencia finita Δ4f, pues con la derivada sería muy laborioso.

[ Simpson 1/3 ] [ Ejercicio ] [Algoritmo Python] [ función Python]

..


Ejercicio

Para integrar la función en el intervalo [1,3] con 4, 16, 32 ,64 y 128 tramos,

f(x)= \sqrt {(x)} \sin(x) 1 \leq x \leq 3

Para el ejercicio planteado en la regla de trapecio, usando cuatro tramos, se aplica el método cada dos tramos.

tramos = 4 h = \frac{3-1}{4} = \frac{1}{2} = 0.5 I\cong \frac{0.5}{3}[f(1)+4f(1.5) + f(2)] + + \frac{0.5}{3}[f(2)+4f(2.5) + f(3)] f(1)= \sqrt {(1)} \sin(1) = 0.8414 f(1.5)= \sqrt {(1.5)} \sin(1.5) = 1.2216 f(2)= \sqrt {(2)} \sin(2) = 1.2859 f(2.5)= \sqrt {(2.5)} \sin(2.5) = 0.9462 f(3)= \sqrt {(3)} \sin(3) = 0.2444 I\cong \frac{0.5}{3}[0.8414+4(1.2216) + 1.2859] + + \frac{0.5}{3}[1.2859+4(0.9462) + 0.2444] I\cong 2.054

Note que al usar Simpson 1/3 con 4 tramos el resultado tiene los 2 primeros decimales iguales a usar Trapecio con 16 tramos.

>>> import numpy as np
>>> fx = lambda x: np.sqrt(x)*np.sin(x)
>>> xi = [1, 1+1/2, 1+2/2, 1+3/2,3]
>>> xi
[1, 1.5, 2.0, 2.5, 3]
>>> fx(xi)
array([0.84147098, 1.22167687, 1.28594075, 
       0.94626755, 0.24442702])
>>> (0.5/3)*(fx(1)+4*fx(1.5)+fx(2)) + (0.5/3)*(fx(2)+4*fx(2.5)+fx(3))
2.0549261957703937
>>>

Para más de 4 tramos es preferible realizar las operaciones con un algoritmo.

[ Simpson 1/3 ] [ Ejercicio ] [Algoritmo Python] [ función Python]
..


Algoritmo en Python

Del ejercicio con trapecios, se repite el ejercicio con n tramos; usando dos tramos (tres puntos) en cada iteración.
Cada iteración se procesa avanzando dos puntos xi, xi+h, xi+2h . Ejemplo:

tramos:  8
Integral:  2.053709383061734
>>> 

Instrucciones en Python

Se realiza mediante la aplicación directa de la fórmula para cada segmento conformado de dos tramos. Se verifica que el valor de tramos sea par.

# Integración: Regla Simpson 1/3
import numpy as np
import matplotlib.pyplot as plt

# INGRESO:
fx = lambda x: np.sqrt(x)*np.sin(x)

# intervalo de integración
a = 1
b = 3
tramos = 8

# Validar cantidad de tramos pares
esimpar = tramos%2
while (esimpar == 1):
    print('tramos: ',tramos)
    tramos = int(input('tramos debe ser par: '))
    esimpar = tramos%2

# PROCEDIMIENTO
# Regla de Simpson 1/3
h = (b-a)/tramos
xi = a
area = 0
for i in range(0,tramos,2):
    deltaA = (h/3)*(fx(xi)+4*fx(xi+h)+fx(xi+2*h))
    area = area + deltaA
    xi = xi + 2*h

# SALIDA
print('tramos:', tramos)
print('Integral: ', area)

Gráfica

Se puede observar mejor lo expresado usando la gráfica para observar los tramos, muestras, por segmento. Para este caso se ejecuta el algoritmo anterior usando tramos =8

regla Simpson 1/3Instrucciones en Python adicionales al algoritmo anterior

# GRAFICA
# fx muestras por tramo
muestras = tramos + 1
xi = np.linspace(a,b,muestras)
fi = fx(xi)
fi0 = np.zeros(muestras) # linea base

# fx suave aumentando muestras
muestrasfxSuave = 4*tramos + 1
xk = np.linspace(a,b,muestrasfxSuave)
fk = fx(xk)

# Relleno
for i in range(0,muestras-1,2):
    relleno = 'lightgreen'
    if (i/2)%2==0: # i/2 par
        relleno ='lightblue'
    xktramo = xk[i*4:(i+2)*4+1]
    fktramo = fk[i*4:(i+2)*4+1]
    plt.fill_between(xktramo,fktramo,fktramo*0,color=relleno)

# Funcion f(x)
plt.plot(xk,fk, label='f(x)')
plt.plot(xi,fi,'o', label='f(xi)')
         
# Divisiones entre Simpson 1/3
for i in range(0,muestras,1):
    tipolinea = 'dotted'
    if i%2==0: # i par
        tipolinea = 'dashed'
    plt.vlines(xi[i],fi0[i],fi[i],
                 linestyle=tipolinea)

plt.axhline(0)
plt.xlabel('x')
plt.ylabel('f')
plt.title('Integral: Regla de Simpson 1/3')
plt.legend()
plt.show()

[ Simpson 1/3 ] [ Ejercicio ] [Algoritmo Python] [ función Python]
..


Algoritmo como función de Python

Caso: f(x) es una expresión matemática

A partir del ejercicio anterior, se resume en una función de Python si  la entrada f(x) es una expresión matemática:

def integrasimpson13(fx,a,b,tramos):
    ''' integral con método de Simpson 1/3
        tramos debe debe ser múltiplo de 2
    '''
    # Validar cantidad de tramos pares
    esmult2 = tramos%2
    if esmult2 != 0:
        suma = 'tramos debe ser múltiplo de 2'
    # Regla de Simpson 1/3
    if esmult2 == 0:
        h = (b-a)/tramos
        xi = a
        suma = 0
        for i in range(0,tramos,2):
            unS13 = (h/3)*(fx(xi)+4*fx(xi+h)+fx(xi+2*h))
            suma = suma + unS13
            xi = xi + 2*h
    return(suma)

Caso: x[i], f[i] son muestras

xi = [1. , 1.5, 2. , 2.5, 3.]
fi = [0.84147098, 1.22167687, 1.28594075,
      0.94626755, 0.24442702]

Cuando se integra sobre muestras dadas por los vectores xi, fi. Se revisa que los tramos entre muestras sean iguales para un Simpson 1/3 y que existan suficientes puntos para completar el integral.

# Integración Simpson 1/3
# Usando una muestras xi,fi
import numpy as np
import matplotlib.pyplot as plt

def integrasimpson13_fi(xi,fi,tolera = 1e-10):
    ''' sobre muestras de fi para cada xi
        integral con método de Simpson 1/3
        respuesta es np.nan para tramos desiguales,
        no hay suficientes puntos.
    '''
    n = len(xi)
    i = 0
    suma = 0
    while not(i>=(n-2)):
        h = xi[i+1]-xi[i]
        dh = abs(h - (xi[i+2]-xi[i+1]))
        if dh<tolera:# tramos iguales
            unS13 = (h/3)*(fi[i]+4*fi[i+1]+fi[i+2])
            suma = suma + unS13
        else:  # tramos desiguales
            suma = 'tramos desiguales en i: '+str(i)
            suma = suma ' , '+str(i+2)
        i = i + 2
    if i<(n-1): # incompleto, faltan tramos por calcular
        suma = 'tramos incompletos, faltan '
        suma = suma ++str((n-1)-i)+' tramos'
    return(suma)

# PROGRAMA -----------------
# INGRESO
xi = [1. , 1.5, 2. , 2.5, 3.]
fi = [0.84147098, 1.22167687, 1.28594075,
      0.94626755, 0.24442702]
# PROCEDIMIENTO
Area = integrasimpson13_fi(xi,fi)

# SALIDA
print('tramos: ',len(xi)-1)
print('Integral con Simpson 1/3: ',Area)
if type(Area)==str:
    print('  Revisar errores...')

resultados

tramos:  4
Integral con Simpson 1/3:  2.0549261966666665
>>> 

[ Simpson 1/3 ] [ Ejercicio ] [Algoritmo Python] [ función Python]


Fórmula con varios segmentos y h constante

Usado cuando el intervalo a integrar tiene varios segmentos, cada segmento tiene dos tramos. Ejemplo para dos segmentos, cuatro tramos, semejante al usado en la gráfica. La simplificación es válida si h es constante.

I\cong \frac{h}{3}[f(x_0)+4f(x_1) + f(x_2)] + + \frac{h}{3}[f(x_2)+4f(x_3) + f(x_4)]

tomando factor común h/3

I\cong \frac{h}{3}[f(x_0)+4f(x_1) + 2f(x_2) + +4f(x_3) + f(x_4)]

[ Simpson 1/3 ] [ Ejercicio ] [Algoritmo Python] [ función Python]

5.1 Regla del Trapecio para Integración numérica con Python

[ Regla del trapecio ] [ Ejercicio ] [Algoritmo Python] [ función Python]

..


Regla del Trapecio

regla trapecio 01Referencia: Chapra 21.1 p621  Burden 4.3 p142, Rodríguez 7.1.1 p27

La integración numérica con la regla del trapecio usa un polinomio de primer grado como aproximación de f(x) entre los extremos a y b del intervalo.

Es la primera de las formulas cerradas de Newton-Cotes.

I = \int_a^b f(x) dx \cong \int_a^b f_1 (x) dx

usando el rango entre [a,b] el polinomio se aproxima con una línea recta:

f_1 (x) = f(a) + \frac{f(b)-f(a)}{b-a} (x-a)

el área bajo esta línea recta es una aproximación de la integral de f(x) entre los límites a y b.

El resultado del integral es la regla del trapecio:

I = (b-a) \frac{f(a)+f(b)}{2}

que se interpreta como la multiplicación entre la base y altura promedio de un trapecio. También se llega al resultando sumando las áreas de sus componentes: rectángulo y triángulo.

El error de truncamiento se encuentra como el integral del término que le sigue al polinomio de Taylor en la aproximación, es decir el de grado 2, que al integrarlo tiene un orden de h3. (Rodríguez p275)

error_{truncar} = -\frac{h^3}{12}f''(z)

a < z < b

[ Regla del trapecio ] [ Ejercicio ] [Algoritmo Python] [ función Python]

..


Ejemplo de Integración con el método del trapecio

Para integrar la función  en el intervalo [1,3] con 4, 16, 32 ,64 y 128 tramos,

f(x)= \sqrt {(x)} \sin(x)

regla trapecio 02

Tramos = 4

La base de los trapecios (xi-xi+1), si esta base tiene valores equidistantes se sustituye por la variable h .

base =\frac{b-a}{tramos}=\frac{3-1}{4}= 0.5 = h

para cada tramo, el área de un trapecio es:

A_i = base\frac{f(x_i)+f(x_{i+1})}{2}

por lo que cada trapecio en cada sub-intervalo tendrá un área de:

A_1 = (0.5)\frac{f(1)+f(1.5)}{2} A_2 = (0.5)\frac{f(1.5)+f(2)}{2} A_3 = (0.5)\frac{f(2)+f(2.5)}{2} A_4 = (0.5)\frac{f(2.5)+f(3)}{2}

y el integral de todo el intervalo es la suma de todos los trapecios.

Integral = A_1+ A_2+ A_3+A_4

si se quiere tomar el factor común entre las sumas de áreas de cada tramo, también se tiene la forma de la ecuación con un h equidistante entre cada tramo del intervalo.

Integral = \frac{0.5}{2}\Big(f(1)+2f(1.5) +2f(2)+2f(2.5)+f(3)\Big)

Interpretando la fórmula,  el integral es la suma de:

  • una vez la función evaluada en cada extremo,
  • mas dos veces cada valor de la función evaluada en los puntos intermedios.
  • El resultado de la suma se multiplica por h/2.

En caso que los puntos no sean equidistantes la simplificación NO procede, y se sumaran las áreas de los trapecios de cada tramo.

Siguiendo el procedimiento, para cada cantidad de tramos en el intervalo, los resultados serán:

tramos: 4
Integral: 1.99841708623
tramos:  16
Integral:  2.05019783717
tramos:  32
Integral:  2.05277225085
tramos:  64
Integral:  2.05341563776
tramos: 128
Integral:  2.05357647096

[ Regla del trapecio ] [ Ejercicio ] [Algoritmo Python] [ función Python]

..


Algoritmo en Python – Regla o Método del trapecio

Algoritmo con fórmula simplificada para varios tramos, con puntos de muestras equidistantes h.

# Integración: Regla de los trapecios
# Usando una función fx()
import numpy as np
import matplotlib.pyplot as plt

# INGRESO
fx = lambda x: np.sqrt(x)*np.sin(x)

# intervalo de integración
a = 1
b = 3
tramos = 4

# PROCEDIMIENTO
# Regla del Trapecio
# Usando tramos equidistantes en intervalo
h = (b-a)/tramos
xi = a
suma = fx(xi)
for i in range(0,tramos-1,1):
    xi = xi + h
    suma = suma + 2*fx(xi)
suma = suma + fx(b)
area = h*(suma/2)

# SALIDA
print('tramos: ', tramos)
print('Integral: ', area)

La gráfica como ayuda didáctica, si la cantidad de tramos sea «poca», muestra los trapecios, si aumenta la cantidad de tramos, no es necesario poner líneas verticales en blanco para separar los trapecios:

# GRAFICA
# Puntos de muestra
muestras = tramos + 1
xi = np.linspace(a,b,muestras)
fi = fx(xi)
# Linea suave
muestraslinea = tramos*10 + 1
xk = np.linspace(a,b,muestraslinea)
fk = fx(xk)

# Graficando
plt.plot(xk,fk, label ='f(x)')
plt.plot(xi,fi, marker='o',
         color='orange', label ='muestras')

plt.xlabel('x')
plt.ylabel('f(x)')
plt.title('Integral: Regla de Trapecios')
plt.legend()

# Trapecios
plt.fill_between(xi,0,fi, color='g')
for i in range(0,muestras,1):
    plt.axvline(xi[i], color='w')

plt.show()

Para rellenar el área bajo la curva se usa la instrucción plt.fill_between(xi,0,fi, color=’g’). La división de los trapecios con una línea blanca (white) se realiza con la instrucción plt.axvline(xi[i], color=’w’).

[ Regla del trapecio ] [ Ejercicio ] [Algoritmo Python] [ función Python]

..


Algoritmo como función de Python

Caso: f(x) es una expresión matemática

A partir del ejercicio anterior, se resume en una función de Python si  la entrada f(x) es una expresión matemática:

def integratrapecio(fx,a,b,tramos):
    h = (b-a)/tramos
    xi = a
    suma = fx(xi)
    for i in range(0,tramos-1,1):
        xi = xi + h
        suma = suma + 2*fx(xi)
    suma = suma + fx(b)
    area = h*(suma/2)
    return(area)

Caso: x[i], f[i] son muestras

Algoritmo usando las muestras y para tramos que pueden tener distancias arbitrarias. En este caso se usa la formula del área del trapecio para cada tramo.

Usado para ejercicios donde los datos proporcionados son muestras. Se adapta el bloque de ingreso y el procedimiento inicia desde «Regla de Trapecio». La cantidad de tramos será el numero de muestras menos uno tramos = len(xi) - 1

# Integración: Regla de los trapecios
# Usando una muestras xi,fi
import numpy as np
import matplotlib.pyplot as plt

def integratrapecio_fi(xi,fi):
    ''' sobre muestras de fi para cada xi
        integral con método de trapecio
    '''
    n = len(xi)
    suma = 0
    for i in range(0,n-1,1):
        dx = xi[i+1]-xi[i]
        untrapecio = dx*(fi[i+1]+fi[i])/2
        suma = suma + untrapecio
    return(suma)

# PROGRAMA -----------------
# INGRESO
xi = [1. , 1.5, 2. , 2.5, 3. ]
fi = [0.84147098, 1.22167687, 1.28594075,
      0.94626755, 0.24442702]

# PROCEDIMIENTO
Area = integratrapecio_fi(xi,fi)

# SALIDA
print('tramos: ',len(xi)-1)
print('Integral con trapecio: ',Area)

[ Regla del trapecio ] [ Ejercicio ] [Algoritmo Python] [ función Python]

4.9 Interpolar – Mascota por 50 años

Ejercicio de interpolación

  • Desarrollar por grupos
  • integrar soluciones en un mismo gráfico
  • mostrar polinomios y rangos de existencia

# caparazon superior
xi = [113., 117, 134, 153, 169, 184, 194, 203]
fi = [127., 141, 161, 166, 160, 155, 140, 132]
puntos = [[xi,fi]]

# caparazon inferior 1
xi = [113., 123, 130, 149, 182, 197, 208, 211]
fi = [127., 116, 112, 107, 110, 114, 112, 108]
puntos.append([xi,fi])

# caparazon inferior 2
xi = [107., 114, 120, 143, 170, 192, 210]
fi = [124., 114, 108, 99, 99, 106, 107]
puntos.append([xi,fi])

# Patas 01
xi = [110., 116, 120, 121, 134, 143, 153, 168, 173, 177, 181, 188, 194, 201, 205, 210, 214, 218]
fi = [92., 86, 89, 86, 87, 95, 91, 93, 85, 85, 83, 86, 88, 86, 91, 87, 92, 92]
puntos.append([xi,fi])

# Patas 02
xi = [109., 115, 117, 120, 125, 130, 134, 138, 142, 145, 150, 152, 154]
fi = [91., 87, 82, 82, 78, 78, 81, 78, 76, 79, 77, 80, 86]
puntos.append([xi,fi])

# Patas 03
xi = [172., 175, 182, 186, 191, 195, 201, 205, 210, 211, 217]
fi = [86., 79, 79, 77, 82, 78, 81, 79, 82, 85, 90]
puntos.append([xi,fi])

# Patas 04
xi = [113., 118, 122, 127, 132, 136, 140, 144, 149, 152]
fi = [88., 90, 88, 86, 85, 83, 87, 84, 86, 85]
puntos.append([xi,fi])

# Rabito 01
xi = [97., 102, 108, 111, 117, 121]
fi = [120., 113, 108, 105, 102, 102]
puntos.append([xi,fi])

# cabeza 01
xi = [194., 196, 203, 210, 211, 209]
fi = [177., 167, 157, 149, 138, 135]
puntos.append([xi,fi])

# cabeza 02
xi = [195., 199, 207, 214, 219, 224, 229, 234, 239, 242, 244]
fi = [177., 185, 190, 190, 188, 193, 192, 185, 175, 172, 168]
puntos.append([xi,fi])

# cabeza 03
xi = [220., 226, 234, 239, 241]
fi = [164., 162, 163, 163, 163]
puntos.append([xi,fi])

# cabeza 04
xi = [203., 211, 214, 219, 223, 224, 225, 230, 236, 241]
fi = [115., 119, 125, 124, 127, 137, 146, 149, 154, 162]
puntos.append([xi,fi])

# cabeza 05
xi = [208., 212, 215, 219, 221, 225, 228]
fi = [174., 177, 178, 179, 182, 183, 178]
puntos.append([xi,fi])

# cabeza 06
xi = [206., 210, 214, 218, 220, 224, 229, 233, 232]
fi = [179., 182, 182, 181, 174, 171, 170, 175, 180]
puntos.append([xi,fi])

4.8 Interpolar – Mascota descansando

Referencia: Burden 9th Ed. Ejercicio 32 p164

La parte superior de una mascota descansando se describe con los puntos presentados en la tabla.  Los puntos se usan para ejercicios de interpolación.

xi = [1,2,5,6,7,8,10,13,17,20,23,24,25,27,27.7,28,29,30]
fi = [3.0,3.7,3.9,4.2,5.7,6.6,7.1,6.7,4.5,7.0,6.1,5.6,5.8,5.2,4.1,4.3,4.1,3.0]

4.7 Interpolar – Pato en pleno vuelo

Referencia: Burden Cap 3. Ejemplo 1.

La figura muestra un joven pato en pleno vuelo. Para aproximar el perfil de la parte superior del pato, se presentan 21 puntos a lo largo de la curva de aproximación relativos a un sistema de coordenadas sobrepuestas.

xi = [0.9, 1.3, 1.9, 2.1, 2.6, 3.0, 3.9, 4.4, 4.7, 5, 6.0, 7.0, 8.0, 9.2, 10.5, 11.3, 11.6, 12.0, 12.6, 13.0, 13.3]
fi = [1.3, 1.5, 1.85, 2.1, 2.6, 2.7, 2.4, 2.15, 2.05, 2.1, 2.25, 2.3, 2.25, 1.95, 1.4, 0.9, 0.7, 0.6, 0.5, 0.4, 0.25]


a) Realice la interpolación entre puntos usando la interpolación de Lagrange para la parte superior del pato en vuelo.

b) ¿Es posible crear un solo polinomio para los 21 puntos presentados? Explique su respuesta en pocas palabras.

c) ¿En concepto, cuando considera necesario crear un nuevo polinomio?

d) Adjunte en los archivos para: la gráfica que interpola los puntos, y los polinomios que la generan.

e) (Puntos extra: 10, para promediar con lección o taller)

Se adjuntan los demás puntos del perfil del pato en pleno vuelo, presente el o los polinomios que interpolan la figura (gráfica y polinomios), las despuestas a literales b,c,d deben también ser válidas para el ejercicio completo.

# Perfil Superior del pato
xiA = [0.9, 1.3, 1.9, 2.1, 2.6, 3.0, 3.9, 4.4, 4.7, 5, 6.0, 7.0, 8.0, 9.2, 10.5, 11.3, 11.6, 12.0, 12.6, 13.0, 13.3]
fiA = [1.3, 1.5, 1.85, 2.1, 2.6, 2.7, 2.4, 2.15, 2.05, 2.1, 2.25, 2.3, 2.25, 1.95, 1.4, 0.9, 0.7, 0.6, 0.5, 0.4, 0.25]

# Perfil inferior cabeza
xiB = [0.817, 0.897, 1.022, 1.191, 1.510, 1.834, 2.264, 2.962, 3.624, 4.202, 4.499, 4.779, 5.109, 5.527]
fiB = [1.180, 1.065, 1.023, 1.010, 1.032, 1.085, 1.192, 1.115, 1.087, 1.100, 0.830, 0.608, 0.350, 0.106]

# Perfil Ala superior
xiC = [4.659, 4.865, 5.085, 5.261, 5.387, 5.478, 5.527]
fiC = [-5.161, -4.741, -3.933, -2.951, -1.970, -0.981, 0.106]

# Perfil Ala inferior
xiD = [4.659, 4.750, 4.990, 5.289, 5.560, 5.839, 6.113, 6.606, 6.916, 7.305, 7.563, 7.802, 7.983]
fiD = [-5.161, -5.259, -5.284, -5.268, -5.161, -4.982, -4.769, -4.286, -3.911, -3.213, -2.670, -2.176, -1.655]

# Perfil inferior posterior
xiE = [8.141, 8.473, 8.832, 9.337, 9.887, 10.572, 10.995, 11.501, 11.923, 12.364, 12.763, 13.300]
fiE = [-1.138, -0.434, -0.514, -0.494, -0.382, -0.005, -0.090, -0.085, -0.030, 0.093, 0.120, 0.250]

# Perfil ojo superior
xiF = [2.663, 2.700, 2.805, 2.886]
fiF = [2.202, 2.279, 2.293, 2.222]

# Perfil ojo inferior
xiG = [2.663, 2.720, 2.826, 2.886]
fiG = [2.202, 2.130, 2.143, 2.222]

4.6 Métodos de interpolación con gráficos animados en Python

[ Dif_Finitas ] [ Dif_Finitas avanzadas ] [ Dif_Divididas_Newton ]

Solo para fines didácticos, y como complemento para los ejercicios presentados en la unidad para Interpolación polinómica, se presentan las instrucciones para las animaciones usadas en la presentación de los conceptos y ejercicios. Los algoritmos para animación NO son necesarios para realizar los ejercicios, que requieren una parte analítica con al menos tres iteraciones en papel y lápiz. Se lo adjunta como una herramienta didáctica de asistencia para las clases.

En los algoritmos, se convierten las partes en funciones, que se usan para generar los polinomios para cada grado y se incorporan en un gráfico animado con los resultados presentados.

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

Se determina el intervalo para el eje x usando los valores mínimos y máximos del vector xi. Se toma como muestras para el polinomio las suficientes para una línea suave, que pueden ser mayores a 21 y se crean los valores para el polinomio en p_xi.

Para cada polinomio se guarda en una tabla los valores px(p_xi)del polinomio evaluado el vector p_xi

La gráfica (graf_ani) se crea en una ventana (fig_ani), inicializando con los puntos [xi,fi] en color rojo, y configurando los parámetros base para el gráfico.

Se usan procedimientos para crear unatrama() para cada polinomio y en cada cambio se limpia la trama manteniendo la base con limpiatrama().

En caso de requerir un archivo gif animado se proporciona un nombre de archivo. Para crear el archivo se requiere de un complemento ‘imagemagick‘ a ser instalado.

otros ejemplos de animación en el curso de Fundamentos de Programación:

Movimiento circular – Una partícula, animación con matplotlib-Python


Interpolación por Diferencias Finitas Avanzadas

Diferencias Finitas Avanzadas GIFanimado

En la función para interpolación se añade la verificación de tamaños de paso equidistantes o iguales. En el caso de no tener tamaños de paso equidistantes

tabla de diferencias finitas
['i', 'xi', 'fi', 'df1', 'df2']
[[0.   0.1  1.45 0.15 0.  ]
 [1.   0.2  1.6  0.   0.  ]]
dfinita:  [0.15 0.  ]
1.45 +
+( 0.15 / 0.1 )* ( x - 0.1 )
polinomio simplificado
1.5*x + 1.3

tabla de diferencias finitas
['i', 'xi', 'fi', 'df1', 'df2', 'df3']
[[ 0.    0.1   1.45  0.15 -0.05  0.  ]
 [ 1.    0.2   1.6   0.1   0.    0.  ]
 [ 2.    0.3   1.7   0.    0.    0.  ]]
dfinita:  [ 0.15 -0.05  0.  ]
1.45 +
+( 0.15 / 0.1 )* ( x - 0.1 )
+( -0.05 / 0.02 )*  (x - 0.2)*(x - 0.1) 
polinomio simplificado
-2.5*x**2 + 2.25*x + 1.25

tabla de diferencias finitas
['i', 'xi', 'fi', 'df1', 'df2', 'df3', 'df4']
[[ 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.  ]
1.45 +
+( 0.15 / 0.1 )* ( x - 0.1 )
+( -0.05 / 0.02 )*  (x - 0.2)*(x - 0.1) 
+( 0.25 / 0.006 )*  (x - 0.3)*(x - 0.2)*(x - 0.1) 
polinomio simplificado
41.66667*x**3 - 27.500002*x**2 + 6.8333337*x + 0.99999998

Polinomios con Diferencias Finitas Avanzadas
px_0 =
1.45

px_1 =
1.5*x + 1.3

px_2 =
       2                
- 2.5*x  + 2.25*x + 1.25

px_3 =
          3              2                           
41.66667*x  - 27.500002*x  + 6.8333337*x + 0.99999998

El resumen de las instrucciones se presentan a continuación.

# Interpolación -Diferencias finitas avanzadas
# Tarea: Verificar tamaño de vectores,
#        verificar puntos equidistantes en x
import numpy as np
import math
import sympy as sym

def dif_finitas(xi,fi, precision=5, vertabla=False):
    '''Genera la tabla de diferencias finitas
    resultado en: tabla,titulo
    Tarea: verificar tamaño de vectores
    '''
    xi = np.array(xi,dtype=float)
    fi = np.array(fi,dtype=float)
    # Tabla de Diferencias Finitas
    titulo = ['i','xi','fi']
    n = len(xi)
    ki = np.arange(0,n,1)
    tabla = np.concatenate(([ki],[xi],[fi]),axis=0)
    tabla = np.transpose(tabla)
    # diferencias finitas vacia
    dfinita = np.zeros(shape=(n,n),dtype=float)
    tabla = np.concatenate((tabla,dfinita), axis=1)
    # Calcula tabla, inicia en columna 3
    [n,m] = np.shape(tabla)
    diagonal = n-1
    j = 3
    while (j < m):
        # Añade título para cada columna
        titulo.append('df'+str(j-2))
        # cada fila de columna
        i = 0
        while (i < diagonal):
            tabla[i,j] = tabla[i+1,j-1]-tabla[i,j-1]
            i = i+1
        diagonal = diagonal - 1
        j = j+1
    if vertabla==True:
        np.set_printoptions(precision)
        print('tabla de diferencias finitas')
        print(titulo)
        print(tabla)
    return(tabla, titulo)

def pasosEquidistantes(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.
    '''
    xi = np.array(xi,dtype=float)
    n = len(xi)
    # revisa tamaños de paso equidistantes
    h_iguales = True
    if n>3: 
        dx = np.zeros(n,dtype=float)
        for i in range(0,n-1,1): # calcula hi como dx
            dx[i] = xi[i+1]-xi[i]
        for i in range(0,n-2,1): # revisa diferencias
            dx[i] = dx[i+1]-dx[i]
            if dx[i]<=casicero: # redondea cero
                dx[i]=0
            if abs(dx[i])>0:
                h_iguales=False
                print('tamaños de paso diferentes en i:',i+1,',',i+2)
        dx[n-2]=0
    return(h_iguales)

def interpola_dfinitasAvz(xi,fi, vertabla=False,
                          precision=5, casicero = 1e-15):
    '''Interpolación de diferencias finitas
    resultado: polinomio en forma simbólica,
    redondear a cero si es menor que casicero 
    '''
    xi = np.array(xi,dtype=float)
    fi = np.array(fi,dtype=float)
    n = len(xi)
    # revisa tamaños de paso equidistantes
    h_iguales = pasosEquidistantes(xi, casicero)
    if vertabla==True:
        np.set_printoptions(precision)
    # POLINOMIO con diferencias Finitas avanzadas
    x = sym.Symbol('x')
    polisimple = sym.S.NaN # expresión del polinomio con Sympy
    if h_iguales==True:
        tabla,titulo = dif_finitas(xi,fi,vertabla)
        h = xi[1] - xi[0]
        dfinita = tabla[0,3:]
        if vertabla==True:
            print('dfinita: ',dfinita)
            print(fi[0],'+')
        n = len(dfinita)
        polinomio = fi[0]
        for j in range(1,n,1):
            denominador = math.factorial(j)*(h**j)
            factor = np.around(dfinita[j-1]/denominador,precision)
            termino = 1
            for k in range(0,j,1):
                termino = termino*(x-xi[k])
            if vertabla==True:
                txt1='';txt2=''
                if n<=2 or j<=1:
                    txt1 = '('; txt2 = ')'
                print('+(',np.around(dfinita[j-1],precision),
                      '/',np.around(denominador,precision),
                      ')*',txt1,termino,txt2)
            polinomio = polinomio + termino*factor
        # simplifica multiplicando entre (x-xi)
        polisimple = polinomio.expand() 
    if vertabla==True:
        print('polinomio simplificado')
        print(polisimple)
    return(polisimple)

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

# PROCEDIMIENTO
# tabla polinomios
n = len(xi)
px_tabla = [fi[0]]
for grado in range(1,n,1):
    polinomio = interpola_dfinitasAvz(xi[:grado+1],fi[:grado+1],
                                      vertabla=True, precision=4)
    print('',)
    px_tabla.append(polinomio)

# SALIDA
print('Polinomios con Diferencias Finitas Avanzadas')
for grado in range(0,n,1):
    print('px_'+str(grado)+' =') #, px_tabla[grado])
    sym.pprint(px_tabla[grado])
    print()


Parte adicional para la gráfica con GIF animado

# GRAFICA CON ANIMACION polinomios --------
import matplotlib.pyplot as plt
import matplotlib.animation as animation

unmetodo = 'Diferencias Finitas Avanzadas'
narchivo = 'DifFinitasAvz' # nombre archivo
muestras = 51 # de cada p(X)

# Puntos para la gráfica
a = np.min(xi)
b = np.max(xi)
p_xi = np.linspace(a,b,muestras)

# lineas por grado de polinomio
x = sym.Symbol('x')
px_lineas = np.zeros(shape=(n,muestras), dtype=float)
for grado in range(0,n,1):
    polinomio = px_tabla[grado]
    px = sym.utilities.lambdify(x,polinomio,'numpy')
    px_lineas[grado] = px(p_xi)

# Parametros de trama/foto
retardo = 800   # milisegundos entre tramas
tramas = len(px_lineas)
ymax = np.max(fi)
ymin = np.min(fi)
deltax = 0.1*np.abs(b-a)
deltay = 0.1*np.abs(ymax-ymin)

# GRAFICA animada en fig_ani
fig_ani, graf_ani = plt.subplots()
# Función Base
fx_linea, = graf_ani.plot(xi,fi,'o',color='red')
# Polinomios de px_tabla grado = 0
px_unalinea, = graf_ani.plot(p_xi, px_lineas[0],
                         label='grado: 0')
# Configura gráfica
graf_ani.set_xlim([a-deltax,b+deltax])
graf_ani.set_ylim([ymin-deltay,ymax+deltay])
graf_ani.axhline(0, color='k')  # Linea horizontal en cero
graf_ani.set_title('Polinomio - '+unmetodo)
graf_ani.set_xlabel('x')
graf_ani.set_ylabel('p(x)')
graf_ani.grid()

# Cuadros de texto en gráfico
txt_x = (b+a)/2
txt_y = ymax
txt_poli = graf_ani.text(txt_x, txt_y,'p(x):',
                      horizontalalignment='center')
txt_grado = graf_ani.text(txt_x, txt_y-deltay,'grado:',
                        horizontalalignment='center')
# Nueva Trama
def unatrama(i,p_xi,pxi): 
    # actualiza cada linea
    px_unalinea.set_xdata(p_xi)
    px_unalinea.set_ydata(pxi[i])
    unpolinomio = px_tabla[i]
    if unpolinomio == sym.S.NaN:
        unpolinomio = 'h pasos no equidistantes' 
    etiquetap = 'p'+str(i)+'(x) = '+str(unpolinomio)
    px_unalinea.set_label(etiquetap)
    # actualiza texto
    txt_poli.set_text(etiquetap)
    txt_grado.set_text('Grado: '+str(i))
    # color de la línea
    if (i<=9):
        lineacolor = 'C'+str(i)
    else:
        numerocolor = i%10
        lineacolor = 'C'+str(numerocolor)
    px_unalinea.set_color(lineacolor)
    
    return (px_unalinea, txt_poli, txt_grado)
# Limpia Trama anterior
def limpiatrama(): 
    px_unalinea.set_ydata(np.ma.array(p_xi, mask=True))
    px_unalinea.set_label('')
    txt_poli.set_text('')
    txt_grado.set_text('')
    return (px_unalinea,txt_poli, txt_grado)

# Trama contador
i = np.arange(0,tramas,1)
ani = animation.FuncAnimation(fig_ani,
                              unatrama,
                              i ,
                              fargs = (p_xi,px_lineas),
                              init_func = limpiatrama,
                              interval = retardo,
                              blit=True)
# Graba Archivo GIFAnimado y video
ani.save(narchivo+'_GIFanimado.gif', writer='imagemagick')
# ani.save(narchivo+'_video.mp4')
plt.draw()
plt.show()

[ Dif_Finitas ] [ Dif_Finitas avanzadas ] [ Dif_Divididas_Newton ]


Interpolación por Diferencias Divididas de Newton

Diferencias Divididas Newton GIF animado

tabla de diferencias divididas
['i', 'xi', 'fi', 'F[1]', 'F[2]']
[[0.   0.1  1.45 1.5  0.  ]
 [1.   0.2  1.6  0.   0.  ]]
difDividida:  [1.5 0. ]
1.45 +
+( 1.5 )*( x - 0.1 )
polinomio simplificado
1.5*x + 1.3

tabla de diferencias divididas
['i', 'xi', 'fi', 'F[1]', 'F[2]', 'F[3]']
[[ 0.    0.1   1.45  1.5  -2.5   0.  ]
 [ 1.    0.2   1.6   1.    0.    0.  ]
 [ 2.    0.3   1.7   0.    0.    0.  ]]
difDividida:  [ 1.5 -2.5  0. ]
1.45 +
+( 1.5 )*( x - 0.1 )
+( -2.5 )* (x - 0.2)*(x - 0.1) 
polinomio simplificado
-2.5*x**2 + 2.25*x + 1.25

tabla de diferencias divididas
['i', 'xi', 'fi', 'F[1]', 'F[2]', 'F[3]', 'F[4]']
[[ 0.00000e+00  1.00000e-01  1.45000e+00  1.50000e+00 -2.50000e+00
   5.00000e+00  0.00000e+00]
 [ 1.00000e+00  2.00000e-01  1.60000e+00  1.00000e+00  3.33067e-15
   0.00000e+00  0.00000e+00]
 [ 2.00000e+00  3.00000e-01  1.70000e+00  1.00000e+00  0.00000e+00
   0.00000e+00  0.00000e+00]
 [ 3.00000e+00  6.00000e-01  2.00000e+00  0.00000e+00  0.00000e+00
   0.00000e+00  0.00000e+00]]
difDividida:  [ 1.5 -2.5  5.   0. ]
1.45 +
+( 1.5 )*( x - 0.1 )
+( -2.5 )* (x - 0.2)*(x - 0.1) 
+( 5.0 )* (x - 0.3)*(x - 0.2)*(x - 0.1) 
polinomio simplificado
5.0*x**3 - 5.5*x**2 + 2.8*x + 1.22

Polinomios con Diferencias Divididas Newton
px_0 =
1.45

px_1 =
1.5*x + 1.3

px_2 =
       2                
- 2.5*x  + 2.25*x + 1.25

px_3 =
     3        2               
5.0*x  - 5.5*x  + 2.8*x + 1.22
>>> 

Instrucciones en Python

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

def dif_divididas(xi,fi, vertabla=False,
                  precision=5, casicero = 1e-15):
    '''Genera la tabla de diferencias divididas
    resultado en: tabla, titulo
    Tarea: verificar tamaño de vectores
    '''
    xi = np.array(xi,dtype=float)
    fi = np.array(fi,dtype=float)
    # Tabla de Diferencias Divididas
    titulo = ['i','xi','fi']
    n = len(xi)
    ki = np.arange(0,n,1)
    tabla = np.concatenate(([ki],[xi],[fi]),axis=0)
    tabla = np.transpose(tabla)
    # diferencias divididas vacia
    dfinita = np.zeros(shape=(n,n),dtype=float)
    tabla = np.concatenate((tabla,dfinita), axis=1)
    # Calcula tabla, inicia en columna 3
    [n,m] = np.shape(tabla)
    diagonal = n-1
    j = 3
    while (j < m):
        # Añade título para cada columna
        titulo.append('F['+str(j-2)+']')

        # cada fila de columna
        i = 0
        paso = j-2 # inicia en 1
        while (i < diagonal):
            denominador = (xi[i+paso]-xi[i])
            numerador = tabla[i+1,j-1]-tabla[i,j-1]
            tabla[i,j] = numerador/denominador
            if np.abs(tabla[i,j])<= casicero:
                tabla[i,j] = 0
            i = i+1
        diagonal = diagonal - 1
        j = j+1

    if vertabla==True:
        np.set_printoptions(precision)
        print('tabla de diferencias divididas')
        print(titulo)
        print(tabla)
    return(tabla, titulo)

def interpola_difDividida(xi,fi, vertabla=False,
                          precision=5, casicero = 1e-15):
    '''Interpolación de diferencias finitas
    resultado: polinomio en forma simbólica,
    redondear a cero si es menor que casicero 
    '''
    xi = np.array(xi,dtype=float)
    fi = np.array(fi,dtype=float)

    # Tabla de Diferencias Divididas
    tabla,titulo = dif_divididas(xi,fi,vertabla,
                                 precision,casicero)
    dDividida = tabla[0,3:]
    n = len(dDividida)
    x = sym.Symbol('x')
    polinomio = fi[0]
    if vertabla==True:
        print('difDividida: ',dDividida)
        print(fi[0],'+')
    for j in range(1,n,1):
        factor = np.around(dDividida[j-1],precision)
        termino = 1
        for k in range(0,j,1):
            termino = termino*(x-xi[k])
        if vertabla==True:
            txt1='';txt2=''
            if n<=2 or j<=1:
                txt1 = '('; txt2 = ')'
            print('+(',factor,')*'+txt1,termino,txt2)
        polinomio = polinomio + termino*factor
    
    # simplifica multiplicando entre (x-xi)
    polisimple = polinomio.expand() 
    if vertabla==True:
        print('polinomio simplificado')
        print(polisimple)
    return(polisimple)

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

# PROCEDIMIENTO
# tabla polinomios
n = len(xi)
px_tabla = [fi[0]]
for grado in range(1,n,1):
    polinomio = interpola_difDividida(xi[:grado+1],fi[:grado+1],
                                          vertabla=True)
    print('',)
    px_tabla.append(polinomio)

# SALIDA
print('Polinomios con Diferencias Divididas Newton')
for grado in range(0,n,1):
    print('px_'+str(grado)+' =') #, px_tabla[grado])
    sym.pprint(px_tabla[grado])
    print()

Para la gráfica animada se usa el mismo bloque de instrucciones del método de Diferencias Finitas avanzadas, solo requiere cambiar el nombre del método y el nombre para el archivo GIF animado.


Interpolación por el Método de Lagrange

Interpola con Lagrange
 1.45 * (x - 0.4)*(x - 0.3)*(x - 0.2) / (-0.2 + 0.1)*(-0.3 + 0.1)*(-0.4 + 0.1) 
+ 1.6 * (x - 0.4)*(x - 0.3)*(x - 0.1) / (-0.1 + 0.2)*(-0.3 + 0.2)*(-0.4 + 0.2) 
+ 1.7 * (x - 0.4)*(x - 0.2)*(x - 0.1) / (-0.1 + 0.3)*(-0.2 + 0.3)*(-0.4 + 0.3) 
+ 2.0 * (x - 0.3)*(x - 0.2)*(x - 0.1) / (-0.1 + 0.4)*(-0.2 + 0.4)*(-0.3 + 0.4) 
polinomio simplificado
41.666667*x**3 - 27.5*x**2 + 6.833333*x + 1.0
Interpolación con Lagrange
41.666667*x**3 - 27.5*x**2 + 6.833333*x + 1.0
>>> 

Instrucciones en Python

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

def interpola_Lagrange(xi,fi,vertabla=False,
                       precision=6, casicero = 1e-15):
    '''
    Interpolación con método de Lagrange
    resultado: polinomio en forma simbólica
    '''
    xi = np.array(xi,dtype=float)
    fi = np.array(fi,dtype=float)
    n = len(xi)
    x = sym.Symbol('x')
    # Polinomio con Lagrange
    if vertabla==True:
        print('Interpola con Lagrange')
    polinomio = sym.S.Zero
    for i in range(0,n,1):
        # Termino de Lagrange
        termino = 1
        numerador = 1
        denominador = 1
        for j  in range(0,n,1):
            if (j!=i):
                numerador = numerador*(x-xi[j])
                denominador = denominador*(sym.UnevaluatedExpr(xi[i])-xi[j])
        if vertabla==True:
            txt0='' ; txt1='('; txt2=')'
            if i>0:
                txt0='+'
            if n>2:
                txt1=''; txt2=''
            print(txt0,fi[i],'*'+txt1,numerador,txt2+'/'+ txt1,
                  denominador,txt2)
        #factor = np.around(fi[i]/float(denominador.doit()),precision) 
        polinomio = polinomio + (fi[i]/denominador.doit())*numerador
    # Expande el polinomio
    polisimple = polinomio.expand()
    polisimple = redondea_coef(polisimple, precision)
    if vertabla==True:
        print('polinomio simplificado')
        print(polisimple)
    return(polisimple)

def redondea_coef(ecuacion, precision=6,casicero = 1e-15):
    ''' redondea coeficientes de términos suma de una ecuacion
    '''
    tipo = type(ecuacion)
    tipo_eq = False
    if tipo == sym.core.relational.Equality:
        RHS = ecuacion.rhs
        ecuacion = ecuacion.lhs
        tipo = type(ecuacion)
        tipo_eq = True

    if tipo == sym.core.add.Add: # términos suma de ecuacion
        term_sum = sym.Add.make_args(ecuacion)
        ecuacion = sym.S.Zero
        for term_k in term_sum:
            # factor multiplicativo de termino suma
            term_mul = sym.Mul.make_args(term_k)
            producto = sym.S.One
            for factor in term_mul:
                if not(factor.has(sym.Symbol)):
                    factor = np.around(float(factor),precision)
                    if (abs(factor)%1)<casicero: # si es entero
                        factor = int(factor)
                producto = producto*factor
            ecuacion = ecuacion + producto
    if tipo == sym.core.mul.Mul: # termino único, busca factores
        term_mul = sym.Mul.make_args(ecuacion)
        producto = sym.S.One
        for factor in term_mul:
            if not(factor.has(sym.Symbol)):
                factor = np.around(float(factor),precision)
                print(factor)
                if (abs(factor)%1)<casicero: # si es entero
                    factor = int(factor)
            producto = producto*factor
        ecuacion = producto
    if tipo == float: # si es entero
        if (abs(ecuacion)%1)<casicero: 
            ecuacion = int(ecuacion)
    if tipo_eq:
        ecuacion = sym.Eq(ecuacion,RHS)
    return(ecuacion)

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

# PROCEDIMIENTO
# tabla polinomios
polisimple = interpola_Lagrange(xi,fi,
                                   vertabla=True)

# SALIDA
print('Interpolación con Lagrange')
print(polisimple)

Para la gráfica animada se usa el mismo bloque de instrucciones del método de Diferencias Finitas avanzadas, solo requiere cambiar el nombre del método y el nombre para el archivo GIF animado.