Tenemos una serie de puntos $(x_0,y_0),(x_1,y_1),\ldots,(x_n,y_n)$ y queremos encontrar un polinomio que pase por todos ellos.
Sea el conjunto de puntos: $$C=\{(2,2),(3,6),(4,5),(5,5),(6,6)\}$$
Vamos a dibujar los puntos.
Para que inserte las figuras en la notebook, en lugar de abrir cada figura en una ventana nueva.
%matplotlib inline
Importamos los módulos matplotlib.pyplot
y numpy
.
import numpy as np
import matplotlib.pyplot as plt
Los puntos los almacenaremos en matrices
x = np.array([2.,3.,4.,5.,6.])
y = np.array([2.,6.,5.,5.,6.])
Y los dibujamos
plt.plot(x,y,'ro')
plt.axis([1.5,6.5,1.5,6.5]); # Dibujamos en [1.5,6.5]x[1.5,6.5]
Como tenemos $5$ puntos podemos plantear $5$ ecuaciones y necesitamos $5$ incógnitas que serán los coeficientes del polinomio. Por lo tanto:
$$P_4(x)=a_0+a_1x+a_2x^2+a_3x^3+a_4x^4$$es decir $a_0,a_1,a_2,a_3,a_4$, que son $5$ incógnitas y el polinomio es, en principio, de grado $4$. En general, para los puntos $(x_0,y_0),(x_1,y_1),\ldots,(x_n,y_n)$ el polinomio interpolante será de grado $\leq n$.
Las ecuaciones serían:
$$ \begin{array}{lll} P_{4}(x_{0})=y_{0} & \quad & a_{0}+a_{1}x_{0}+a_{2}x_{0}^{2}+a_{3}x_{0}^{3}+a_{4}x_{0}^{4}=y_{0}\\ P_{4}(x_{1})=y_{1} & \quad & a_{0}+a_{1}x_{1}+a_{2}x_{1}^{2}+a_{3}x_{1}^{3}+a_{4}x_{1}^{4}=y_{1}\\ P_{4}(x_{2})=y_{2} & \quad & a_{0}+a_{1}x_{2}+a_{2}x_{2}^{2}+a_{3}x_{2}^{3}+a_{4}x_{2}^{4}=y_{2}\\ P_{4}(x_{3})=y_{3} & \quad & a_{0}+a_{1}x_{3}+a_{2}x_{3}^{2}+a_{3}x_{3}^{3}+a_{4}x_{3}^{4}=y_{3}\\ P_{4}(x_{4})=y_{4} & \quad & a_{0}+a_{1}x_{4}+a_{2}x_{4}^{2}+a_{3}x_{4}^{3}+a_{4}x_{4}^{4}=y_{4} \end{array} $$Y el sistema a resolver es:
$$ \left(\begin{array}{ccccc} 1 & x_{0} & x_{0}^{2} & x_{0}^{3} & x_{0}^{4}\\ 1 & x_{1} & x_{1}^{2} & x_{1}^{3} & x_{1}^{4}\\ 1 & x_{2} & x_{2}^{2} & x_{2}^{3} & x_{2}^{4}\\ 1 & x_{3} & x_{3}^{2} & x_{3}^{3} & x_{3}^{4}\\ 1 & x_{4} & x_{4}^{2} & x_{4}^{3} & x_{4}^{4} \end{array}\right)\left(\begin{array}{c} a_{0}\\ a_{1}\\ a_{2}\\ a_{3}\\ a_{4} \end{array}\right)=\left(\begin{array}{c} y_{0}\\ y_{1}\\ y_{2}\\ y_{3}\\ y_{4} \end{array}\right) $$La matriz de coeficientes del sistema se llama Matriz de Vandermonde.
Vandermonde(x)
que tenga como argumentos de entrada el vector x
y como argumento de salida la matriz de Vandermonde V
.Va=y
. Usar la orden numpy.linalg.solve
.numpy.polyval
para evaluar el polinomio en un punto o puntos a partir de los coeficientes obtenidos en el apartado anterior.Usar los nodos:
x = np.array([2.,3.,4.,5.,6.])
y = np.array([2.,6.,5.,5.,6.])
Comprobar que el programa también funciona con los nodos:
x = np.array([0.,1.,2.,3.,4.,5.,6.])
y = np.array([3.,5.,6.,5.,4.,4.,5.])
%run Ejercicio1.py
Como resultados intermedios en este problema, podemos obtener la matriz de Vandermonde y los coeficientes del polinomio.
# opciones del print
np.set_printoptions(precision = 2) # solo dos decimales
np.set_printoptions(suppress = True) # no usar notación exponencial
print 'Matriz de Vandermonde V'
print V
print 'Coeficientes del polinomio (desde a0 hasta a4)'
print a
Existe una función Python que calcula la Matriz de Vandermonde. El orden de los elementos es distinto al que hemos usado
va = np.vander(x, len(x))
print(va)
Pero este no es un método aconsejable porque la matriz de Vandermonde, en general, está mal condicionada y puede dar lugar a grandes errores en la solución del sistema.
Para cada $k=0,1,\ldots,n$, existe un único polinomio $\ell_{k}$ de grado $\leq n$ tal que $\ell_{k}\left(x_{j}\right)=\delta_{kj}$ (uno si $k=j$, cero en caso contrario):
$$ \ell_{k}\left(z\right)= \frac{(z-x_{0})\ldots(z-x_{k-1})(z-x_{k+1})\ldots(z-x_{n})}{(x_{k}-x_{0}) \ldots(x_{k}-x_{k-1})(x_{k}-x_{k+1})\ldots(x_{k}-x_{n})}. $$Los polinomios $\ell_{0},\ell_{1},\ldots,\ell_{n}$ son los polinomios fundamentales de Lagrange.
El polinomio interpolante de Lagrange en los puntos $x_{0,}x_{1},\ldots,x_{n}$ relativo a los valores $y_{0},y_{1},\ldots,y_{n}$ es
$$ P_{n}\left(x\right)=y_{0}\ell_{0}\left(x\right)+ y_{1}\ell_{1}\left(x\right)+\cdots+y_{n}\ell_{n}\left(x\right) $$Escribir un programa que calcule el polinomio interpolante de Lagrange utilizando los polinomios funcamentales de Lagrange. Para ello, crear dos funciones. La primera será lagrange_fund(k,z,x)
que tendrá:
y una funcion polinomio_lagrange(z,x,y)
que tendrá:
Dibujar el polinomio Fundamental de Lagrange con índice $k=2$ en el intervalo $[2,6]$. Dibujar el polinomio interpolante en el intervalo $[2,6]$.
Usar los nodos:
x = np.array([2.,3.,4.,5.,6.])
y = np.array([2.,6.,5.,5.,6.])
Comprobar que también funciona con los nodos:
x = np.array([0.,1.,2.,3.,4.,5.,6.])
y = np.array([3.,5.,6.,5.,4.,4.,5.])
%run Ejercicio2.py
El inconveniente de este método es que si queremos incorporar un nodo nuevo tenemos que rehacer todos los cálculos.
Fórmula de Newton
El polinomio de interpolación de Lagrange en los puntos $x_{0,}x_{1},\ldots,x_{n}$ relativo a los valores $y_{0},y_{1},\ldots,y_{n}$ es
$$ P_{n}\left(x\right)= \left[y_{0}\right]+\left[y_{0},y_{1}\right]\left(x-x_{0}\right)+ \left[y_{0},y_{1},y_{2}\right]\left(x-x_{0}\right)\left(x-x_{1}\right)+\cdots +\left[y_{0},y_{1},\ldots,y_{n}\right]\left(x-x_{0}\right)\left(x-x_{1}\right)\cdots\left(x-x_{n-1}\right) $$Tabla de diferencias divididas
Los coeficientes del polinomio anterior son la primera fila de la tabla: $$ \begin{array}{cccccc} x_{0} & y_{0} & \left[y_{0},y_{1}\right]=\frac{y_{0}-y_{1}}{x_{0}-x_{1}} & \left[y_{0},y_{1},y_{2}\right]=\frac{\left[y_{0},y_{1}\right]-\left[y_{1},y_{2}\right]}{x_{0}-x_{2}} & \ldots & \left[y_{0},y_{1},\ldots,y_{n}\right]=\frac{\left[y_{0},\ldots,y_{n-1}\right]-\left[y_{1},\ldots,y_{n}\right]}{x_{0}-x_{n}}\\ x_{1} & y_{1} & \left[y_{1},y_{2}\right]=\frac{y_{1}-y_{2}}{x_{1}-x_{2}} & \left[y_{1},y_{2},y_{3}\right]=\frac{\left[y_{1},y_{2}\right]-\left[y_{2},y_{3}\right]}{x_{1}-x_{3}} & \ldots\\ x_{2} & y_{2} & \left[y_{2},y_{3}\right]=\frac{y_{2}-y_{3}}{x_{2}-x_{3}} & \left[y_{2},y_{3},y_{4}\right]=\frac{\left[y_{2},y_{3}\right]-\left[y_{3},y_{4}\right]}{x_{2}-x_{4}} & \ldots\\ \ldots & \ldots & \ldots & \ldots & \ldots\\ x_{n-1} & y_{n-1} & \left[y_{n-1},y_{n}\right]=\frac{y_{n-1}-y_{n}}{x_{n-1}-x_{n}}\\ x_{n} & y_{n} \end{array} $$
Con este método, si añadimos un nodo, no hay que rehacer todos los cálculos, sino que se añade una línea más a la tabla anterior en su parte inferior.
Escribir un programa que calcule el polinomio interpolante de Lagrange utilizando las diferencias divididas. Para ello, crear dos funciones. La primera será dif_div(x,y)
que tendrá:
y una funcion polinomio_newton(z,x,y)
que tendrá:
Escribir la matriz de diferencias divididas. Dibujar el polinomio interpolante en el intervalo $[2,6]$.
Usar los nodos:
x = np.array([2.,3.,4.,5.,6.])
y = np.array([2.,6.,5.,5.,6.])
Comprobar que también funciona con los nodos:
x = np.array([0.,1.,2.,3.,4.,5.,6.])
y = np.array([3.,5.,6.,5.,4.,4.,5.])
%run Ejercicio3.py
El polinomio de interpolación se puede obtener con la función de numpy polyfit
escogiendo como grado del polinomio el número de puntos menos uno. Nos da los coeficientes del polinomio interpolante en un vector, empezando por el de mayor grado:
x = np.array([2.,3.,4.,5.,6.])
y = np.array([2.,6.,5.,5.,6.])
pol = np.polyfit(x,y,len(x)-1) # coeficientes del polinomio
xx = np.linspace(min(x),max(x))
yy = np.polyval(pol,xx) # valor del polinomio en los puntos de la matriz xx
Representamos el polinomio
plt.plot(xx, yy, '-',x, y, 'ro')
plt.axis([min(xx)-d, max(xx)+d, min(yy)-d, max(yy)+d]);
Escribir una función chebyshev(f,a,b,n)
que interpole la función f
en el intervalo [a,b]
utilizando n
nodos:
El número de nodos es $n = 11$, el itervalo $[a,b]=[-1,1]$ y la función $f$
$$ f(x)=\frac{1}{1+25x^{2}} $$Para interpolar utilizar las funciones numpy.polyfit
y numpy.polyval
.
En ambos casos dibujar los nodos, la función y el polinomio interpolador.
Comprobar que también funciona con 15 nodos y con la función:
$$ f(x)=e^{-20x^2} $$%run Ejercicio4.py
Podemos obtener la interpolación a trozos con funciones de scipy.
from scipy.interpolate import interp1d
f1 = interp1d(x, y, kind='nearest')
f2 = interp1d(x, y, kind='linear')
f3 = interp1d(x, y, kind='cubic')
xx = np.linspace(min(x),max(x))
De grado cero
plt.plot(x, y, 'ro', xx, f1(xx), '-')
plt.axis([min(xx)-d, max(xx)+d, min(yy)-d, max(yy)+d]);
De grado uno o lineal
plt.plot(x, y, 'ro', xx, f2(xx), '-')
plt.axis([min(xx)-d, max(xx)+d, min(yy)-d, max(yy)+d]);
De grado tres o cúbica
plt.plot(x, y, 'ro', xx, f3(xx), '-')
plt.axis([min(xx)-d, max(xx)+d, min(yy)-d, max(yy)+d]);
Y la interpolación con splines (con la opción not-a-knot) con una función de scipy
from scipy import interpolate
s = interpolate.InterpolatedUnivariateSpline(x, y)
xx = np.linspace(min(x),max(x))
yy = s(xx)
plt.figure()
plt.plot(x, y, 'ro', xx, yy)
plt.axis([min(xx)-d, max(xx)+d, min(yy)-d, max(yy)+d]);
Realizar una programa que, para la función $f(x) = \cos x$ en el intervalo $[0,10]$ tomando como nodos $x=0,1,\ldots,10$ dibuje los polinomios de interpolación:
y calcule, para los puntos
xx = np.linspace(0,10,100)
el error absoluto en cada caso. Para evaluar el error utilizar la función numpy.linalg.norm
.
%run Ejercicio5.py