Buscamos obtener una aproximación de $f'$ en un punto $\hat x$ en función de valores de $f$ en un número finito de puntos cercanos a $\hat x$.
¿Por qué?
A partir de la definición de derivada
$$ f'(\hat x)=\lim_{h\to0}\frac{f(\hat x+h)-f(\hat x)}{h} $$podemos obtener tres aproximaciones o fórmulas en diferencias finitas
Diferencias finitas progresivas
$$ (\delta^+ f)(\hat x)=\frac{f(\hat x+h)-f(\hat x)}{h}$$Diferencias finitas regresivas
$$ (\delta^- f)(\hat x)=\frac{f(\hat x)-f(\hat x -h)}{h}$$Diferencias finitas centradas
$$ (\delta f)(\hat x)=\frac{1}{2}\big((\delta^+ f)(\hat x)+(\delta^- f)(\hat x)\big)= \frac{f(\hat x+h)-f(\hat x-h)}{2h}$$Tener en cuenta que si solo usamos una de estas fórmulas no podemos calcular la derivada al menos en un extremo del intervalo. Por ejemplo, si usamos la fórmula progresiva no podemos calcular $(\delta^+ % f)(b)$ porque necesitamos $f(b+h)$ que, en general, es desconocido.
Órdenes python Para los ejecicios propuestos es útil la orden np.diff(x)
que calcula $(x_2-x_1,x_3-x_2,\ldots,x_n-x_{n-1})$, y la orden np.linalg.norm(x)
que calcula la norma euclídea de un vector $x$, por ejemplo
Dada la función $f(x)=e^x$ en $[h,1-h]$ y para h=0.1
y h=0.01
.
df_p
, regresivas, df_r
, y centradas, df_c
.np.linalg.norm(x)
y la fórmula%matplotlib inline
from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
%run'Ejercicio1.py'
Vemos que la fórmula centrada da lugar a un error menor. Esto se debe a que las fórmulas progresiva y regresiva son de orden 1 y la fórmula centrada es de orden 2.
¿Es posible conseguir aproximaciones de orden 2 para $f'(a)$ y $f'(b)$? Si usamos un polinomio de interpolación de $f$ de $2^{o}$ grado en $x_{0}$, $x_{1}$, $x_{2}$, siendo $y_{j}=f(x_{j})$, el polinomio de interpolación en la forma de Newton es:
$$ p(x)=[y_{0}]+[y_{0},y_{1}](x-x_{0})+[y_{0},y_{1},y_{2}](x-x_{0})(x-x_{1}) $$con
$$ [y_{0}]=y_{0},\quad[y_{0},y_{1}]=\frac{y_{1}-y_{0}}{x_{1}-x_{0}}=\frac{y_{1}-y_{0}}{h}, $$$$ [y_{0},y_{1},y_{2}]=\frac{[y_{1},y_{2}]-[y_{0},y_{1}]}{x_{2}-x_{0}}=\frac{1}{2h}\big(\frac{y_{2}-y_{1}}{h}-\frac{y_{1}-y_{0}}{h}\big)=\frac{1}{2h^{2}}\big(y_{0}-2y_{1}+y_{2}\big). $$$$ [y_{0}]=y_{0},\quad[y_{0},y_{1}]=\frac{y_{1}-y_{0}}{h},\quad[y_{0},y_{1},y_{2}]=\frac{1}{2h^{2}}(y_{0}-2y_{1}+y_{2}). $$Si derivamos $p(x)$
$$ f'(x)\approx p'(x)=[y_{0},y_{1}]+[y_{0},y_{1},y_{2}]((x-x_{0})+(x-x_{1})), $$Sustituyendo $x$ por $x_0$ obtenemos una fórmula progresiva de orden 2
$$ f'(x_{0})\approx p'(x_{0})=[y_{0},y_{1}]+[y_{0},y_{1},y_{2}]((x_{0}-x_{0})+(x_{0}-x_{1})), $$$$ f'(x_{0})\approx\frac{y_{1}-y_{0}}{h}+\frac{1}{2h^{2}}\big(y_{0}-2y_{1}+y_{2}\big)(-h), $$$$ f'(x_{0})\approx\frac{-3y_{0}+4y_{1}-y_{2}}{2h}. $$Sustituyendo $x$ por $x_1$, la fórmula centrada que ya teníamos
$$ f'(x_{0})\approx p'(x_{0})=[y_{0},y_{1}]+[y_{0},y_{1},y_{2}]((x_{0}-x_{0})+(x_{0}-x_{1})), $$$$ f'(x_{0})\approx\frac{y_{1}-y_{0}}{h}+\frac{1}{2h^{2}}\big(y_{0}-2y_{1}+y_{2}\big)(-h), $$$$ f'(x_{0})\approx\frac{-3y_{0}+4y_{1}-y_{2}}{2h}. $$Sustituyendo $x$ por $x_3$ obtenemos una fórmula regresiva de orden 2
$$ f'(x_{2})\approx p'(x_{2})=[y_{0},y_{1}]+[y_{0},y_{1},y_{2}]((x_{2}-x_{0})+(x_{2}-x_{1})), $$$$ f'(x_{2})\approx\frac{y_{1}-y_{0}}{h}+\frac{1}{2h^{2}}\big(y_{0}-2y_{1}+y_{2}\big)(2h+h), $$$$ f'(x_{2})\approx\frac{y_{0}-4y_{1}+3y_{2}}{2h}. $$Que también podemos escribir de la forma:
$$ \begin{array}{l} \mathbf{Progresiva}\\ f'(\hat x)\approx \frac{1}{2h}(-3f(\hat x)+4f(\hat x+h)-f(\hat x+2h))\\ \mathbf{Centrada}\\ f'(\hat x)\approx \frac{1}{2h}(f(\hat x+h)-f(\hat x-h))\\ \mathbf{Regresiva}\\ f'(\hat x)\approx \frac{1}{2h}(f(\hat x-2h)-4f(\hat x-h)+3f(\hat x)) \end{array} $$Dada la función $f(x)=\dfrac{1}{x}$ en $[0.2,1.2]$, y h=0.01
.
%run Ejercicio2.py
A partir del polinomio de interpolación.
$$ p(x)=[y_1]+[y_1,y_2](x-x_1)+[y_1,y_2,y_3](x-x_1)(x-x_2) $$Derivando dos veces el polinomio anterior obtenemos
$$p''(x_2)=2[y_1,y_2,y_3]= \frac{1}{h^2}(y_1-2y_2+y_3)\quad (1)$$Dada la función $f(x)=\mathrm{sen}(2\pi x)$ en $[0,1]$ y h=0.01
:
Calcular su derivada segunda exacta en $[0,1]$ y su derivada segunda aproximada utilizando la fórmula (1) y el error relativo de la aproximación en el intervalo $[h,1-h]$.
%run Ejercicio3.py
Sea $\Omega =[a,b]\times[c,d]\subset \mathbb{R}^2$. Introducimos particiones uniformes
\begin{eqnarray*} a=x_1Podemos construir las fórmulas de forma análoga a como hicimos con las fórmulas para una variable. Por ejemplo
$\partial_x f(x_m,y_n)$
$$ (\delta_x^+f)(x_m,y_n)= \frac{f(x_{m+1},y_n)-f(x_{m},y_n)}{h_x} $$$\nabla f(x_m,y_n)=(\partial_x f(x_m,y_n),\partial_y f(x_m,y_n))$
$$ \left(\frac{f(x_{m+1},y_n)-f(x_{m-1},y_n)}{2h_x}, \frac{f(x_{m},y_{n+1})-f(x_{m},y_{n-1})}{2h_y}\right)\quad (2) $$$\mathrm{div} (\mathbb{f}(x_m,y_n))=\partial_x f_1(x_m,y_n)+\partial_y f_2(x_m,y_n)$
$$ \frac{f_1(x_{m+1},y_n)-f_1(x_{m-1},y_n)}{2h_x}+ \frac{f_2(x_{m},y_{n+1})-f_2(x_{m},y_{n-1})}{2h_y} \quad (3) $$$\Delta f(x_m,y_n)=\partial_{xx} f(x_m,y_n)+ \partial_{yy} f(x_m,y_n)$
$$ \frac{f(x_{m+1},y_n)-2f(x_m,y_n)+f(x_{m-1},y_n)}{h_x^2}+ \frac{f(x_{m},y_{n+1})-2f(x_m,y_n)+f(x_{m},y_{n-1})}{h_y^2} \quad (4) $$Pero antes de usar estas fórmulas, recordemos como se representan las funciones de dos variables, tanto escalares como vectoriales, en python.
Se representan las funciones de dos variables en coordenadas cartesianas en dominios rectangulares. Como en el caso de las funciones de una variable nosotros decidimos el número de puntos que utilizaremos en la representación.
Carguemos primero las librerías necesarias
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from mpl_toolkits.mplot3d import Axes3D
Si queremos representar la función escalar $f(x,y)=x^2+y^2$ en $x \in [0,5]$ con paso $h_x=1$ e $y \in [-2,2]$ con paso $h_y=1$, primero creamos la malla
h = 1.
x = np.arange(0., 5. + h, h)
y = np.arange(-2., 2. + h, h)
x, y = np.meshgrid(x, y)
y hemos creado dos matrices
print(x)
print(y)
Calculamos ahora los valores de $z=f(x,y)=x^2+y^2$ para cada par de $(x_{ij},y_{ij}) \quad i=1,\ldots,m,\,j=1,\ldots,n$ siendo $m$ y $n$ el número de filas y columnas tanto de x
como de y
. Operando elemento a elemento obtenemos una matriz z
de dimensiones $m\times n$
z = -(x**2 + y**2)
print(z)
Con estas tres matrices x
, y
, z
ya podemos representar la función
$f$.
fig = plt.figure(figsize=(8,6))
ax = fig.add_subplot(221, projection='3d')
ax.plot_surface(x, y, z, rstride=1, cstride=1, linewidth=0,
cmap=cm.coolwarm)
ax = fig.add_subplot(222, projection='3d')
ax.plot_wireframe(x, y, z)
ax = fig.add_subplot(223)
plt.contour(x, y, z)
ax = fig.add_subplot(224)
plt.contourf(x, y, z)
plt.colorbar();
plt.suptitle(r'$f(x,y)=-x^2-y^2$', fontsize=20);
Si queremos representar una función vectorial $\mathbb{f}=(f_1(x,y),f_2(x,y))$ podemos usar el comando quiver
f1 = lambda x, y: (x**2 + y**2)/3
f2 = lambda x, y: (x**2 - y**2)/2
plt.figure()
ax = plt.gca()
ax.quiver(x,y,f1(x,y),f2(x,y))
ax.set_xlim([-1,6])
ax.set_ylim([-3,3]);
La orden numpy
fy, fx = np.gradient(f,hx,hy)
devuelve el gradiente (calculado numéricamente) de la función escalar f
, usando los pasos hx
y hy
. Lo calcula usando una fórmula centrada de orden dos para los puntos interiores y diferencias de orden uno o dos, progresivas o regresivas en los bordes.
Por lo tanto, la matriz que devuelve tiene las mismas dimensiones que las matrices de los argumentos de entrada.
Calcular el gradiente de una función usando la orden numpy gradient
:
hx=0.1
y hy=0.1
.np.linalg.norm
.%run Ejercicio4.py
gradient
para las funciones $f_1$ y $f_2$, con hx=0.1
y hy=0.1
. Dibujar en la misma figura $\mathbb{f}$ y $\text{div} (\mathbb{f})$ np.linalg.norm
.%run Ejercicio5.py
%run Ejercicio6.py