5.2 Regla de Simpson 1/3 para Integración numérica con Python

[ Simpson 1/3 ] [ Ejercicio ] [ Algoritmo f(x) ] [ gráfica ] [ Algoritmo xi,fi ]

..


1. 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)]

integra regla Simpson 1/3 animado

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}{tramos}

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 f(x) ] [ gráfica ] [ Algoritmo xi,fi ]

..


2. 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 f(x) ] [ gráfica ] [ Algoritmo xi,fi ]
..


3. Algoritmo para integral f(x) 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: 2
Integral fx con Simpson1/3:  2.0765536739078203
tramos: 4
Integral fx con Simpson1/3:  2.0549261957703937
tramos: 8
Integral fx con Simpson1/3:  2.053709383061734
tramos: 16
Integral fx con Simpson1/3:  2.053635013281097

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.
integral regla Simpson 1/3 tramos 4Instrucciones en Python para f(x)

# Regla Simpson 1/3 para f(x) entre [a,b],tramos
import numpy as np

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

a = 1 # intervalo de integración
b = 3
tramos = 2 # par, múltiplo de 2

# validar: tramos debe múltiplo de 2
while tramos%2 > 0: 
    print('tramos: ',tramos)
    tramos = int(input('tramos debe ser par: '))

# PROCEDIMIENTO
muestras = tramos + 1
xi = np.linspace(a,b,muestras)
fi = fx(xi)

# Regla de Simpson 1/3
h = (b-a)/tramos
suma = 0 # integral numérico
for i in range(0,tramos,2):
    S13= (h/3)*(fi[i]+4*fi[i+1]+fi[i+2])
    suma = suma + S13

# SALIDA
print('tramos:', tramos)
print('Integral fx con Simpson1/3: ', suma)

[ Simpson 1/3 ] [ Ejercicio ] [ Algoritmo f(x) ] [ gráfica ] [ Algoritmo xi,fi ]
..


4. Gráfica para integral f(x) con Simpson 1/3

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=4.

Instrucciones en Python adicionales al algoritmo anterior

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

titulo = 'Regla de Simpson 1/3'
titulo = titulo + ', tramos:'+str(tramos)
titulo = titulo + ', Area:'+str(suma)

fx_existe = True
try: 
    # fx suave aumentando muestras
    muestrasfxSuave = tramos*10 + 1
    xk = np.linspace(a,b,muestrasfxSuave)
    fk = fx(xk)
except NameError:
    # falta variables a,b,muestras y la función fx
    fx_existe = False

try: # existen mensajes de error
    msj_existe = len(msj)
except NameError:
    # falta variables mensaje: msj
    msj = []  

# Simpson 1/3 relleno y bordes, cada 2 tramos
for i in range(0,muestras-1,2): 
    x_tramo = xi[i:(i+2)+1]
    f_tramo = fi[i:(i+2)+1]

    # interpolación polinomica a*(x**2)+b*x+c
    coef = np.polyfit(x_tramo, f_tramo, 2) # [a,b,c]
    px = lambda x: coef[0]*(x**2)+coef[1]*x+coef[2]

    xp = np.linspace(x_tramo[0],x_tramo[-1],21)
    fp = px(xp)
    
    plt.plot(xp,fp,linestyle='dashed',color='orange')
    
    relleno = 'lightgreen'
    if (i/2)%2==0: # bloque 2 tramos, es par
        relleno ='lightblue'
    if len(msj)==0: # sin errores
        plt.fill_between(xp,fp,fp*0,color=relleno)

# Divisiones verticales Simpson 1/3
for i in range(0,muestras,1):
    tipolinea = 'dotted'
    if i%2==0: # i par, multiplo de 2
        tipolinea = 'dashed'
    if len(msj)==0: # sin errores
        plt.vlines(xi[i],0,fi[i],linestyle=tipolinea,
                   color='orange')

# Graficar f(x), puntos
if fx_existe==True:
    plt.plot(xk,fk,label='f(x)')
plt.plot(xi,fi,'o',color='orange',label ='muestras')

plt.xlabel('x')
plt.ylabel('f(x)')
plt.title(titulo)
plt.legend()
plt.tight_layout()
plt.show()

[ Simpson 1/3 ] [ Ejercicio ] [ Algoritmo f(x) ] [ gráfica ] [ Algoritmo xi,fi ]
..


5. Algoritmo para x[i], f[i] como muestras en Python

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.

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.

La gráfica se puede obtener usando el bloque correspondiente en la sección anterior.

# Integración Simpson 1/3 para muestras xi,fi
import numpy as np

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

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

