Importamos los módulos matplotlib.pyplot
, numpy
y las funciones de numpy
para polinomios.
import numpy as np
import matplotlib.pyplot as plt
import numpy.polynomial.polynomial as pol
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=\{(-1,1),(0,3),(2,4),(3,3),(5,1)\}$$
Los puntos los almacenaremos en matrices numpy (numpy arrays)
x = np.array([-1., 0, 2, 3, 5])
y = np.array([ 1., 3, 4, 3, 1])
Y dibujamos los puntos y el polinomio de Lagrange, que es el polinomio que pasa por todos ellos
xp = np.linspace(min(x),max(x))
p = pol.polyfit(x,y,len(x)-1)
yp = pol.polyval(xp,p)
plt.figure()
plt.plot(xp,yp,'b-', label = 'polinomio interpolante')
plt.plot( x, y,'ro', label = 'puntos')
plt.legend()
plt.show()
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})}{(x_{k}-x_{0})}\cdots \frac{(z-x_{k-1})}{(x_{k}-x_{k-1})} \cdot \frac{(z-x_{k+1})}{(x_{k}-x_{k+1})} \cdots \frac{(z-x_{n})}{(x_{k}-x_{n})} $$Por ejemplo, con nuestros 5 nodos, serían
$$ \ell_{0}\left(z\right)= \Bigg\rvert^{x_0} \frac{(z-x_1)}{(x_0-x_1)} \cdot \frac{(z-x_2)}{(x_0-x_2)} \cdot \frac{(z-x_3)}{(x_0-x_3)} \cdot \frac{(z-x_4)}{(x_0-x_4)} $$$$ \ell_{1}\left(z\right)= \frac{(z-x_0)}{(x_1-x_0)} \Bigg\rvert^{x_1} \frac{(z-x_2)}{(x_1-x_2)} \cdot \frac{(z-x_3)}{(x_1-x_3)} \cdot \frac{(z-x_4)}{(x_1-x_4)} $$$$ \ell_{2}\left(z\right)= \frac{(z-x_0)}{(x_2-x_0)} \cdot \frac{(z-x_1)}{(x_2-x_1)} \Bigg\rvert^{x_2} \frac{(z-x_3)}{(x_2-x_3)} \cdot \frac{(z-x_4)}{(x_2-x_4)} $$$$ \ell_{3}\left(z\right)= \frac{(z-x_0)}{(x_3-x_0)} \cdot \frac{(z-x_1)}{(x_3-x_1)} \cdot \frac{(z-x_2)}{(x_3-x_2)} \Bigg\rvert^{x_3} \frac{(z-x_4)}{(x_3-x_4)} $$$$ \ell_{4}\left(z\right)= \frac{(z-x_0)}{(x_4-x_0)} \cdot \frac{(z-x_1)}{(x_4-x_1)} \cdot \frac{(z-x_2)}{(x_4-x_2)} \cdot \frac{(z-x_3)}{(x_4-x_3)} \Bigg\rvert^{x_4} $$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(z\right)=y_{0}\ell_{0}\left(z\right)+ y_{1}\ell_{1}\left(z\right)+\cdots+y_{n}\ell_{n}\left(z\right) $$Escribir un programa que calcule los polinomios fundamentales de Lagrange. Para ello, crear una función lagrange_fund(k,x,z)
que tendrá:
k
del polinomio Fundamental, los nodos x
del problema de interpolación y un punto (o puntos, vector) donde vamos a calcular los valores del polinomio, z
.x
en el punto (o puntos, vector) z
.Dibujar todos los polinomios Fundamental de Lagrange.
Usar los nodos:
x = np.array([-1., 0, 2, 3, 5])
Nota:
k = 2
z = 1.3
yz = lagrange_fundamental(k,x,z)
print(yz)
y ahora sabemos que $\ell_{2}\left(1.3\right)=1.0448388888888889$. La segunda versión haría
k = 2
z = np.array([1.3, 2.1, 3.2])
yz = lagrange_fundamental(k,x,z)
print(yz)
y $\ell_{2}\left([1.3, \,2.1,\,3.2]\right)=[ 1.04483889,\,0.94395,\, -0.2688 ]$.
x
y sus correspondientes $y$s se pueden obtener como las filas de la matriz identidad:print(np.eye(len(x)))
%run Ejercicio1.py
Recordamos que 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(z\right)=y_{0}\ell_{0}\left(z\right)+ y_{1}\ell_{1}\left(z\right)+\cdots+y_{n}\ell_{n}\left(z\right) $$Escribir un programa que calcule el polinomio interpolante de Lagrange utilizando los polinomios fundamentales de Lagrange. Para ello, usar la función lagrange_fund(k,x,z)
y una funcion polinomio_lagrange(x,y,z)
que tendrá:
x
, y
del problema y un punto (o vector) a evaluar z
.z
.Dibujar el polinomio interpolante en el intervalo $[-1,5]$.
Usar los nodos:
x = np.array([-1., 0, 2, 3, 5])
y = np.array([ 1., 3, 4, 3, 1])
Comprobar que también funciona con los nodos en el intervalo $[-1,7]$:
x1 = np.array([-1., 0, 2, 3, 5, 6, 7])
y1 = np.array([ 1., 3, 4, 3, 2, 2, 1])
%run Ejercicio2.py
El inconveniente de este método es que si queremos incorporar un nodo nuevo tenemos que rehacer todos los cálculos.
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([-1., 0, 2, 3, 5])
y = np.array([ 1., 3, 4, 3, 1])
p = pol.polyfit(x,y,len(x)-1) # coeficientes del polinomio
xp = np.linspace(min(x),max(x))
yp = pol.polyval(xp,p) # valor del polinomio en los puntos de xp
Representamos el polinomio
plt.figure()
plt.plot(xp, yp, '-',x, y, 'o')
plt.show()
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_1(x)=\frac{1}{1+25x^{2}} $$Comprobar que también funciona con 15 nodos y con la función:
$$ f_2(x)=e^{-20x^2} $$Seguir los pasos:
Construir los nodos: los n nodos se almacenarán en un vector x
de n elementos en ambos casos. Para los nodos equiespaciados se pueden usar np.linspace
o np.arange
. Las y
correspondientes se obtienen utilizando f1
y f2
.
Construir los polinomios interpolantes: Usar las funciones pol.polyfit
y pol.polyval
para calcular los polinomios interpolantes.
Dibujar la gráfica: En ambos casos dibujar los puntos, la función y el polinomio interpolador. Usar plt.axis([-1.05, 1.05, -0.3, 2.3])
para que dibuje todas las gráficas en el rectángulo $[-1.05, 1.05]\times[ -0.3, 2.3]$. Usar más de 50 puntos (valor por defecto) en np.linspace
porque el polinomio interpolante oscila mucho.
%run Ejercicio3.py
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(z\right)= \left[y_{0}\right]+\left[y_{0},y_{1}\right]\left(z-x_{0}\right)+ \left[y_{0},y_{1},y_{2}\right]\left(z-x_{0}\right)\left(z-x_{1}\right)+\cdots +\left[y_{0},y_{1},\ldots,y_{n}\right]\left(z-x_{0}\right)\left(z-x_{1}\right)\cdots\left(z-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} $$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á:
x
, y
del problema de interpolación.y una funcion polinomio_newton(x,y,z)
que tendrá:
x
, y
del problema y un punto (o vector) a evaluar z
.z
.Escribir la matriz de diferencias divididas. Dibujar el polinomio interpolante en el intervalo de interpolación.
Usar los nodos:
x = np.array([-1., 0, 2, 3, 5])
y = np.array([ 1., 3, 4, 3, 1])
Comprobar que también funciona con los nodos:
x1 = np.array([-1., 0, 2, 3, 5, 6, 7])
y1 = np.array([ 1., 3, 4, 3, 2, 2, 1])
%run Ejercicio4.py
Podemos obtener la interpolación a trozos con funciones de scipy.
from scipy.interpolate import interp1d, CubicSpline
f1 = interp1d(x, y, kind='nearest')
f2 = interp1d(x, y, kind='linear')
f3 = CubicSpline(x, y, bc_type='natural')
xp = np.linspace(min(x),max(x))
De grado cero
plt.figure()
plt.plot(x, y, 'o', xp, f1(xp), '-')
plt.show()
De grado uno o lineal
plt.figure()
plt.plot(x, y, 'o', xp, f2(xp), '-')
plt.show()
De grado tres, un spline cúbico natural
plt.figure()
plt.plot(x, y, 'o', xp, f3(xp), '-')
plt.show()
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
xp = np.linspace(0,10,100)
el error absoluto en cada caso. Para evaluar el error utilizar la función Ea = np.linalg.norm(yp-f(xp))
.
%run Ejercicio5.py
Para los puntos
x = np.array([-1., 0, 2, 3, 5])
y = np.array([ 1., 3, 4, 3, 1])
si queremos calcular el polinomio que pasa por todos ellos, 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) $$es decir, $Vp=y$, donde $V$ es la matriz de coeficientes del sistema, Matriz de Vandermonde, $y$ contiene los términos independientes del sistema y la solución del sistema son los coeficientes del polinomio, contenidos en $p$.
Vandermonde(x)
que tenga como argumento de entrada el vector x
y como argumento de salida la matriz de Vandermonde V
.polVandermonde(x,y)
que tenga como argumento de entrada los valores de las coordenadas x
e y
de los nodos y como salida p
, que contiene los coeficientes del polinomio interpolante que pasa por los nodos, es decir, $p = (a_0,a_1,a_2,a_3,a_4)$. Para ello:V
llamando a la función Vandermonde
.Vp = y
. Usar la orden p = np.linalg.solve(V,y)
.xp = np.linspace(min(x),max(x))
nos da 50 puntos $x$ en el intervalo de interpolación.yp = pol.polyval(xp,p)
para evaluar el polinomio en un punto o puntos xp
a partir de los coeficientes del polinomio, p
, obtenidos en el apartado anterior y obtener sus valores yp
.Usar los nodos:
x = np.array([-1., 0, 2, 3, 5])
y = np.array([ 1., 3, 4, 3, 1])
Comprobar que también funciona con los nodos:
x1 = np.array([-1., 0, 2, 3, 5, 6, 7])
y1 = np.array([ 1., 3, 4, 3, 2, 2, 1])
%run Ejercicio6.py
Como resultados intermedios en este problema, podemos obtener la matriz de Vandermonde y los coeficientes del polinomio.
print('Matriz de Vandermonde V')
print(V)
print('Coeficientes del polinomio')
print(p)
Existe una función numpy
que calcula la Matriz de Vandermonde
va = pol.polyvander(x, len(x)-1)
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.