No te olvides de preparar el laboratorio. Los programas de hoy son

Derivadas

Las funciones son probablemente el concepto matemático más utilizado: la posición de la tierra en su órbita alrededor del sol, el producto interior bruto de un país a lo largo del año o la cantidad de números primos menores que un determinado valor son conceptos que se pueden explicar con funciones. Una función es simplemente una "máquina" (o una regla) que asigna a un elemento de un conjunto (el input, o entrada) un único elemento de otro conjunto (la salida). El ejemplo más evidente es el de una función que transforma un número en otro, y son estas de las que nos vamos a ocupar hoy. Por ejemplo la función

f(x) = x2

transforma un número en su cuadrado. El resultado de aplicarle la función al número 2 (entrada) es f(2)=4 (salida).

La derivada de una función nos dice cómo varía la salida de una función cuando variamos la entrada. Si la derivada es muy grande el valor de la función cambiará mucho si cambiamos un poco la entrada. La derivada de la función f(x) en el punto x se denota como f'(x).

Por ejemplo el valor de la función f(x)=x2 en 1 es f(1)=1, y el valor de su derivada es f'(1)=2. Esto quiere decir que para saber el valor de la función en los puntos x=1.1, 1.04, 0.98, 0.87 (o cualquier otro punto cerca de 1) no necesito volver a calcular el valor de la función. Multiplicando el valor de la derivada por el desplazamiento obtendremos lo que cambia la función con una muy buena aproximación. Por ejemplo


f(1.1)  = f(1 + 0.10) = f(1) + f'(1)*0.1      = 1.0 + 2*0.1  = 1.2
f(1.04) = f(1 + 0.04) = f(1) + f'(1)*0.04     = 1.0 + 2*0.04 = 1.08
f(0.98) = f(1 - 0.02) = f(1) + f'(1)*(-0.02)  = 1.0 - 2*0.02 = 0.96
f(0.87) = f(1 - 0.13) = f(1) + f'(1)*(-0.13)  = 1.0 - 2*0.13 = 0.74

(el lector puede comprobar fácilmente que esos valores son buenas aproximaciones). Fíjense que para calcular el valor de la función en todos esos puntos solo he usado dos números: el valor de la función y de su derivada en el punto x=1. No necesito saber nada más: la función puede ser complicadísima, puede que nadie sepa calcularla exactamente o puede incluso que nadie sepa escribir una fórmula explícita para calcular valores de la función. Aun así con el valor de la función en un punto y de su derivada siempre sabré cuánto vale la función en un entorno de ese punto.

Con el programa desplaza.py podemos comprobar que estas afirmaciones son ciertas para cualquier función y cualquier desplazamiento. En realidad solo es cierto para funciones "diferenciables", y aunque el que lea esto pueda pensar que esto es la pescadilla que se muerde la cola, no es cierto... Los matemáticos son muy buenos resolviendo el problema del huevo y la gallina. En la línea 26 del programa aprovechamos esta propiedad de la derivada para calcularla. Básicamente miramos cuánto cambia la función al hacer un pequeño desplazamiento hacia la derecha, luego a la izquierda y usamos la media de los dos como estimación de la derivada:


h = 1.0e-6
der = (f(x+h)-f(x-h))/(2.0*h)

Espero no perder a muchos lectores con este párrafo. Es aún más impresionante que esta propiedad de la derivada sigue siendo verdad cuando se trata de una función compleja. Los números complejos (z=a+bi) se representan por puntos en un plano, con la parte real en el eje horizontal (a), y la imaginaria en el vertical (b). i es un número con la curiosa propiedad i2=-1, y en python se representa como 1j. Ahora hay infinitas direcciones en las que nos podemos desplazar de un punto. Podemos ir "hacia arriba" en el plano complejo, "en diagonal", etc... Aun así la derivada, un solo número, nos va a decir cómo varía la función en todas esas infinitas direcciones. De hecho lo podemos comprobar con nuestro programa desplaza.py (fijaos en que el valor de la derivada no cambia). Desplazándonos "hacia arriba":


Entra un numero: 1.0
Entra un desplazamiento: 0.0+0.1*1j

El valor de la funcion en  1.0  es  1.0  y su derivada  2.0

El valor de la funcion en                   (1+0.1j)  es  (0.99+0.2j)
Con la derivada predecimos que deberia ser  (1+0.2j)

Desplazándonos "en diagonal":


Entra un numero: 1.0
Entra un desplazamiento: 0.1+0.1*1j

El valor de la funcion en  1.0  es  1.0  y su derivada  2.0

El valor de la funcion en                   (1.1+0.1j)  es  (1.2+0.22j)
Con la derivada predecimos que deberia ser  (1.2+0.2j)

Linearizando problemas

En ciencia uno se enfrenta con frecuencia con problemas que no se pueden resolver de forma exacta. En esos casos sustituir una complicada función por la aproximación a esa función que nos da la derivada es una estrategia que muchas veces funciona.