msj = [] # mensajes de error 
if tramos<2: # puntos insuficientes
    msj.append(['tramos insuficientes:',tramos])

suma = 0 # integral numerico
i = 0
while i<(muestras-2) and len(msj)==0: # i<tramos, al menos dos tramos
    h = xi[i+1]-xi[i] # tamaño de paso, supone constante
    if (i+2)<muestras: # dos tramos
        d2x = np.diff(xi[i:i+3],2) # diferencias entre tramos
        errado = np.max(np.abs(d2x))
        if errado<casicero: # Simpson 1/3
            S13 = (h/3)*(fi[i]+4*fi[i+1]+fi[i+2])
            suma = suma + S13
            i = i+2
        else:
            donde = np.argmax(np.abs(d2x))+i+1
            msj.append(['no equidistantes en i',donde])

# SALIDA
print('tramos: ',tramos)
if len(msj)>0: # mensajes de error
    print('Revisar errores en xi:',xi)
    print('tramos:',np.diff(xi,1))
    for unmsj in msj:
        print('',unmsj[0],':',unmsj[1])
else:
    print('Integral fx con Simpson1/3:',suma)

resultados

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

[ Simpson 1/3 ] [ Ejercicio ] [ Algoritmo f(x) ] [ gráfica ] [ Algoritmo xi,fi ]
..


6. 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 f(x) ] [ Algoritmo xi,fi  ]
[ Integral Compuesto xi,fi ]

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

[ Regla trapecio ] [ Ejercicio ] [Algoritmo f(x) ] [ gráfica ] [ Algoritmo xi,fi ]
..


1. Regla del Trapecio

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

I \cong \frac{h}{2}(f(x_0)+f(x_1))

regla de trapecio integral numérico
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 del intervalo [a,b].

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 intervalo 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 para el intervalo [a,b]:

I \cong (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.

Para disminuir el error, para un intervalo mas grande [a,b] se divide el intervalo en varios tramos de tamaño h. El integral de un tramo se define como:

h = \frac{b-a}{tramos} I \cong \frac{h}{2}(f(x_i)+f(x_i+h)) I \cong \frac{h}{2}(f(x_i)+f(x_{i+1}))

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 trapecio ] [ Ejercicio ] [Algoritmo f(x) ] [ gráfica ] [ Algoritmo xi,fi ]
..


2. Ejercicio 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 del trapecio integración numérica

Tramos = 4

El intervalo [1,3] se divide en tramos con tamaño de paso h

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

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

A_1 = \frac{0.5}{2} (f(1)+f(1+0.5))= 0.5157 A_2 = \frac{0.5}{2} (f(1.5)+f(1.5+0.5))= 0.6269 A_3 = \frac{0.5}{2} (f(2)+f(2+0.5))= 0.5580 A_4 = \frac{0.5}{2} (f(2.5)+f(2.5+0.5))= 0.2976

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

Integral = A_1+ A_2+ A_3+A_4 = 1.9984

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

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

[ Regla trapecio ] [ Ejercicio ] [Algoritmo f(x) ] [ gráfica ] [ Algoritmo xi,fi ]
..


3. Algoritmo para integral f(x) en Python

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

# Regla de los trapecios: f(x) entre [a,b],tramos
import numpy as np

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

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

# PROCEDIMIENTO
muestras = tramos + 1
xi = np.linspace(a,b,muestras)
fi = fx(xi)

# Regla del Trapecio
suma = 0 # integral numérico
i = 0
while (i+1)<muestras:
    h = xi[i+1]-xi[i] # incluye tramos desiguales
    trapecio = (h/2)*(fi[i]+fi[i+1])
    suma = suma + trapecio
    i = i+1

# SALIDA
print('tramos: ', tramos)
print('Integral fx con trapecio: ', suma)

[ Regla trapecio ] [ Ejercicio ] [Algoritmo f(x) ] [ gráfica ] [ Algoritmo xi,fi ]
..


4. Gráfica de puntos para integrar con trapecio

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.

La sección de 'fx suave aumentando muestras' se realiza solo si se ha definido la función fx, que permite comparar en la gráfica las diferencias entre fx y los trapecios. Cuando solo se usan muestras, esta sección se omite dentro del algoritmo de la gráfica.

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

titulo = 'Regla de Trapecios'
titulo = titulo + ', tramos:'+str(tramos)
titulo = titulo + ', Area:'+str(suma)

try:
    # fx suave aumentando muestras
    muestrasfxSuave = tramos*10 + 1
    xk = np.linspace(a,b,muestrasfxSuave)
    fk = fx(xk)
