"Ciencia es aquello que entendemos lo suficientemente bien como para explicárselo a un ordenador. Todo lo demás es arte." -- Donald Knuth --

El propósito de este blog es usar los ordenadores para entender algunos conceptos científicos. En cierto modo es una forma de contar cómo yo personalmente entiendo algunas cosas, cómo me he convencido de que son ciertas y por qué creo que son importantes.

Hay una gran diferencia entre saber que algo es cierto, y entender que lo es. Lo segundo requiere acostumbrarse al concepto, explorar las implicaciones y entender las situaciones en las que deja de ser cierto. En definitiva requiere jugar y desafiar, hasta que nos convencemos de que no puede ser de otra manera. En ese momento el resultado deja de ser algo aprendido, y se convierte en algo obvio... de repente resulta difícil recordar el momento y las razones por las que antes no estaba claro. Nunca he encontrado este proceso particularmente sencillo y los ordenadores me han ayudado desde hace mucho a andar este camino. Este blog le propondrá al lector una serie de entradas que tendrán de complemento códigos de ordenador escritos el python. El lector puede prepararse un laboratorio en su ordenador e investigar los problemas que vayamos analizando con ayuda de los códigos que yo aportaré y los que él mismo escriba.

Como usar este blog

Espero que este blog resulte entretenido a distintos niveles. En un primer nivel, espero que cualquiera pueda leer las entradas y disfrutarlas. Quedarse con el concepto que se está explicando y por qué es importante.

A otro nivel, el lector puede simplemente leer las entradas y ejecutar los programas. Con ello adquirirá una cierta intuición sobre los puntos explicados en el blog. No hay que olvidarse de preparar el laboratorio para ejecutar los programas de ejemplo.

Pero sin lugar a dudas el lector aprenderá muchas más cosas si se dedica a modificar los programas, e incluso crear los suyos propios. A lo largo de cada una de las entradas del blog yo mismo iré proponiendo algunas de estas modificaciones. Algunas serán triviales, mientras que otras pueden llegar a ser difíciles y llevarán algo de tiempo completarlas. Espero que a estos lectores los comentarios en el código le ayuden a entender lo que hace el programa, y cómo modificarlo para que haga otras cosas.

A la hora de ponernos a realizar nuestros propios programas hay que recordar siempre que el aprendizaje es un proceso de ensayo y error. Las dos cosas son importantes, y en la programación esto es aún más cierto que en otros campos. Es muy, muy complicado realizar un programa bien a la primera. Nos vamos a equivocar, muchas veces, y no pasa nada. Lo importante es adquirir la habilidad para darnos cuenta de que nos equivocamos. Hay que aprender a interpretar los resultados, escrutarlos y mirarlos desde todos los ángulos posibles. Algunos errores son mecánicos y no nos enseñan nada en particular, pero otros nos mostrarán que algunas de las cosas que creíamos ciertas no lo son, y esos errores valen su peso en oro, ya que impulsan el aprendizaje.

Un último consejo. Programar a veces resulta frustrante. Nosotros creemos que le estamos diciendo al ordenador que haga una cosa, y no la hace. No funciona. Nada tiene sentido. Uno llega a convencerse de que quizá la instalación está mal, o hay un error en python o en el procesador que no sabe multiplicar dos números. En estos momentos conviene volver a escuchar al gran Knuth:

"Los ordenadores son muy buenos siguiendo instrucciones, pero malísimos leyéndonos la mente."

Sí, a veces hay errores en python, y a veces los chips multiplican mal dos números. Pero después de 20 años programando yo nunca me he encontrado con estos casos extraordinarios. Siempre era yo el que estaba haciendo algo mal. A base de dedicarle tiempo uno termina solucionando los problemas.

Un poquito de programación

Para ejecutar los programas hay que seguir las instrucciones de la entrada preparando el laboratorio. En general el propósito de este blog no es enseñar a programar, aunque espero que los lectores aprendan algo leyéndolo.

Todos los programas los vamos a realizar en python, pero la sintaxis no es demasiado importante. Lo importante es saber programar: dividir un problema en una serie de instrucciones (condiciones y bucles) que den exactamente el resultado que estamos buscando.