Por ejemplo, imaginemos que queremos calcular la raíz de una función. Es decir queremos encontrar el punto (x) en el que la función se anula (f(x)=0). En general este es un problema imposible de solucionar de forma analítica (pruebe el lector a despejar x de la ecuación cos(x)-x = 0). Pero hay una estrategia posible. Empezamos en un punto cualquiera, digamos x=1.0, calculamos el valor de la función y su derivada. Ahora tenemos una aproximación de la función en un entorno de x=1.0 (una recta). Nadie sabe calcular raíces de una función general, pero calcular raíces de funciones lineales es fácil (donde corta una recta el eje horizontal). Así que podemos calcular dónde se anula la aproximación de la función.

Hay quien dirá que el ejemplo es muy muy malo, ya que no hay ningún motivo en absoluto para que la función y su aproximación lineal se anulen en el mismo punto. Es totalmente cierto, pero aun así el método será útil si siguiendo ese proceso nos acercamos a la raíz de la función. ¿Por qué?, pues porque podemos repetir el proceso iterativamente (volver a calcular la aproximación lineal de la función en el nuevo punto y su raíz), y acercarnos cada vez más a la verdadera raíz de la función. Esta es la base del método de Newton y la animación que Ralf Pfeifer ha colgado en la wikipedia lo ilustra de maravilla.

Nosotros podemos escribir un programa parecido. Tan solo hay que saber que si una función en x vale f(x), y su derivada f'(x), la raíz de la aproximación lineal viene dada por


x - f(x)/f'(x)

Y por lo tanto solo hay que escribir un programa que vaya reemplazando x por x-f(x)/f'(x). Al hacerlo obtendremos puntos para los que f(x) estará más y más cerca de cero. Cuando llegamos a un punto en que la función vale cero, la iteración no cambia el valor de x. Los matemáticos dirían que las raíces de la función son puntos fijos de la iteración (y podríamos usar esta propiedad para parar la iteración). El programa raiz.py nos muestra cómo la estrategia funciona, y la versión más completa hasta nos muestra un gráfico de la función y las sucesivas aproximaciones.

¿Qué sencillo, verdad? ¡Tan solo con la aproximación lineal a una función hemos resulto un problema muy difícil de resolver! Nuestra estrategia nos permite "despejar" x de la ecuación cos(x)-x=0: el programa raiz.py nos dice que x=0.739085133215. La función puede ser complicadísima, no lineal, pero aun así sucesivas aproximaciones lineales nos permiten dar con la solución.

Fractales de Newton

El demonio siempre está en los detalles. ¿Qué pasa si aplicamos nuestro método a una función que no tiene raíces, como por ejemplo f(x)=x2+1? En este caso nuestras iteraciones deambulan de un punto a otro sin converger. Quizás el lector piense que no se trata de un problema serio, ya que el método, por muy bueno que sea, no puede hacer milagros y encontrarle una raíz a una función que no tiene ninguna. El problema es que la función f(x)=x2+1 sí que tiene raíces, lo que pasa es que son complejas. Como hemos mencionado el número complejo i tiene la curiosa propiedad de que i2=-1, y por lo tanto i2 + 1 = 0. De hecho cualquier polinomio de grado n tiene n raíces complejas. De hecho, nuestro programa raiz.py encuentra estas raíces sin ningún problema, a no ser que empecemos las iteraciones en un número real.

Entra un numero: 1.0+0.1*1j
Iteracion 0  x= (1+0.1j) f(x)= (1.99+0.2j)
Iteracion 1  x= (0.00495049502275+0.0995049504937j) f(x)= (0.990123272228+0.000985197524315j)
Iteracion 2  x= (-0.24690130932+5.06222130342j) f(x)= (-24.5651242683-2.49973813576j)
Iteracion 3  x= (-0.118644704521+2.62964711918j) f(x)= (-5.90096740548-0.623987410898j)
Iteracion 4  x= (-0.0507610417758+1.50457686992j) f(x)= (-1.26117487414-0.152747778698j)
Iteracion 5  x= (-0.0141815602052+1.08422995225j) f(x)= (-0.175353472716-0.0307521446884j)
Iteracion 6  x= (-0.00105995238351+1.00319287977j) f(x)= (-0.00639483052471-0.00212667336806j)
Iteracion 7  x= (-3.36874870154e-06+1.00000452462j) f(x)= (-9.04923958012e-06-6.73752788767e-06j)
Iteracion 8  x= (-1.52422119658e-11+1j) f(x)= (-9.12381281637e-12-3.04844239317e-11j)
Iteracion 9  x= (-2.53236820889e-22+1j) f(x)= -5.06473641778e-22j
Iteracion 10  x= 1j f(x)= 0j

Por lo tanto hay ocasiones en las que nuestro método de Newton no funciona. Los números reales están a igual distancia de las dos raíces (como (-i)2+1 = 0, las dos raíces son i y -i). Las iteraciones deambulan por la recta real sin decidirse de cuál es la raíz a la que deberían converger (no saben si ir hacia arriba o hacia abajo). Uno podría pensar que esta situación excepcional se da cuando nuestro punto inicial se encuentra a igual distancia de todas las raíces, y cuando tengamos más de dos raíces estos puntos serán muy excepcionales.

