Etiqueta: algoritmo Python

  • 5.3 Regla de Simpson 3/8 con Python

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


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

    Integra regla Simpson 3/8 animado

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


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

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


    3. Algoritmo en Python para f(x)

    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: 3
    Integral fx con Simpson 3/8:  2.0636730597016992
    tramos:  6
    Integral con Simpson 3/8:  2.0542043270535757
    
    tramos: 9
    Integral fx con Simpson 3/8:  2.053741914538051
    tramos: 12
    Integral fx con Simpson 3/8:  2.0536653010019474

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

    # Regla Simpson 3/8 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 = 6 # multiplo de 3
    
    # validar: tramos debe ser múltiplo de 3
    while tramos%3 >0:
        print('tramos: ',tramos)
        txt = 'tramos debe ser múltiplo de 3:'
        tramos = int(input(txt))
    
    # PROCEDIMIENTO
    muestras = tramos + 1
    xi = np.linspace(a,b,muestras)
    fi = fx(xi)
    
    # Regla de Simpson 3/8
    h = (b-a)/tramos
    suma = 0 # integral numérico
    for i in range(0,tramos-2,3): #muestras-3
        S38 = (3/8)*h*(fi[i]+3*fi[i+1]+3*fi[i+2]+fi[i+3])
        suma = suma + S38
    
    # SALIDA
    print('tramos:', tramos)
    print('Integral fx con Simpson 3/8: ', suma)
    

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


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

    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.

    Integral regla Simpson 3/8Instrucciones en Python adicionales al algoritmo anterior

    # GRAFICA ---------------------
    import matplotlib.pyplot as plt
    
    titulo = 'Regla de Simpson 3/8'
    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:
        fx_existe = False
    
    try: # existen mensajes de error
        msj_existe = len(msj)
    except NameError:
        msj = []  
    
    # Simpson 3/8 relleno y bordes, cada 3 tramos
    for i in range(0,muestras-2,3):
        x_tramo = xi[i:(i+3)+1]
        f_tramo = fi[i:(i+3)+1]
    
        # interpolación polinomica a*(x**3)+b*(x**2)+c*x+d
        coef = np.polyfit(x_tramo, f_tramo, 3) # [a,b,c,d]
        px = lambda x: coef[0]*(x**3)+coef[1]*(x**2)+coef[2]*x+coef[3]
        
        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/3)%2==0: # bloque 3 tramos, es par
            relleno ='lightblue'
        plt.fill_between(xp,fp,fp*0,color=relleno)
           
    # Divisiones entre Simpson 3/8
    for i in range(0,muestras,1):
        tipolinea = 'dotted'
        if i%3==0: # i es multiplo de 3
            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 3/8 ] [ 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 3/8 y que existan suficientes puntos para completar el integral.

    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]
    

    Resultados con el algoritmo

    tramos:  6
    Integral fx con Simpson3/8: 2.0542043057079566

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

    # Integración Simpson 3/8 para muestras xi,fi
    import numpy as np
    
    # 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]
    
    # PROCEDIMIENTO
    casicero=1e-7 # decimales para truncar 
    # 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<3: # puntos insuficientes
        msj.append(['tramos insuficientes:',tramos])
    
    suma = 0 # integral numerico
    i = 0
    while i<(muestras-3) 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+4],2) # diferencias entre tramos
            errado = np.max(np.abs(d2x))
            if errado<casicero: # 3 tramos equidistantes
                S38 = (3/8)*h*(fi[i]+3*fi[i+1]+3*fi[i+2]+fi[i+3])
                suma = suma + S38
                i = i+3
            else:
                donde = np.argmax(np.abs(d2x))+i+1
                msj.append(['no equidistantes en i',donde])
    
    # SALIDA
    print('tramos: ',len(xi)-1)
    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 Simpson3/8:',suma)
    
    

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

     

  • 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.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 ]

     

  • 4.4 Trazadores lineales (Splines) grado1 con Python

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


    1. Trazadores lineales (Splines) grado1

    Referencia: Chapra 18.6.1 p525
    spline 01

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

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

    spline 02

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

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

    ...

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

    donde

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

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

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

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


    2. Ejercicio

    Datos de los puntos como ejemplo para el algoritmo

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

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

    ..


    3. Desarrollo analítico

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

    0.1 ≤ x≤ 0.2

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

    0.2 ≤ x ≤ 0.3

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

    0.3 ≤ x ≤ 0.4

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

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

    spline 02

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


    4. Algoritmo en Python

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

    # Trazador (spline) lineal, grado 1
    import numpy as np
    import sympy as sym
    
    # INGRESO , Datos de prueba
    xi = [0.1 , 0.2, 0.3, 0.4]
    fi = [1.45, 1.8, 1.7, 2.0]
    
    # PROCEDIMIENTO
    # Vectores como arreglo, numeros reales
    xi = np.array(xi,dtype=float)
    fi = np.array(fi,dtype=float)
    n = len(xi)
    
    # trazador lineal, grado 1
    x = sym.Symbol('x')
    px_tabla = [] # por cada tramo
    
    i = 0 # tramo inicial px = f0 +d1f*(x-x0)
    while i<(n-1):
        # con 1ra diferencia finita avanzada
        d1f =(fi[i+1]-fi[i])/(xi[i+1]-xi[i])
        pxtramo = fi[i] + d1f*(x-xi[i])
        px_tabla.append(pxtramo)
        i = i + 1
    
    # SALIDA
    print('Polinomios por tramos: ')
    for i in range(0,n-1,1):
        print('x = [',xi[i],',',xi[i+1],']')
        print('',px_tabla[i])
    

    Se obtiene como resultado:

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

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


    5. Gráfica con Python

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

    • evaluar el polinomio en cada tramo
    • concatenar los resultados de todos los tramos en los vectores xtraza, ytraza.
    • poner en la gráfica los puntos del problema y las líneas que genera cada polinomio
    # GRAFICA --------------
    import matplotlib.pyplot as plt
    
    muestras = 5 # entre cada par de puntos
    # Puntos para graficar cada tramo
    xtraza = np.array([],dtype=float)
    ytraza = np.array([],dtype=float)
    i = 0
    while i<(n-1): # cada tramo
        xtramo = np.linspace(xi[i],xi[i+1],muestras)
        # evalua polinomio del tramo
        pxt = sym.lambdify('x',px_tabla[i])
        ytramo = pxt(xtramo)
        # vectores de trazador en x,y
        xtraza = np.concatenate((xtraza,xtramo))
        ytraza = np.concatenate((ytraza,ytramo))
        i = i + 1
    
    # Graficar
    plt.plot(xi,fi,'o', label='puntos')
    plt.plot(xtraza,ytraza, label='trazador')
    plt.title('Trazadores lineales (splines)')
    plt.xlabel('xi')
    plt.ylabel('px(xi)')
    plt.legend()
    plt.show()

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

  • 4.3 Interpolación polinómica de Lagrange con Python

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


    1. Interpolación polinómica de Lagrange

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

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

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

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

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

    interpola Lagrange 01

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


    2. Ejercicio

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

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

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

    ..


    3. Desarrollo analítico

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

    término 1

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

    término 2

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

    término 3

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

    término 4

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

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

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

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

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

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

    interpola lagrange 01

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


    3. Algoritmo en Python

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

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

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

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

        valores de fi:  [1.  1.6 1.7 2. ]
    divisores en L[i]:  [-0.024  0.004 -0.003  0.008]
    
    Polinomio de Lagrange, expresiones
    250.0*x*(x - 0.3)*(x - 0.2) +
    400.0*x*(x - 0.4)*(x - 0.3) +
    -566.666666666667*x*(x - 0.4)*(x - 0.2) +
    -41.6666666666667*(x - 0.4)*(x - 0.3)*(x - 0.2)
    
    Polinomio de Lagrange: 
    41.6666666666667*x**3 - 27.5*x**2 + 6.83333333333334*x + 1.0
    
                      3         2                           
    41.6666666666667⋅x  - 27.5⋅x  + 6.83333333333334⋅x + 1.0
    

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

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

    # Interpolacion de Lagrange
    # divisoresL solo para mostrar valores denominador
    import numpy as np
    import sympy as sym
    
    # INGRESO , Datos de prueba
    xi = [0, 0.2, 0.3, 0.4]
    fi = [1, 1.6, 1.7, 2.0]
    
    # PROCEDIMIENTO
    # Vectores como arreglo, numeros reales
    xi = np.array(xi,dtype=float)
    fi = np.array(fi,dtype=float)
    n = len(xi)
    
    # Polinomio de Lagrange
    x = sym.Symbol('x')
    polinomio = 0*x   # sym.S.Zero en Sympy
    divisorL = np.zeros(n, dtype = float)
    for i in range(0,n,1):
        
        # Termino de Lagrange
        numerador = 1
        denominador = 1
        for j  in range(0,n,1):
            if (j!=i):
                numerador = numerador*(x-xi[j])
                denominador = denominador*(xi[i]-xi[j])
        terminoLi = numerador/denominador
    
        polinomio = polinomio + terminoLi*fi[i]
        divisorL[i] = denominador
    
    polisimple = polinomio.expand() # simplifica los (x-xi)
    px = sym.lambdify(x,polisimple) # evaluacién numérica
    
    # SALIDA
    print('    valores de fi: ',fi)
    print('divisores en L[i]: ',divisorL)
    print()
    print('Polinomio de Lagrange, expresiones')
    #print(polinomio)
    terminos = sym.Add.make_args(polinomio)
    n_term = len(terminos)
    for i in range(0,n_term,1):
        if i<(n_term-1):
            print(terminos[i],'+')
        else:
            print(terminos[i])
    print()
    print('Polinomio de Lagrange: ')
    print(polisimple)
    print()
    sym.pprint(polisimple)
    

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


    4. Gráfica del polinomio

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

    # Gráfica --------------
    import matplotlib.pyplot as plt
    muestras = 21   # resolución gráfica
    
    a = np.min(xi)  # intervalo [a,b]
    b = np.max(xi)
    xk = np.linspace(a,b,muestras)
    yk = px(xk)
    
    plt.plot(xi,fi,'o', label = '[xi,fi]')
    plt.plot(xk,yk, label = 'p(x)')
    plt.legend()
    plt.xlabel('xi')
    plt.ylabel('fi')
    plt.title('Interpolación Lagrange')
    plt.show()
    

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


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

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

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

     

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

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


    1. Diferencias divididas de Newton

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

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

    Diferencias Divididas de Newton interpolación polinómica

    La n-ésima diferencia dividida finita es:

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

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

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

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

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

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

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


    2. Ejercicio

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

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

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

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

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

    ..


    3. Desarrollo analítico

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

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

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

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

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

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

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

    p_3(x) = f_0 = 5.12

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

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

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

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

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

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

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

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

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

    Diferencias Divididas 02

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


    4. Algoritmo en Python

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

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

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

    # Diferencias Divididas de Newton -Interpolación
    # Tarea: Verificar tamaño de vectores,
    import numpy as np
    import sympy as sym
    
    # INGRESO
    xi = [3.2 , 3.8 , 4.2 , 4.5 ]
    fi = [5.12, 6.42, 7.25, 6.85]
    
    # PROCEDIMIENTO
    casicero = 1e-15  # redondear a cero
    # Matrices como arreglo, numeros reales
    xi = np.array(xi,dtype=float)
    fi = np.array(fi,dtype=float)
    n = len(xi)
    
    # Tabla de Diferencias Divididas
    tabla_etiq = ['i   ','xi  ','fi  ']
    ki = np.arange(0,n,1) # filas
    tabla = np.concatenate(([ki],[xi],[fi]),axis=0)
    tabla = np.transpose(tabla)
    dfinita = np.zeros(shape=(n,n),dtype=float)
    tabla = np.concatenate((tabla,dfinita), axis=1)
    n,m = np.shape(tabla)
    # Calcula tabla de Diferencias Divididas
    diagonal = n-1 # derecha a izquierda
    j = 3  # inicia en columna 3
    while (j < m): # columna
        tabla_etiq.append('F['+str(j-2)+']') # título columna
        i = 0 # cada fila de columna
        paso = j-2 # inicia en 1
        while (i < diagonal): # antes de diagonal
            tabla[i,j] = tabla[i+1,j-1]-tabla[i,j-1]
    
            denominador = (xi[i+paso]-xi[i])
            tabla[i,j] = tabla[i,j]/denominador
    
            if abs(tabla[i,j])<casicero: # casicero revisa
                tabla[i,j]=0
            i = i + 1
        diagonal = diagonal - 1
        j = j + 1
        
    dDividida = tabla[0,3:] # diferencias Divididas
    
    # polinomio con diferencias Divididas de Newton
    x = sym.Symbol('x')
    polinomio = fi[0] +0*x # en Sympy
    for i in range(1,n,1):
        factor = dDividida[i-1]
        termino = 1
        for j in range(0,i,1):
            termino = termino*(x-xi[j])
        polinomio = polinomio + termino*factor
    
    polisimple = polinomio.expand() # simplifica los (x-xi)
    px = sym.lambdify(x,polisimple) # evaluación numérica
    
    # SALIDA
    np.set_printoptions(precision = 4)
    print('Tabla Diferencia Dividida-Newton')
    print([tabla_etiq])
    print(tabla)
    print('dDividida: ')
    print(dDividida)
    print('polinomio: ')
    print(polinomio)
    print('polinomio simplificado: ' )
    print(polisimple)
    

    El resultado del algoritmo se muestra a continuación:

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

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


    5. Gráfica de polinomio de interpolación

    # GRAFICA  --------------
    import matplotlib.pyplot as plt
    
    titulo = 'Interpolación: Diferencias Divididas - Newton'
    muestras = 21  # resolución gráfica
    
    a = np.min(xi) # intervalo [a,b]
    b = np.max(xi)
    xk = np.linspace(a,b,muestras)
    yk = px(xk)
    
    plt.plot(xi,fi,'o', label='[xi,fi]')
    plt.plot(xk,yk, label='p(x)')
    
    try: # existen mensajes de error
        msj_existe = len(msj)
    except NameError:
        msj = []
    if len(msj)>0: # tramos con error
        untipo = msj[0][0]
        donde = msj[0][1] # indice error
        plt.plot(xi[donde:donde+2],fi[donde:donde+2],
                 'ro',label=untipo)
        plt.plot(xi[donde:donde+2],fi[donde:donde+2],
                 'r--')
    
    plt.xlabel('xi')
    plt.ylabel('fi')
    plt.legend()
    plt.title(titulo)
    plt.show()
    

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


    6. Algoritmo como función

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

    El bloque de salida es semejante al resultado anterior.

    # Diferencias Divididas de Newton - Interpolación
    # funciones diferencias_tabla
    # Tarea: Verificar tamaño de vectores
    import numpy as np
    import math
    import sympy as sym
    
    def diferencias_tabla(xi,fi,tipo='finitas', vertabla=False,
                    casicero = 1e-15, precision=4):
        '''Genera la tabla de diferencias finitas o divididas
          tipo = 'finitas, tipo = 'divididas'
        resultado en: tabla, titulo
        Tarea: verificar tamaño de vectores
        '''
        # revisa tipo de tabla
        tipolist = ['finitas','divididas']
        if not(tipo in tipolist):
            print('error de tipo, seleccione:',tipolist)
            return()
        
        prefijo = ['d','f']
        if tipo=='divididas':
            prefijo = ['F[',']']
        
        # Matrices como arreglo, numeros reales
        xi = np.array(xi,dtype=float)
        fi = np.array(fi,dtype=float)
        n = len(xi)
    
        # Tabla de Diferencias Finitas/Divididas
        tabla_etiq = ['i','xi','fi']
        ki = np.arange(0,n,1) # filas
        tabla = np.concatenate(([ki],[xi],[fi]),axis=0)
        tabla = np.transpose(tabla)
        dfinita = np.zeros(shape=(n,n),dtype=float)
        tabla = np.concatenate((tabla,dfinita), axis=1)
        n,m = np.shape(tabla)
        # Calular tabla de Diferencias Finitas/Divididas
        diagonal = n-1 # derecha a izquierda
        j = 3  # inicia en columna 3
        while (j < m): # columna
            tabla_etiq.append(prefijo[0]+str(j-2)+prefijo[1])
            # paso = j-2 # inicia en 1
            i = 0 # fila
            while (i < diagonal): # antes de diagonal
                tabla[i,j] = tabla[i+1,j-1]-tabla[i,j-1]
    
                if tipo=='divididas':
                    # denominador = xi[i+paso]-xi[i]
                    denominador = xi[i+j-2]-xi[i]
                    tabla[i,j] = tabla[i,j]/denominador
                    
                if abs(tabla[i,j])<casicero: # casicero revisa
                        tabla[i,j]=0
                i = i + 1
            diagonal = diagonal - 1
            j = j + 1
                
        if vertabla==True:
            np.set_printoptions(precision)
            print('Tabla de Diferencias',tipo)
            print([tabla_etiq])
            print(tabla)
        return([tabla, tabla_etiq])
    
    # PROGRAMA ---------------------
    
    # INGRESO
    xi = [3.2, 3.8, 4.2, 4.5]
    fi = [5.12, 6.42, 7.25, 6.85]
    
    tipo_tabla = 'divididas'
    
    # PROCEDIMIENTO
    casicero = 1e-12
    # Vectores como arreglo, numeros reales
    xi = np.array(xi,dtype=float)
    fi = np.array(fi,dtype=float)
    n = len(xi)
    
    tabla,tabla_etiq = diferencias_tabla(xi,fi,
                            tipo=tipo_tabla,
                            vertabla=True,casicero = casicero)
    
    dfinita = tabla[0,3:] # diferencias divididas
    h = xi[1] - xi[0]     # suponer tramos equidistantes
    
    # polinomio con diferencias Finitas Avanzadas/ divididas
    x = sym.Symbol('x')
    polinomio = fi[0] +0*x # sym.S.Zero en Sympy
    for i in range(1,n,1):
        factor = dfinita[i-1] # diferencias divididas
        if tipo_tabla=='finitas': # diferencias Finitas Avanzadas
            denominador = math.factorial(i)*(h**i)
            factor = factor/denominador
        termino = 1
        for j in range(0,i,1):
            termino = termino*(x-xi[j])
        polinomio = polinomio + termino*factor
    
    polisimple = polinomio.expand() # simplifica los (x-xi)
    px = sym.lambdify(x,polisimple) # evaluacion numerica
    
    # SALIDA
    print('d'+tipo_tabla+': ')
    print(dfinita)
    print('Tramo h:',h)
    print('polinomio: ')
    #print(polinomio)
    terminos = sym.Add.make_args(polinomio)
    n_term = len(terminos)
    for i in range(0,n_term,1):
        if i<(n_term-1):
            print(terminos[i],'+')
        else:
            print(terminos[i])
    print()
    print('polinomio simplificado: ' )
    print(polisimple)
    

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


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