Por una vez, y a modo de ejemplo, vamos a realizar juntos este proceso. Vamos a usar de inspiración una de las conjeturas matemáticas más famosas: la conjetura de Goldbach. Esta afirma que todo número par mayor que 4 es suma de dos primos. Parece ser cierta, ya que 6 = 3 + 3, 8 = 5 + 3, etc...

Vamos a hacer un programa que compruebe la conjetura de Goldbach. Pedirá un número (par) por pantalla N, y encontrará dos números primos que sumen N. Lo primero que vamos a hacer en todos nuestros programas es cargar un par de módulos con utilidades.


import numpy as np 
import matplotlib as mat 
import matplotlib.pyplot as plt 
import sys 

El módulo numpy esta lleno de utilidades matemáticas. Gracias a él podemos usar funciones como la raíz cuadrada, o los senos/cosenos. Por ejemplo, podemos escribir np.cos(0.0), a lo que devolverá 1.0, ya que el coseno de 0 es 1. Mas información de numpy en el manual, o mira este tutorial. Los otros módulos los usaremos más adelante.

¿Como organizamos el programa? Lo primero que necesitamos es saber cuándo un número es primo. Un número es primo si "es mayor que 1 y solo es divisible por él mismo y la unidad". Dicho de otra forma (pensarlo hasta convencerse), un numero es primo si no es divisible entre ningún número estrictamente menor que él y estrictamente mayor que 1. La siguiente función de python comprueba si un número es primo. La forma de hacerlo es comprobar el resto al dividir el número entre 2, 3, 4, ..., n - 1


# Definimos una funcion que devuelve true (verdad)
# si n es primo, y false en caso contrario.
def es_primo(n): 
    for i in range(2,n): # bucle i=2,3,...,n-1 
        if (n%i == 0):   # Si el resto al dividir n entre 
            return False # cualquier valor de i es 0, 
                         # n no es primo, devolvemos False
                         # y la rutina se acaba.
    
    # Si hemos llegado aqui, n no es divisible entre 
    # ningun valor de i, y por lo tanto es primo.
    return True 

Hay que notar que la identación (el número de espacios en blanco al principio de cada línea) es importante, ya que delimita los bloques de código (funciones, bucles y condiciones). Este tutorial puede ayudarnos a empezar con python.

Primero debemos probar esta función. Le decimos a python que nos imprima es_primo(2), y es_primo(13), y es_primo(121), y comprobamos que da los resultados correctos. Una vez convencidos de que la función realiza su labor, no la tocamos más. Guardamos la versión que funciona en lugar seguro, y usamos los resultados que nos da convencidos de que están bien. Esta es una parte importante de la programación. Programar funciones que realizan una tarea sencilla, y construir los programas complicados a partir de esas funciones sencillas.

Armados con una función que nos dice si un número es primo o no, lo tenemos muy fácil para terminar el pequeño proyecto. El programa principal solo tiene que pedir un número (lo llamamos n), y comprobar si se puede poner como suma de dos primos. Es decir, hacemos un bucle con una variable (i) desde 3 hasta n - 3, y comprobamos si tanto i como n - i son primos. Si lo son hemos ganado, ya que i + (n - i) = n.


for i in range(3,n-2):                           # Para i=3,4,...,n-3
    if ( (es_primo(i)) and (es_primo(n-i)) ):    # Si tanto i como n-i son primos
        print n, " = ", i , " + ", n-i           # Imprimimos y salimos
        raw_input('(Pulsa  para salir)')
        sys.exit(0)

El código completo que realiza la tarea se puede descargar pinchando aquí. (Ver la siguiente sección para entenderlo bien). Un ejemplo de ejecución deberia ser


Entra un numero par: 34494
34494  =  7  +  34487
(Pulsa  para salir)

Optimizaciones

En general no me voy a preocupar por la optimización. Los ordenadores son lo bastante rápidos como para que la velocidad de ejecución no sea un problema. El ejemplo que hemos construido juntos, es mejorable. Hay unas cuantas cosas obvias:

  • El bucle para determinar si un número es primo, no tiene que ir hasta n - 1, sino hasta la raíz cuadrada de n, ya que si un número es producto de dos, al menos uno de ellos será menor o igual que su raíz cuadrada.
  • El  bucle  que comprueba la conjetura  de  Goldbach (el que va desde 3 hasta n - 3), puede ir en pasos de 2, ya que es fácil darse cuenta de que los dos números  primos tienen que ser impares. Además el bucle puede terminar en n/2 + 1, ya que el resto del bucle solo daría el mismo resultado con los sumandos en orden inverso.