except NameError:
    # falta variables a,b,muestras y la función fx
    fk = fi # existen solo muestras
    xk = xi

# Trapecios relleno y bordes, cada 1 tramo
for i in range(0,muestras,1):
    x_tramo = xi[i:(i+1)+1]
    f_tramo = fi[i:(i+1)+1]
    
    relleno = 'lightgreen'
    if (i%2)==0: # i múltiplo 2, o par
        relleno ='lightblue'
    plt.fill_between(x_tramo,f_tramo,f_tramo*0,
                     color=relleno)
    # Divisiones entre tramos
    plt.vlines(xi[i],0,fi[i],linestyle='dotted',
               color='orange')

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

plt.xlabel('x')
plt.ylabel('f(x)')
plt.title(titulo)
plt.legend()
plt.tight_layout()

plt.show()

Para rellenar el área bajo la curva con base en eje x, se usa la instrucción plt.fill_between(x_tramo,f_tramo,f_tramo*0,color='green').
La división de los trapecios con una línea naranja (orange) se realiza con la instrucción plt.vlines(xi[i],0,fi[i],linestyle='dotted',color='orange').

[ Regla trapecio ] [ Ejercicio ] [Algoritmo f(x) ] [ gráfica ] [ Algoritmo xi,fi ]
..


5. Algoritmo para x[i], f[i] como muestras en Python

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.

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

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 .  La gráfica se realiza con las instrucciones de la sección anterior.

# Regla de los trapecios para muestras xi,fi
import numpy as np

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

# PROCEDIMIENTO
# vectores como arreglo, números reales
xi = np.array(xi,dtype=float)
fi = np.array(fi,dtype=float)
muestras = len(xi)
tramos = muestras-1

# Regla del Trapecio
suma = 0 # integral numerico
i = 0
while (i+1)<muestras:
    dx = xi[i+1]-xi[i] # incluye tramos desiguales
    trapecio = (dx/2)*(fi[i]+fi[i+1])
    suma = suma + trapecio
    i = i+1

# SALIDA
print('tramos: ',tramos)
print('Integral fx con trapecio: ',suma)

[ Regla trapecio ] [ Ejercicio ] [Algoritmo f(x) ] [ gráfica ] [ Algoritmo xi,fi ]

4.8.3 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

Mascota 50 anios 01

# 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.2 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]

mascota descansando, interpolación

4.8.1 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]

pato en pleno vuelo, interpolar
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]

pato en pleno vuelo, interpolar con python

