#!/usr/bin/env python # coding: utf-8 # # De tíos, primos, teoremas y conjeturas # *Esta notebook fue creada originalmente como un blog post por [Raúl E. López Briega](http://relopezbriega.com.ar/) en [Mi blog sobre Python](http://relopezbriega.github.io). El contenido esta bajo la licencia BSD.* # De tíos, primos y conjecturas # ## El tío Petros y la conjetura # # Hace un tiempo atrás, quede atrapado en la lectura de la apasionante novela de Apostolos Doxiadis, [El tío Petros y la conjetura de Goldbach](http://www.amazon.es/El-tio-petros-conjetura-goldbach/dp/8440694903). La novela trata basicamente de la relación entre un joven, en busca de su vocación, y su tío, quien en el pasado fue un prodigio de las matemáticas, pero que luego se recluyó de su familia y de la comunidad científica consumido por el intento solitario de demostrar uno de los problemas abiertos más difíciles de la [teoría de números](https://es.wikipedia.org/wiki/Teor%C3%ADa_de_n%C3%BAmeros), la *[Conjetura de Goldbach](https://es.wikipedia.org/wiki/Conjetura_de_Goldbach)*. Lo que más me sorprendió del problema que consumió la vida del querido tío Petros, es lo simple que es su enunciado; la *[Conjetura de Goldbach](https://es.wikipedia.org/wiki/Conjetura_de_Goldbach)* nos dice que ***"Todo número par mayor que 2 puede escribirse como suma de dos números primos."*** # # Este enunciado, junto con la otra conjetura postulada por [Goldbach](https://es.wikipedia.org/wiki/Christian_Goldbach), conocida como la *[Conjetura debil de Goldbach](https://es.wikipedia.org/wiki/Conjetura_d%C3%A9bil_de_Goldbach)*, que nos dice que ***"Todo número impar mayor que 5 puede expresarse como suma de tres números primos."***; trae a colación el concepto de los *[números primos](https://es.wikipedia.org/wiki/N%C3%BAmero_primo)* como bloques constructores de los enteros. # ## Los números primos # # Pero, ¿cómo es esto de que los *[números primos](https://es.wikipedia.org/wiki/N%C3%BAmero_primo)* pueden ser considerados como bloques constructores de los números enteros? # # Un *[número primo](https://es.wikipedia.org/wiki/N%C3%BAmero_primo)* es un entero positivo que solo puede ser dividido en dos [factores](https://es.wikipedia.org/wiki/Divisibilidad) distintos, 1 y si mismo. De esta definición se desprende que el número 1 no es un *[número primo](https://es.wikipedia.org/wiki/N%C3%BAmero_primo)* ya que solo puede ser dividido en un solo [factor](https://es.wikipedia.org/wiki/Divisibilidad); en cambio, el número 2 si es *[primo](https://es.wikipedia.org/wiki/N%C3%BAmero_primo)*, el único *[número primo](https://es.wikipedia.org/wiki/N%C3%BAmero_primo)* que es par. # # La idea de que los *[números primos](https://es.wikipedia.org/wiki/N%C3%BAmero_primo)* pueden ser considerados como bloques constructores de los números enteros surge del enunciado del [Teorema fundamental de la aritmética](https://es.wikipedia.org/wiki/Teorema_fundamental_de_la_aritm%C3%A9tica) que nos dice que ***"Todo entero positivo puede ser representado de forma única como un producto de factores primos"***; así por ejemplo el número $28$ puede ser representado como $2^2 * 7$; o el $60$ como $2^2 * 3 * 5$. Este teorema es un concepto fundamental en [criptografía](https://es.wikipedia.org/wiki/Criptograf%C3%ADa), los principales algoritmos de cifrado que se utilizan hoy en día residen en la [factorización de primos](https://es.wikipedia.org/wiki/Factorizaci%C3%B3n_de_enteros), ya que es un proceso que requiere de mucho esfuerzo para calcularse mientras más grande sea el número que queremos factorizar. # # Otro aspecto interesante de los *[números primos](https://es.wikipedia.org/wiki/N%C3%BAmero_primo)* es que parecen surgir en forma aleatoria, hay veces que pueden aparecer en pares como (11, 13), (29, 31) o (59, 61) pero otras veces puede haber un largo intervalo entre ellos. Aún no se ha encontrado una formula que pueda predecir cual va a ser el próximo *[número primo](https://es.wikipedia.org/wiki/N%C3%BAmero_primo)*. Como parece ser bastante difícil encontrar un patrón en los *[números primos](https://es.wikipedia.org/wiki/N%C3%BAmero_primo)*, el esfuerzo de los matemáticos paso de intentar encontrar un patrón a intentar comprender la distribución de los *[números primos](https://es.wikipedia.org/wiki/N%C3%BAmero_primo)* dentro de todos los enteros; es decir, intentar responder la pregunta de ¿Cuál es la probabilidad de que un número sea primo si elijo un número al azar en el rango de 0 a N?. Uno de los primeros en dar una respuesta bastante aproximada a esta pregunta fue [Gauss](https://es.wikipedia.org/wiki/Carl_Friedrich_Gauss), quien con tan solo 15 años de edad propuso la formula $\pi(x)\approx\frac{x}{ln(x)}$ para responder a esa pregunta. # # La formula propuesta [Gauss](https://es.wikipedia.org/wiki/Carl_Friedrich_Gauss) implica que a medida que los números se hacen cada vez más grandes, los # *[números primos](https://es.wikipedia.org/wiki/N%C3%BAmero_primo)* son cada vez más escasos. Esto es lo que se conoce como [teorema de los números primos](https://es.wikipedia.org/wiki/Teorema_de_los_n%C3%BAmeros_primos). # A pesar de que los *[números primos](https://es.wikipedia.org/wiki/N%C3%BAmero_primo)* son cada vez más escasos mientras más grandes, siguen surgiendo indefinidamente, son infinitos; esto esta bien demostrado por el ya famoso [teorema de Euclides](https://es.wikipedia.org/wiki/Teorema_de_Euclides); quien incluyo una de las más bellas demostraciones de las matemáticas en su obra [Elementos](https://es.wikipedia.org/wiki/Elementos_de_Euclides) en el 300 AC. # # ### Encontrando los números primos # # Para encontrar todos los *[números primos](https://es.wikipedia.org/wiki/N%C3%BAmero_primo)* menores que un número N, uno de los algoritmos más eficientes y más fáciles de utilizar es lo que se conoce como la [criba de Eratóstenes](https://es.wikipedia.org/wiki/Criba_de_Erat%C3%B3stenes), el procedimiento que se utiliza consiste en crear una tabla con todos los números naturales comprendidos entre 2 y N, y luego ir tachando los números que no son primos de la siguiente manera: Comenzando por el 2, se tachan todos sus múltiplos; comenzando de nuevo, cuando se encuentra un número entero que no ha sido tachado, ese número es declarado primo, y se procede a tachar todos sus múltiplos, así sucesivamente. La siguiente animación describe el procedimiento graficamente. # criba de Eratostenes # # ### Ejemplo en Python # # Veamos un ejemplo en [Python](https://www.python.org/) de como implementar la [criba de Eratóstenes](https://es.wikipedia.org/wiki/Criba_de_Erat%C3%B3stenes) y un proceso de [factorización de primos](https://es.wikipedia.org/wiki/Factorizaci%C3%B3n_de_enteros). # In[1]: # Factorizando primos en Python import numpy as np def criba_eratostenes(n): """Criba Eratostenes""" l=[] multiplos = set() for i in range(2, n+1): if i not in multiplos: l.append(i) multiplos.update(range(i*i, n+1, i)) return l def factorizar_primos(n): """Factoriza un entero positivo en primos >>>factorizar_primos(28) [2, 2, 7] """ if n <=1: return "Ingresar un entero mayor a 1" factores = [] primos = criba_eratostenes(n) pindex = 0 p = primos[pindex] num = n while p != num: if num % p == 0: factores.append(p) num //= p else: pindex += 1 p = primos[pindex] factores.append(p) return factores factorizar_primos(28) # Factores primos de 28 # In[2]: # Factores primos de 1982 factorizar_primos(1982) # In[3]: # Factores primos de 2015 factorizar_primos(2015) # ## La conjetura de Goldbach y Python # # La *[Conjetura de Goldbach](https://es.wikipedia.org/wiki/Conjetura_de_Goldbach)*, es otro ejemplo de como también podemos construir todos los números enteros con simples operaciones aritméticas como la suma y no más que tres *[números primos](https://es.wikipedia.org/wiki/N%C3%BAmero_primo)*. Al día de hoy, la [conjetura](https://es.wikipedia.org/wiki/Conjetura_de_Goldbach) continua sin poder ser demostrada; y es considerada uno de los problemas más difíciles de las matemáticas. La mayoría de los matemáticos estiman que es verdadera, ya que se ha mostrado cierta hasta por lo menos el $10^{18}$; aunque algunos dudan que sea cierta para números extremadamente grandes, el gran [Ramanujan](https://es.wikipedia.org/wiki/Srinivasa_Aiyangar_Ramanujan) dicen que se incluía en este último grupo. # # Obviamente, como este blog esta dedicado a [Python](https://www.python.org/), no podría concluir este artículo sin incluir una implementación de la *[Conjetura de Goldbach](https://es.wikipedia.org/wiki/Conjetura_de_Goldbach)* en uno de los lenguajes que mejor se lleva con las matemáticas! # # En esta implementación vamos a utilizar tres funciones, en primer lugar una [criba de primos](https://es.wikipedia.org/wiki/Criba_de_Erat%C3%B3stenes) vectorizada utilizando [numpy](http://www.numpy.org/), para lograr un mejor rendimiento que con la [criba de Eratóstenes](https://es.wikipedia.org/wiki/Criba_de_Erat%C3%B3stenes) del ejemplo anterior; y luego vamos a tener dos sencillas funciones adicionales, una que nos devuelva la composición de Goldbach para cualquier número par que le pasemos como parámetro.(esta función solo nos va a devolver la primer solución que encuentra, ya que pueden existir varias soluciones para algunos enteros pares). Por último, la restante función va a listar los resultados de la *[Conjetura de Goldbach](https://es.wikipedia.org/wiki/Conjetura_de_Goldbach)* para un rango de números enteros. # In[4]: # La conjetura de Goldbach en Python import numpy as np def criba_primos(n): """Criba generadora de números primos. Input n>=6, devuleve un array de primos, 2 <= p < n """ criba = np.ones(n / 3 + (n % 6 == 2), dtype=np.bool) for i in range(1, int(n**0.5 / 3 + 1)): if criba[i]: k = 3 * i + 1 | 1 criba[k * k / 3::2 * k] = False criba[k * (k - 2 * (i & 1) + 4) / 3::2 * k] = False return np.r_[2, 3, ((3 * np.nonzero(criba)[0][1:] + 1) | 1)] def goldbach(n): """imprime la composición de goldbach para n. >>> goldbach(28) (5, 23) """ primos = criba_primos(n) lo = 0 hi = len(primos) - 1 while lo <= hi: sum = primos[lo] + primos[hi] if sum == n: break elif sum < n: lo += 1 else: hi -= 1 else: print("No se encontro resultado de la conjetura de Goldbach para {}".format(n)) return primos[lo], primos[hi] def goldbach_list(lower, upper): """Imprime la composición de Goldbach para todos los números pares grandes que 'lower' y menores o iguales que 'upper'. >>> goldbach_list(9,20) 10 = 3 + 7 12 = 5 + 7 14 = 3 + 11 16 = 3 + 13 18 = 5 + 13 20 = 3 + 17 """ # La conjetura se aplica a pares mayores que 2. if lower % 2 != 0: lower += 1 if lower < 4: lower = 4 if upper % 2 != 0: upper -= 1 for n in range(lower, upper + 1, 2): gb = goldbach(n) print("{0} = {1} + {2}".format(n, gb[0], gb[1])) # Goldbach entre 2000 y 2016 goldbach_list(2000, 2016) # In[5]: # Goldbach entre 9.999.980 y 10.000.000 goldbach_list(9999980, 10000000) # In[6]: # Goldbach entre 99.999.980 y 100.000.000 goldbach_list(99999980, 100000000) # Con esto termino, el que quiera puede divertirse intentando comprobar la *[Conjetura de Goldbach](https://es.wikipedia.org/wiki/Conjetura_de_Goldbach)*, aunque corre el riesgo de terminar desperdiciando su tiempo como el bueno del tío Petros en la novela. Espero que les haya parecido interesante el artículo. # # Saludos! # # *Este post fue escrito utilizando IPython notebook. Pueden descargar este [notebook](https://github.com/relopezbriega/relopezbriega.github.io/blob/master/downloads/pygoldbach.ipynb) o ver su versión estática en [nbviewer](http://nbviewer.ipython.org/github/relopezbriega/relopezbriega.github.io/blob/master/downloads/pygoldbach.ipynb).*