Pero esto tampoco es cierto. Por ejemplo consideremos el polinomio f(x)=x3-x, que tiene tres raíces, x=-1,0,1. Si empezamos nuestras iteraciones en un determinado punto, digamos x=-2, ¿A cuál de las raíces nos acercaremos? Uno podría pensar que a la que está más cerca (en este caso x=-1), y si el lector modifica el programa verá que es cierto en este caso. Pero en general saber a qué raíz vamos a converger no es tan sencillo. Por ejemplo, el punto inicial x=0.466 converge a -1 (¡la raíz más lejana!), mientras que el punto x=0.465 converge a 1 (¡la segunda más lejana!).

La forma más intuitiva de ver la complejidad que esconde nuestro inocente método para encontrar raíces consiste en explorar a cuál de las raíces vamos a converger dependiendo del punto inicial. A modo de ejemplo consideremos la función f(x)=x3-1. Como todo polinomio de grado 3, tiene 3 raíces complejas: z=(-0.5-0.866025403784j), (-0.5+0.866025403784j) y 1. Vamos a empezar nuestras iteraciones en todos los puntos del plano complejo, y vamos a "pintar" el punto del plano complejo de un color dependiendo de cuál sea la raíz a la que converjan nuestras iteraciones. El programa newton.py hace exactamente eso, y el resultado (bastante impresionante), se puede observar en la figura de la derecha.

La complejidad presente en nuestro problema original (¿Cómo encontrar raíces de una función arbitraria?), que creíamos haber superado con el truco de "linearizar" el problema, de repente nos estalla en la cara. El comportamiento que nos indica esa figura es absolutamente impredecible. Hay puntos arbitrariamente cerca cuyas iteraciones divergen a puntos completamente distintos (caos determinista, o sensibilidad a las condiciones iniciales). Lo que es aún más grave, la frontera entre los puntos que convergen a una y otra raíz es complicadísima, hecha de una estructura que se repite ad-infinitum: el fractal de Newton.

El lector puede jugar con el programa newton.py con distintas funciones hasta convencerse de que este complicado comportamiento no es una excepción, sino más bien la norma.

Po fueno, po fale, po malegro

La propiedad de que con 2 números (el valor de la función y su derivada en un punto) podamos calcular cómo se comporta una función en un entorno de ese punto es el concepto clave de toda una rama de las matemáticas: el cálculo. El que esto siga siendo cierto en el plano complejo, donde el entorno de un punto tiene infinitas direcciones (la propiedad de "analiticidad") es la razón de ser de las condiciones de Cauchy-Rienmann, con las que más de un estudiante ha tenido pesadillas. Es una propiedad tan potente que permite resolver muchos problemas en dos dimensiones a base de identificarlas con las partes reales e imaginarias de un solo número complejo. Este truco se usa en dinámica de fluidos, electrónica, teoría cuántica de campos, física de la materia condensada, gravitación cuántica, topología, etc...

"Linearizar" problemas es una estrategia que muchas veces da frutos. Además se puede mejorar: en un principio tenemos en cuenta lo que varía la función (usando la derivada, tal y como hemos hecho), pero también podemos tener en cuenta lo que cambia la derivada (la derivada de la derivada, o derivada segunda), lo que cambia la derivada de la derivada (derivada tercera), etc... El método general se llama teoría de perturbaciones, pero tiene distintos alias: método de Hartree-Fock, diagramas de Feynman, o los antiguos epiciclos de los movimientos de los planetas, por ejemplo. Los problemas más interesantes son los que no podemos resolver, y por lo tanto, los que no se pueden resolver con el método perturbativo. El problema de los tres cuerpos o determinar las condiciones para el flujo laminar de un fluido son dos ejemplos importantes.

Los fractales son unos patrones con la propiedad básica de que son invariantes a escala (si los miramos en un microscopio, vemos una copia de él mismo). Muchos patrones naturales (formas de montañas, costas, o fluctuaciones en los precios de la bolsa o en la densidad de un fluido en su punto crítico) se parecen a los fractales, por razones que solo a veces entendemos bien. El método de Newton es muy útil en la práctica para determinar raíces de funciones complicadas, pero las imágenes del fractal de Newton nos enseñan que determinar su convergencia es muy difícil, y aplicarlo a ciegas no está exento de riesgos. El lector con ganas puede generar animaciones sorprendentes de fractales de Newton.

Alberto Ramos
Alberto Ramos

Investigador postdoctoral en DESY, Alemania

Sobre este blog

Ahora mismo usted está frente a un impresionante devorador de números, capaz de realizar más de 100.000.000.000 operaciones por segundo. Pero en este blog no nos vamos a ocupar de los ordenadores, porque a la ciencia le importan bien poco, al igual que no le interesan los tubos de ensayo ni los aceleradores de partículas. La ciencia trata sobre lo que podemos aprender usando estas herramientas. Veamos qué podemos aprender con los ordenadores.

Ver todos los artículos (9)