El código incluye estas y algunas otras pequeñas mejoras. El problemas es que siempre hay un compromiso entre hacer un código eficiente y hacer un código que se entienda fácilmente. Yo siempre voy a elegir la última opción. Una forma eficiente de comprobar la conjetura de Goldbach no se programa como lo hemos hecho. Eso se lo dejamos a los investigadores que se ocupan de ello, y yo no sabría ni por dónde empezar (bueno, sí, leyendo lo que han hecho los demás antes). El objetivo es aprender sobre el tema.

Po fueno, po fale, po malegro

Esta sección nos acompañará en todas las entradas del blog. En ella pretendo explicar el contexto del contenido de la entrada, así como dar referencias, proponer problemas para el que desee explorar más el tema y contar alguna anécdota. Será el último recurso para convencer al escéptico que crea que el tema que se ha tratado es irrelevante, poco interesante, o ambos.

No soy solo yo el que está convencido de que los ordenadores han cambiado la forma en la que entendemos la ciencia. Muchos campos de investigación tratan de temas que no sabemos resolver con papel y lápiz, donde los ordenadores cumplen una función esencial. Gracias a ellos tenemos resultados que no podemos obtener de otra forma. Esos resultados pueden inspirarnos a desarrollar modelos que los expliquen. El conocimiento avanza más rápido que si tuviésemos que buscar a ciegas.

De hecho algunos campos de investigación se ven lastrados por la falta de métodos numéricos eficientes y validos. El estudio de los superconductores de alta temperatura, o las propiedades del plasma de quarks y gluones a alta densidad (por mencionar solo un par) son problemas inabordables en un ordenador, debido a lo que se llama el problema del signo. Si alguien encontrase una forma eficiente de obtener predicciones de estos modelos en un ordenador, entenderíamos mucho mejor los superconductores de alta temperatura, el estado de la materia poco después del Big Bang y muchas otras cosas.

Ni siquiera la más pura de las matemáticas, donde el juego es demostrar teoremas, ignora el poder de los ordenadores, que han desempeñado un papel fundamental en la demostración del teorema de clasificación de grupos simples, o en el famoso teorema de los cuatro colores.

La conjetura de Goldbach lleva mas de 250 años sin resolver, y representa uno de los problemas más famosos de la teoría de números. Es protagonista de la película española "La habitación de Fermat", y este no es el único cameo que tiene en su haber. Es uno de esos problemas característicos de la teoría de números: puedes explicárselo a un niño de 12 años, pero nadie sabe resolverlo. En general se cree que la conjetura es cierta, aunque nadie ha sido capaz de probarlo. Olivier Ramaré demostró en 1995 que todo número par mayor que 4 es suma de a lo sumo 6 primos. Una versión débil de la conjetura de Goldbach (todo número impar es suma de 3 primos), implicaría que todo número par es suma de como mucho 4 primos. Recientemente el matemático Harald Helfgott afirma haber encontrado una demostración de esta versión débil de la conjetura.

La conjetura de Goldbach se ha comprobado numéricamente varias veces. Nils Pipping la comprobó en 1938 (¡a mano!) para todo número menor que 105 (algo que nuestro programa puede hacer en un rato). A día de hoy se sabe que es cierta para todos los números pares menores que 1018 (ver aqui). Resulta interesante que el número de formas distintas en las que se puede escribir n como suma de dos primos crece cuando n se hace más grande (hay más posibilidades). Por ejemplo, 50 se puede escribir como suma de dos primos de 4 formas distintas (sin contar el reordenamiento de los sumandos), mientras que 5000 se puede escribir como suma de dos primos de 76 formas distintas. Este es un argumento "probabilístico" a favor de que la conjetura es cierta, ya que parece poco probable que un número mayor que 1018 no tenga ninguna forma de descomponerse en suma de dos primos (hay argumentos de este tipo maś precisos). De hecho con un poco de esfuerzo, el lector puede modificar el programa de la entrada de hoy para que cuente de cuántas formas distintas un número par se puede escribir como suma de dos primos. Así podrá obtener un gráfico del cometa de Goldbach:

cometa.png

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)