4.7 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 la librería 'pillow'.

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', 'd1f', 'd2f']
[[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', 'd1f', 'd2f', 'd3f']
[[ 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', '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.  ]
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('d'+str(j-2)+'f')
        # 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='pillow')
# 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.


 

4.6 Interpolación paramétrica con Python

Referencia: Rodriguez 6.9.2 p236, Burden 9Ed 3.6 p164

En algunos casos, los datos (x,y) no tienen una relación de tipo funcional y(x), entonces no se pueden aplicar directamente los métodos de interpolación revisados.

Por ejemplo, en la trayectoria del balón en el "gol imposible", la gráfica de la trayectoria en el espacio o sus proyecciones en los planos dependen del parámetro tiempo "t"en lugar de una relación de x,y,z

Referencia: 1Eva_IT2018_T4 El gol imposible

Tabla de datos:

ti = [0.00, 0.15, 0.30, 0.45, 0.60, 0.75, 0.90, 1.05, 1.20]
xi = [0.00, 0.50, 1.00, 1.50, 1.80, 2.00, 1.90, 1.10, 0.30]
yi = [0.00, 4.44, 8.88,13.31,17.75,22.19,26.63,31.06,35.50]
zi = [0.00, 0.81, 1.40, 1.77, 1.91, 1.84, 1.55, 1.03, 0.30]

Sin embargo si las coordenadas (x,y) se expresan como funciones de otra variable t denominada parámetro, entonces los puntos x(t), y(t) tienen relación funcional, y se pueden construir polinomios de interpolación.

Solución propuesta: s1Eva_IT2018_T4 El gol imposible


Ejemplo

Las coordenadas x(t) y y(t) del recorrido de un cohete registradas en los instantes t fueron:

ti  = [0,1,2,3]
xti = [2,1,3,4]
yti = [0,4,5,0]

Usaremos un algoritmo en Python para mostrar la trayectoria x,y para el problema planteado.

Al realizar la interpolación de los puntos para obtener polinomios que dependen de "t" se obtiene:

px = lambda t: (-2/3)*(t**3) + (7/2)*(t**2) + (-23/6)*t + 2
py = lambda t: (-1/2)*(t**3) + (9/2)*t

polinomios con los que se puede realizar la gráfica px(t), py(t) en forma separada. Pero para comprender mejor la trayectoria del cohete, se utiliza la gráfica px(t) vs py(t) en el intervalo t entre[0,3]

Las intrucciones para mostrar el resultado son:

# interpolación paramétrica
import numpy as np
import matplotlib.pyplot as plt

# INGRESO
ti = [0,1,2,3]
xti = [2,1,3,4]
yti = [0,4,5,0]

# PROCEDIMIENTO
# interpolando con lagrange
px = lambda t: (-2/3)*(t**3) + (7/2)*(t**2) + (-23/6)*t + 2
py = lambda t: (-1/2)*(t**3) + (9/2)*t

t = np.arange(0,3,0.01)
puntosx = px(t)
puntosy = py(t)

# Salida
plt.plot(puntosx,puntosy)
plt.show()

4.5.1 Trazadores Cúbicos, frontera sujeta con Python

Trazador cúbico frontera sujeta [ Algoritmo ] [ gráfica ] [ función ]
..


1. Algoritmo en Python - Frontera sujeta

Para el ejercicio anterior, haciendo que la primera derivada sea cero en los extremos  y luego de realizar los ajustes al algoritmo, se tiene como resultado:

spline frontera sujeta 04

h: [0.1 0.1 0.1]
A:
[[-0.03333333 -0.01666667  0.          0.        ]
 [ 0.1         0.4         0.1         0.        ]
 [ 0.          0.1         0.4         0.1       ]
 [ 0.          0.          0.01666667  0.03333333]]
B: [ -3.5 -27.   24.   -3. ]
S: [ 178. -146.  136. -158.]
coeficientes[ ,tramo]:
a: [-540.  470. -490.]
b: [ 89. -73.  68.]
c: [-3.99680289e-15  1.60000000e+00  1.10000000e+00]
d: [1.45 1.8  1.7 ]
Trazador cúbico frontera sujeta
Polinomios por tramos: 
x = [ 0.1 , 0.2 ]
expresión: -3.99680288865056e-15*x - 540.0*(x - 0.1)**3 + 89.0000000000001*(x - 0.1)**2 + 1.45
simplifica:
         3          2                
- 540.0⋅x  + 251.0⋅x  - 34.0⋅x + 2.88
x = [ 0.2 , 0.3 ]
expresión: 1.6*x + 470.0*(x - 0.2)**3 - 73.0*(x - 0.2)**2 + 1.48
simplifica:
       3          2                                        
470.0⋅x  - 355.0⋅x  + 87.2000000000001⋅x - 5.20000000000001
x = [ 0.3 , 0.4 ]
expresión: 1.1*x - 490.0*(x - 0.3)**3 + 68.0*(x - 0.3)**2 + 1.37
simplifica:
         3          2                  
- 490.0⋅x  + 509.0⋅x  - 172.0⋅x + 20.72

Instrucciones en Python

# Trazador cubico frontera sujeta
import numpy as np
import sympy as sym

def traza3sujeto(xi,yi,d1y0,dy1n, vertabla=False):
    ''' trazador cubico sujeto, d1y0=y'(x[0]), dyn=y'(x[n-1])
    1ra derivadas en los nodos extremos son conocidas
    '''
    # Vectores como arreglo, numeros reales
    xi = np.array(xi,dtype=float)
    yi = np.array(yi,dtype=float)
    n = len(xi)
    
    h = np.diff(xi,n=1) # h tamano de paso

    # Sistema de ecuaciones
    A = np.zeros(shape=(n,n), dtype=float)
    B = np.zeros(n, dtype=float)
    S = np.zeros(n-1, dtype=float)
    A[0,0] = -h[0]/3
    A[0,1] = -h[0]/6
    B[0] = d1y0 - (yi[1]-yi[0])/h[0]
    for i in range(1,n-1,1):
        A[i,i-1] = h[i-1]
        A[i,i] = 2*(h[i-1]+h[i])
        A[i,i+1] = h[i]
        B[i] = 6*((yi[i+1]-yi[i])/h[i] - (yi[i]-yi[i-1])/h[i-1])
    A[n-1,n-2] = h[n-2]/6
    A[n-1,n-1] = h[n-2]/3
    B[n-1] = d1yn - (yi[n-1]-yi[n-2])/h[n-2]
    
    # Resolver sistema de ecuaciones, S
    S = np.linalg.solve(A,B)

    # Coeficientes
    a = np.zeros(n-1, dtype = float)
    b = np.zeros(n-1, dtype = float)
    c = np.zeros(n-1, dtype = float)
    d = np.zeros(n-1, dtype = float)
    for j in range(0,n-1,1):
        a[j] = (S[j+1]-S[j])/(6*h[j])
        b[j] = S[j]/2
        c[j] = (yi[j+1]-yi[j])/h[j] - (2*h[j]*S[j]+h[j]*S[j+1])/6
        d[j] = yi[j]

    # Polinomio trazador
    x = sym.Symbol('x')
    px_tabla = []
    for j in range(0,n-1,1):
        pxtramo = a[j]*(x-xi[j])**3 + b[j]*(x-xi[j])**2 + c[j]*(x-xi[j])+ d[j]
        px_tabla.append(pxtramo)
    
    if vertabla==True:
        print('h:',h)
        print('A:') ; print(A)
        print('B:',B) ; print('S:',S)
        print('coeficientes[ ,tramo]:')
        print('a:',a) ; print('b:',b)
        print('c:',c) ; print('d:',d)

    return(px_tabla)

# PROGRAMA ---------------
# INGRESO
xi = [0.1 , 0.2, 0.3, 0.4]
fi = [1.45, 1.8, 1.7, 2.0]

titulo = 'Trazador cúbico frontera sujeta'
# sujeto,  1ra derivadas en los nodos extremos son conocidas
d1y0 = 0  # izquierda, xi[0]
d1yn = 0  # derecha, xi[n-1]

# PROCEDIMIENTO
n = len(xi)
# Obtiene los polinomios por tramos
px_tabla = traza3sujeto(xi,fi,d1y0,d1yn,vertabla=True)

# SALIDA
print(titulo)
print('Polinomios por tramos: ')
for i in range(0,n-1,1):
    print('x = [',xi[i],',',xi[i+1],']')
    print('expresión:',px_tabla[i])
    print('simplifica:')
    sym.pprint(px_tabla[i].expand())

Trazador cúbico frontera sujeta [ Algoritmo ] [ gráfica ] [ función ]
..


2. Gráfica en Python

La gráfica se realiza en un bloque con los resultados del algoritmo

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

muestras = 10 # entre cada par de puntos

# Puntos para grafica en 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
    pxk = sym.lambdify('x',px_tabla[i])
    ytramo = pxk(xtramo)
    # vectores de trazador en x,y
    xtraza = np.concatenate((xtraza,xtramo))
    ytraza = np.concatenate((ytraza,ytramo))
    i = i + 1

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

Trazador cúbico frontera sujeta [ Algoritmo ] [ gráfica ] [ función ]
..


3. Trazador cúbico sujeto como función en Scipy.interpolate.CubicSpline

También es posible usar la función de Scipy para generar los trazadores cúbicos en el caso de frontera sujeta. Se hacen los ajustes en el bloque de ingreso, considerando que las primeras derivadas en los nodos extremos son conocidas.

# Trazador cúbico frontera sujeta con Scipy
import numpy as np
import scipy as sp
import sympy as sym

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

titulo = 'Trazador cúbico frontera sujeta'
# sujeto,  1ra derivadas en los nodos extremos son conocidas
d1y0 = 0  # izquierda, xi[0]
d1yn = 0  # derecha, xi[n-1]
cs_tipo = ((1, d1y0), (1, d1yn)) # sujeto

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

# coeficientes de trazadores cúbicos
traza_c = sp.interpolate.CubicSpline(xi,fi,bc_type=cs_tipo )
traza_coef = traza_c.c # coeficientes
a = traza_coef[0]
b = traza_coef[1]
c = traza_coef[2]
d = traza_coef[3]

# Polinomio trazador
x = sym.Symbol('x')
px_tabla = []
for j in range(0,n-1,1):
    pxtramo = a[j]*(x-xi[j])**3 + b[j]*(x-xi[j])**2 + c[j]*(x-xi[j])+ d[j]
    px_tabla.append(pxtramo)

# SALIDA
print(titulo)
print('coeficientes:')
print('a:',a)
print('b:',b)
print('c:',c)
print('d:',d)
print('Polinomios por tramos: ')
for i in range(0,n-1,1):
    print('x = [',xi[i],',',xi[i+1],']')
    print('expresión:',px_tabla[i])
    print('simplifica:')
    sym.pprint(px_tabla[i].expand())

La gráfica se puede realizar con el bloque presentado para el algoritmo anterior. El resultado del algoritmo se presenta como

Trazador cúbico frontera sujeta
coeficientes:
a: [-540.  470. -490.]
b: [ 89. -73.  68.]
c: [0.  1.6 1.1]
d: [1.45 1.8  1.7 ]
Polinomios por tramos: 
x = [ 0.1 , 0.2 ]
expresión: -540.0*(x - 0.1)**3 + 89.0*(x - 0.1)**2 + 1.45
simplifica:
         3          2                
- 540.0⋅x  + 251.0⋅x  - 34.0⋅x + 2.88
x = [ 0.2 , 0.3 ]
expresión: 1.6*x + 470.0*(x - 0.2)**3 - 73.0*(x - 0.2)**2 + 1.48
simplifica:
       3          2                           
470.0⋅x  - 355.0⋅x  + 87.2000000000001⋅x - 5.2
x = [ 0.3 , 0.4 ]
expresión: 1.1*x - 490.0*(x - 0.3)**3 + 68.0*(x - 0.3)**2 + 1.37
simplifica:
         3          2                  
- 490.0⋅x  + 509.0⋅x  - 172.0⋅x + 20.72

Trazador cúbico frontera sujeta [ Algoritmo ] [ gráfica ] [ función ]

4.5 Trazadores Cúbicos Natural o libre con Python

[ Trazador Cúbico ] [ Algoritmo ] [ gráfica ] [ función ]
..


1. Trazador Cúbico - Planteamiento

Referencia:  Burden 3.5 p105, Chapra 18.6.3 p532, Rodríguez 6.11.1 p244

Tiene como objetivo incorporar condiciones adicionales para la función en los extremos del intervalo donde se encuentran los puntos o "nodos".

Por ejemplo, si los datos son los de una trayectoria en un experimento de física, se podría disponer de la aceleración el punto inicial de las muestras (izquierda) y de salida (derecha) del intervalo de las muestras.

spline 03

El método busca obtener un polinomio de tercer grado para cada sub-intervalo o tramo entre "nodos" consecutivos (j,  j+1), de la forma:

S_j(x_j) = a_j + b_j (x-x_j) + c_j(x-xj)^2 + d_j(x-x_j)^3

para cada j = 0, 1, ..., n-1

Para n datos existen n-1 tramos, cuatro incógnitas (coeficientes) que evaluar por cada tramo, como resultado 4(n-1) incógnitas para todo el intervalo .

Para los términos (xj+1- xj) de los tramos que se usan varias veces en el desarrollo, se simplifican como hj:

h_j = x_{j+1} - x_{j} S_j(x_j) = a_j + b_j h_j + c_j h_j^2 + d_jh_j^3

Para generar el sistema de ecuaciones, se siguen los siguientes criterios:

1.  Los valores de la función deben ser iguales en los nodos interiores

S_j(x_{j+1}) = f(x_{j+1}) = S_{j+1}(x_{j+1})

2. Las primeras derivadas en los nodos interiores deben ser iguales

S'_j(x_{j+1}) = S'_{j+1}(x_{j+1})

3. Las segundas derivadas en los nodos interiores deben ser iguales

S''_j(x_{j+1}) = S''_{j+1}(x_{j+1})

4. El primer y último polinomio deben pasar a través de los puntos extremos

f(x_0) = S_0(x_0) = a_0 f(x_n) = S_n(x_n) = a_n

5. Una de las condiciones de frontera se satisface:

5a. frontera libre o natural: Las segundas derivadas en los nodos extremos son cero

S''(x_0) = S''(x_n) = 0

5b. frontera sujeta: las primeras derivadas en los nodos extremos son conocidas

S'(x_0) = f'(x_0)

S'(x_n) = f'(x_n)

Tarea: Revisar en los libros. textos el planteamiento de las ecuaciones para resolver el sistema que se genera al plantear el polinomio.

Ubicar en el texto las ecuaciones resultantes, que son las que se aplicarán en el algoritmo.

[ Trazador Cúbico ] [ Algoritmo ] [ gráfica ] [ función ]

..


2. Algoritmo en Python

El algoritmo parte de lo desarrollado para "trazadores lineales", donde se presentaron los bloques para:

  • construir el trazador en una tabla de polinomios por tramos
  • realizar la gráfica con los trazadores en cada tramo
  • evaluar cada uno de ellos en cada tramo con muestras suficientes para presentar una buena resolución o precisión en la gráfica

Del algoritmo básico se modifica entonces el bloque del cálculo de los polinomios de acuerdo al planteamiento de formulas y procedimientos para trazadores cúbicos naturales (enviado a revisar como tarea).

# Trazador cubico natural
import numpy as np
import sympy as sym

def traza3natural(xi,yi, vertabla=False):
    ''' trazador cubico natural,
    2da derivadas en los nodos extremos son cero
    '''
    # Vectores como arreglo, numeros reales
    xi = np.array(xi,dtype=float)
    yi = np.array(yi,dtype=float)
    n = len(xi)

    h = np.diff(xi,n=1) # h tamano de paso

    # Sistema de ecuaciones
    A = np.zeros(shape=(n-2,n-2), dtype=float)
    B = np.zeros(n-2, dtype=float)
    S = np.zeros(n, dtype=float)
    A[0,0] = 2*(h[0]+h[1])
    A[0,1] = h[1]
    B[0] = 6*((yi[2]-yi[1])/h[1] - (yi[1]-yi[0])/h[0])
    for i in range(1,n-3,1):
        A[i,i-1] = h[i]
        A[i,i] = 2*(h[i]+h[i+1])
        A[i,i+1] = h[i+1]
        B[i] = 6*((yi[i+2]-yi[i+1])/h[i+1] - (yi[i+1]-yi[i])/h[i])
    A[n-3,n-4] = h[n-3]
    A[n-3,n-3] = 2*(h[n-3]+h[n-2])
    B[n-3] = 6*((yi[n-1]-yi[n-2])/h[n-2] - (yi[n-2]-yi[n-3])/h[n-3])

    # Resolver sistema de ecuaciones, S
    r = np.linalg.solve(A,B)
    #S[0] = 0; S[n-1] = 0 # Definidos en cero
    S[1:n-1] = r[0:n-2]

    # Coeficientes
    a = np.zeros(n-1, dtype = float)
    b = np.zeros(n-1, dtype = float)
    c = np.zeros(n-1, dtype = float)
    d = np.zeros(n-1, dtype = float)
    for j in range(0,n-1,1):
        a[j] = (S[j+1]-S[j])/(6*h[j])
        b[j] = S[j]/2
        c[j] = (yi[j+1]-yi[j])/h[j] - (2*h[j]*S[j]+h[j]*S[j+1])/6
        d[j] = yi[j]

    # Polinomio trazador
    x = sym.Symbol('x')
    px_tabla = []
    for j in range(0,n-1,1):
        pxtramo = a[j]*(x-xi[j])**3 + b[j]*(x-xi[j])**2 + c[j]*(x-xi[j])+ d[j]
        px_tabla.append(pxtramo)
    
    if vertabla==True:
        print('h:',h)
        print('A:') ; print(A)
        print('B:',B) ; print('S:',S)
        print('r:',r)
        print('coeficientes[ ,tramo]:')
        print('a:',a) ; print('b:',b)
        print('c:',c) ; print('d:',d)

    return(px_tabla)

# PROGRAMA ---------------
# INGRESO
xi = [0.1 , 0.2, 0.3, 0.4]
fi = [1.45, 1.8, 1.7, 2.0]

titulo = 'Trazador cúbico natural o libre'
# natural, 2da derivadas en los nodos extremos son cero

# PROCEDIMIENTO
n = len(xi)
# Obtiene los polinomios por tramos
px_tabla = traza3natural(xi,fi,vertabla=True)

# SALIDA
print(titulo)
print('Polinomios por tramos: ')
for i in range(0,n-1,1):
    print('x = [',xi[i],',',xi[i+1],']')
    print('expresion:',px_tabla[i])
    print('simplificado:')
    sym.pprint(px_tabla[i].expand())

con lo que se obtiene el resultado por cada tramo

h: [0.1 0.1 0.1]
A:
[[0.4 0.1]
 [0.1 0.4]]
B: [-27.  24.]
r: [-88.  82.]
S: [  0. -88.  82.   0.]
coeficientes[ ,tramo]:
a: [-146.66666667  283.33333333 -136.66666667]
b: [  0. -44.  41.]
c: [4.96666667 0.56666667 0.26666667]
d: [1.45 1.8  1.7 ]
Trazador cúbico natural o libre
Polinomios por tramos: 
x = [ 0.1 , 0.2 ]
expresion: 4.96666666666667*x - 146.666666666667*(x - 0.1)**3 + 0.953333333333333
simplificado:
                    3         2                            
- 146.666666666667⋅x  + 44.0⋅x  + 0.566666666666666⋅x + 1.1
x = [ 0.2 , 0.3 ]
expresion: 0.566666666666666*x + 283.333333333333*(x - 0.2)**3 - 44.0*(x - 0.2)**2 + 1.68666666666667
simplificado:
                  3          2                            
283.333333333333⋅x  - 214.0⋅x  + 52.1666666666667⋅x - 2.34
x = [ 0.3 , 0.4 ]
expresion: 0.266666666666665*x - 136.666666666667*(x - 0.3)**3 + 41.0*(x - 0.3)**2 + 1.62
simplificado:
                    3          2                           
- 136.666666666667⋅x  + 164.0⋅x  - 61.2333333333333⋅x + 9.0
>>>

[ Trazador Cúbico ] [ Algoritmo ] [ gráfica ] [ función ]
..


3. Gráfica con Python

Para el trazado de la gráfica se añade al final del algoritmo:

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

muestras = 10 # entre cada par de puntos

# Puntos para grafica en 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
    pxk = sym.lambdify('x',px_tabla[i])
    ytramo = pxk(xtramo)
    # vectores de trazador en x,y
    xtraza = np.concatenate((xtraza,xtramo))
    ytraza = np.concatenate((ytraza,ytramo))
    i = i + 1

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

[ Trazador Cúbico ] [ Algoritmo ] [ gráfica ] [ función ]
..


4. Trazador cúbico natural como función en Scipy

Referenciahttps://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.CubicSpline.html#scipy.interpolate.CubicSpline

La librería Scipy dispone de una función para calcular los coeficientes de los trazadores cúbicos. Para el ejemplo del ejercicio se realiza el cálculo y los polinomios.

Trazador cúbico natural o libre
coeficientes:
a: [-146.66666667  283.33333333 -136.66666667]
b: [ 8.8817842e-15 -4.4000000e+01  4.1000000e+01]
c: [4.96666667 0.56666667 0.26666667]
d: [1.45 1.8  1.7 ]
Polinomios por tramos: 
x = [ 0.1 , 0.2 ]
expresión: 4.96666666666667*x - 146.666666666667*(x - 0.1)**3 + 8.88178419700125e-15*(x - 0.1)**2 + 0.953333333333333
simplifica:
                    3         2                            
- 146.666666666667⋅x  + 44.0⋅x  + 0.566666666666662⋅x + 1.1
x = [ 0.2 , 0.3 ]
expresión: 0.566666666666667*x + 283.333333333333*(x - 0.2)**3 - 44.0*(x - 0.2)**2 + 1.68666666666667
simplifica:
                  3          2                            
283.333333333333⋅x  - 214.0⋅x  + 52.1666666666667⋅x - 2.34
x = [ 0.3 , 0.4 ]
expresión: 0.266666666666665*x - 136.666666666667*(x - 0.3)**3 + 41.0*(x - 0.3)**2 + 1.62
simplifica:
                    3          2                           
- 136.666666666667⋅x  + 164.0⋅x  - 61.2333333333333⋅x + 9.0

Las instrucciones en Python con la librería Scipy para trazadores cúbicos natural son:

# Trazador cúbico natural o libre con Scipy
import numpy as np
import scipy as sp
import sympy as sym

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

# natural, 2da derivadas en los nodos extremos son cero
cs_tipo = ((2, 0.0), (2, 0.0)) # natural

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

# coeficientes de trazadores cúbicos
traza_cub = sp.interpolate.CubicSpline(xi,fi,bc_type=cs_tipo )
traza_coef = traza_cub.c # coeficientes
a = traza_coef[0]
b = traza_coef[1]
c = traza_coef[2]
d = traza_coef[3]

# Polinomio trazador
x = sym.Symbol('x')
px_tabla = []
for j in range(0,n-1,1):
    pxtramo = a[j]*(x-xi[j])**3 + b[j]*(x-xi[j])**2 + c[j]*(x-xi[j])+ d[j]
    px_tabla.append(pxtramo)

# SALIDA
print('coeficientes:')
print('a:',a)
print('b:',b)
print('c:',c)
print('d:',d)
print('Polinomios por tramos: ')
for i in range(0,n-1,1):
    print('x = [',xi[i],',',xi[i+1],']')
    print('expresion:',px_tabla[i])
    print('simplifica:')
    sym.pprint(px_tabla[i].expand())

la gráfica se puede generar con el bloque anterior

[ Trazador Cúbico ] [ Algoritmo ] [ gráfica ] [ función ]


Si los polinomios no se igualan entre los nodos, tampoco sus velocidades y aceleraciones; los puntos de inflexión en los nodos podrían generar una experiencia semejante a variaciones de velocidad y aceleración en una trayectoria como la mostrada en los siguientes videos.

youtube: slingshot ride

Car sales woman scares customers. maxman.tv. 4 enero 2016

[ Trazador Cúbico ] [ Algoritmo ] [ gráfica ] [ función ]