Desiframiento Rsa
Desiframiento Rsa
Desiframiento Rsa
Director
Yezid Enrique Donoso Meisel, Ph.D.
i
Prólogo
En este trabajo de tesis se detalla el modelaje, implementación y estudio de una posible forma
de descifrar RSA en un computador clásico basados en la Mecánica Cuántica. El fin es determinar
las vulnerabilidades inherentes al sistema de encripción de llaves públicas RSA, y ası́ poder dis-
cernir sobre su efectividad. El modelaje del algoritmo es hecho sobre la noción del potencial de la
Grid del departamento usando Globus Alliance y Condor Project, que permiten el uso de múltiples
procesadores con el fin de llegar a la solución en el menor tiempo. Se comparan los resultados del
algoritmo propuesto contra el mejor algoritmo existente para factorizar un número, y se discuten
las discrepancias las cuales incluyen la superioridad del mejor algoritmo clásico debido a la inefi-
ciencia de algunas operaciones que cuánticamente no se calculan explı́citamente como se hace en
el algoritmo adaptado a la computación clásica.
II
Índice general
Prólogo II
Introducción 1
2. Diseño y Especificación 11
2.1. Definición del problema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.2. Especificaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.3. Desarrollo del Diseño . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.3.1. Fuentes de Información . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.3.2. Diseño . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3. Implementación 17
3.1. Descripción de la Implementación . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.2. Implementación en C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.2.1. MCD-C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.2.2. Potenciación-C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.2.3. Exponenciación Modular-C++ . . . . . . . . . . . . . . . . . . . . . . . . 21
3.2.4. Orden-C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.2.5. Main-C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.3. Implementación con Librerı́a GMP . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.3.1. Orden-GMP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.3.2. Main-GMP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.4. Trabajos (jobs) en CONDOR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
4. Análisis y Validación 30
4.1. Pruebas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.1.1. Fase de Pruebas I . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.1.2. Resultados y Análisis I . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.1.3. Fase de Pruebas II . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4.1.4. Resultados y Análisis Fase II . . . . . . . . . . . . . . . . . . . . . . . . . 32
III
Comentarios Finales 36
Anexos 38
5.1. Gráficas de Resultados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
Bibliografia 61
IV
Indice de Códigos
1. MCD Binario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2. Exponenciación Binaria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3. Exponenciación Modular . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
4. Orden de f (x) = ax (mód b) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
5. Factorizar en primos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
6. Orden de f (x) = ax (mód b) usando GMP . . . . . . . . . . . . . . . . . . . . . 25
7. Factorizar en primos con GMP . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
8. Trabajos en CONDOR (moo.condor) . . . . . . . . . . . . . . . . . . . . . . . . . 28
V
Índice de figuras
VI
Índice de tablas
VII
Introducción
Kahn muestra uno de los métodos que más ha sido usado por criptógrafos para cifrar men-
sajes; llamados sistemas de llame simétrica o pública -también conocidos como de llave secreta.
Un ejemplo de éste método es reemplazar cada sı́mbolo en un texto plano por una combinación
arbitraria de letras y otros sı́mbolos; pero, antes que el intercambio del criptograma suceda con la
estructura mencionada previamente, debe haber un intercambio -de manera no cifrada- de la in-
formación necesaria para descifrar el mensaje. No se puede estar seguro de que el intercambio de
información no ha sido interceptado.
Sin embargo, estas deficiencias son eliminadas por medio del sistema de llaves asimétricas o
públicas, de modo que usa dos llaves. Por ejemplo, en el evento que Alice y Bob quieran trans-
mitirse información, la llave usada por Bob para cifrar la información no es la misma que Al-
ice usará para descifrar el criptograma recibido. El sistema asimétrico (RSA) es uno de los más
comúnmente usados en la actualidad para cifrar información. El inmenso esfuerzo que un espı́a
debe realizar con la ayuda de un computador clásico y ası́ encontrar las llaves para luego descifrar
el mensaje mediante la descomposición en sus factores primos un numero supremamente grande es
la fuente del éxito de RSA, ya que no hay un computador que factorice un numero de 400 dı́gitos
en pocos años.
Usando Mecánica Cuántica mostraremos cómo RSA puede ser descifrado mediante el aprovechamien-
to de efectos cuánticos en gran escala compleja como el enredamiento e interferencia. Lo que quer-
emos lograr entonces, es estudiar cómo por medio de la Mecánica Cuántica podrı́amos resolver
el problema de la factorización y aplicar una posible implementación de un algoritmo cuántico
adaptado a la Computación Clásica usando una Grid. Resultando en un posible mejoramiento al
comparar sobre el mejor algoritmo actual para factorizar un número.
Durante la etapa de diseño estudiamos los puntos claves para hacer eficiente el algoritmo. La
implementación la hicimos sobre C-Unix por razones de desempeño, durante la segunda etapa de la
1
implementación usamos unas librerı́as que manejan números mucho más grandes de lo que C nativo
puede. Aunque no logramos una mejora respecto al mejor algoritmo resultó interesante notar cómo,
siguiendo el método cuántico, con ciertos números especiales obtenemos un desempeño bastante
similar al mejor algoritmo clásico. La construcción del proyecto se sigue de acuerdo a:
En el capı́tulo 1 se da una descripción general sobre el estado del arte en el tema de factor-
ización de números, ası́ como el funcionamiento de RSA. Además, una breve introducción a
la computación cuántica.
En el capı́tulo 4 se muestran los análisis del programa hecho ejecutandose dentro de la grid.
Se finaliza con unos comentarios finales, en donde se concluye y se dan direcciones futuras
sobre esta investigación y su extensión.
2
Capı́tulo 1
Iniciamos el proyecto introduciendo el propósito del proyecto y el problema a resolver. Esto in-
cluye una descripción del sistema de encripción RSA, cómo funciona y el porqué de su efectividad
en contraste a los sistemas de encripción privados o de llaves simétricas. Además ilustraremos cómo
el potencial del algoritmo de encripción RSA puede ser fácilmente roto bajo regı́menes Mecánico
Cuánticos. Para esto, entonces mostraremos brevemente cómo se relaciona la Fı́sica con la Com-
putación lo cual, en principio, es poco notorio ya que usualmente se relaciona con las matemáticas.
Esto dará paso a explicar la teorı́a que está detrás de la computación cuántica y su enfoque a
la resolución del problema planteado. Además veremos cómo podrı́a enriquecerse el sistema de
encripción actual de tal modo que se vuelva a prueba de computadores cuánticos puede tornar in-
vestigación futura en el campo de la encripción.
Durante el estudio del sistema de encripción RSA se encuentran los principios necesarios para
que el sistema de encripción pueda funcionar apropiadamente, en esencia lo que se necesita en
teorı́a, es una Función de una sola dirección (o One-way Function, en Inglés) y una Función Trampa
(Trapdoor Function). Los conocimientos básicos que se necesitaran para entender el funcionamien-
to del sistema de encripción RSA no van más allá de aquellos aprendidos en un curso básico de
teorı́a de números; el fin es mostrar y describir los pasos que alguien debe seguir con el fin de
encriptar vı́a RSA ası́ como el proceso de des-encripción.
1.1. RSA
El proyecto va a estar dedicado a la des-encripción o el rompimiento del sistema RSA por
medio de técnicas distintas a la forma que se hace actualmente, las cuales van a ser detalladas más
adelante. El proyecto está enmarcado en un medio en el que la información es un bien, y como
tal representa valor para las personas u organizaciones que lo manejan. Estas entidades procuran
al máximo mantener una alta confidencialidad de la misma como ya lo mencionaba Kahn [1] y
estudiar los puntos de quiebre es de vital importancia ya que es allı́ en donde los maleantes van
a dirigir sus esfuerzos con tal de obtener información que no les corresponde. Muchos bancos
y/o entidades que manejen el dinero de otras personas, como tiendas en internet, subastas, entre
otros, usan el sistema de encripción mencionado, ya que ha demostrado ser de gran efectividad y
prácticamente imposible de romper.
3
1.1.1. ¿Qué es?
El algoritmo de encripción fue públicamente descrito en 1977 por Ronald Rivest, Adi Shamir,
y Leonard Adleman en el MIT. En su tiempo este fue el primer algoritmo -concreto- de encripción
de llave pública el cual es mejor que los sistemas de llave simétricos, los cuáles eran usados am-
pliamente en la antigüedad. 1 El sistema privado de encripción funciona mediante la sustitución de
sı́mbolos por sı́mbolos. Sin embargo, la información para descifrar el mensaje ya cifrado debe ser
transmitida o intercambiada entre los interesados en forma no cifrada; los interesados no pueden
estar seguros que la información no ha sido interceptada. Más aún clásicamente no podemos garan-
tizar la ausencia de un espı́a, ni cuantificar qué tanto obtuvo del mensaje.
Las deficiencias notadas en un sistema privado son eliminadas gracias al uso de llaves simétri-
cas, en el cual la llave que usa el interesado en cifrar la información no será la misma que usará el
remitente de la misma para descifrarla. RSA básicamente usa dos llaves, una pública y una privada;
la privada para descifrar y la pública para encriptar.
Las Funciones Trampa son Funciones en una dirección pero con una “trampa”, esto es infor-
mación adicional que puede desbloquear el problema inverso. En pocas palabras, una función es de
tipo trampa si:
2. Para una llave pública y, f (x, y) es vista como una función fy (x) de x que mapea n bits
a m (n) bits. Luego existe un algoritmo eficiente que, al tener como entrada hy, fy (x) , zi
produce un x0 tal que fy (x0 ) = fy (x), para alguna trampa z.
Estamos, entonces, interesados en una función E (p, Le ) que transforme el texto plano p en
uno encriptado c el cual resultarı́a de poco sentido para un observador, luego dicha función es una
función en un sentido. Por ejemplo, si Alice y Bob quieren comunicarse de manera segura, Alice
puede usar esta función para encriptar su mensaje antes de enviárselo a Bob. Claramente, Bob
necesita de la función inversa de tal forma que pueda recuperar el mensaje original, la cual serı́a
una función secreta D (E (p, Le ) , Ld ) obteniendo p. Es por esto que necesitamos una función tipo
trampa. El sistema de encripción RSA usa una Función trampa en un sentido basada en la aritmética
modular.2
1
De hecho es bastante intuitivo el sistema, y tenemos conciencia que es seguro, pues es una transformación que
se aplica a la información que se quiere transmitir entre un par o un grupo de personas y todos conocen la forma de
descifrarlo.
2
Veamos que f (x) = g x (mód p) es una función en una dirección bastante efectiva, teniendo un g fijo y con
módulo p, siendo primo. Esta función es bastante difı́cil de invertir (ya que no se conocen trampas). Sin embargo,
f (x) = xe (mód N ) con e fijo y con módulo, en donde N es compuesto, es una función en una dirección, y con e y
N especı́ficos tiene una trampa!
4
1.1.3. Funcionamiento
Supongamos que entre Alice y Bob quieren transmitirse información de manera segura. En
este caso va a ser Bob el que le va a enviar información a Alice, por lo tanto Alice necesita crear
sus llaves, tanto pública como privada y ası́ usar el criptosistema RSA [2]; ella usa el siguiente
procedimiento:
Ahora Bob cuenta con lo necesario para poderle mandar en forma segura su mensaje a Alice
usando como llave pública (e, N ). Suponemos que la longitud del mensaje de Bob es de blog (N )c
bits y que cualquier mensaje de longitud superior a esta cantidad, puede ser dividido en bloques de
blog (N )c y usar el mismo procedimiento. Para uno de estos bloques, Bob calcula:
E (M ) = E e (módN ) (1.1.1)
Ahora Bob tiene E (M ) que es el mensaje M cifrado, que ahora será enviado a Alice. Para que
Alice pueda descifrar el mensaje, debe usar en principio su llave Privada de des-encripción (d, N )
y elevar el mensaje a la d-ésima potencia:
D (E (M )) = E (M )e (módN ) (1.1.2)
De esta forma Alice recupera el mensaje y puede estar segura que, inclusive, si alguien tiene la
llave pública o supo cómo Bob pasó su mensaje original a números, un espı́a no podrı́a leer el men-
saje, ya que carecerı́a de coherencia dentro del lenguaje. Sin entrar en detalles vamos a suponer,
para que el sistema funcione, que siempre que D (E (M )) = M (módN ), en otras palabras, que
siempre que usemos la función de des-encripción, esta nos retornará el mensaje original.
Ejemplo
Con el fin de motivar la discusión daremos un ejemplo numérico. Supongamos que Bob quiere
mandarle un mensaje a Alice, M será 5678. Alice debe preparar lo necesario para que se transmita
por medio de RSA:
5
4. La llave Pública es (11, 13843). La llave Secreta será entonces (12371, 13843)
Ahora que Bob tiene esta información puede mandarle el mensaje c ya encriptado a Alice
usando la llave pública (11, 13843) y la ecuación 1.1.1:
c = M e (módN )
c = (5678)11 (mód13843)
c = 8029
Bob ahora puede mandar de manera segura el mensaje a Alice y esperar a que ella lo lea. La
forma en que Alice recupera el mensaje original es mediante la ecuación 1.1.2, ası́:
M = cd (módN )
M = (8029)12371 (mód13843)
M = 5678
Como hemos visto el sistema es bastante sencillo de implementar. En el caso que Bob quiera
mandar un mensaje que contenga letras lo que él deberı́a hacer es simplemente hacer un cambio
a números de cada una de las letras y la publique, no importa que sea público o privado. Ya que
si por ejemplo cambia cada letra por un número entonces bajo alguna transformación HOLA cor-
responderı́a a 5678. Vimos que luego de la encripción, el número resultante es 8029, si un espı́a
se obtiene tal número y además la transformación que hizo Bob para transformar HOLA en 5678
entonces va a obtener, claramente, un mensaje totalmente distinto.5
Un espı́a al darse cuenta de la situación descrita previamente estarı́a seguro que de nada le sirve
tratar de descifrar el mensaje con lo que usa Bob para volver texto en números. Como el algoritmo
para generar las llaves es conocido, entonces el espı́a se preocuparı́a por obtener la llave de des-
encripción privada (d, N ), y para esto necesita saber cómo hallar eficientemente d. Con ese valor,
ya vimos cómo des-encriptar, y usarı́a el mismo procedimiento 1.1.2.
Basándonos en la teorı́a, es fácil darse cuenta cómo Alice obtiene d. Uno de los métodos exis-
tentes6 , es usando Exponenciación Modular. En donde usando el Teorema de Euler tenemos que:
En el caso de Alice a juega el papel de e y d el inverso. Entonces lo que necesita el espı́a es poder
calcular ϕ (N ) y para esto son necesarios los factores primos de N , en otras palabras factorizar N .
Con estos factores, se vuelve trivial el cálculo de d y por ende la llave privada de des-encripción.
6
de la factorización eficientemente. Sin embargo, hay aproximaciones al problema de factorización
bastante rápidas aunque sin dejar una complejidad exponencial [3]. Los detalles de cómo funciona
el algoritmo conocido como “Number Field Sieve” esta por fuera del alcance de este trabajo, sin
embargo es remarcable notar su eficiencia ya que ha factorizado el número 2512 + 1 en cuatro meses
[4]. Más recientemente se ha logrado hacer una implementación usando los mismos principios
por Jason Papadopoulos [5] el cual ha podido ayudar a factorizar un número de 180 dı́gitos. Esta
implementación será la que usaremos para comparar lo que desarrollaremos a lo largo del proyecto.
El concepto actual de la computación proviene de las ideas del Matemático Alan Turing, en
particular el conjunto de computaciones de hoy en dı́a es el de la máquina abstracta Turing Univer-
sal la cual es el modelo de todos los computadores clásicos actuales. Es por esto que se considera
como una rama de las matemáticas. Sin embargo, extrayendo las ideas de Deutsch [6] que la teorı́a
de la computación siempre ha tenido bastante en común conceptualmente con la fı́sica.
Nuestro sistema, siendo el Computador, ejecuta un Cálculo. Cuándo hace un cálculo, este em-
pieza con una información de entrada la cual va a ser modificada de acuerdo a unas reglas que
rigen el sistema, que están fuertemente ligadas al hardware del computador. Por lo tanto, la salida
depende de la entrada y de las reglas que rigen operacionalmente al computador.
Por otra parte, los sistemas fı́sicos experimentan un movimiento o cambio entre dos estados o
tiempos; ya sea uno inicial y uno final, de acuerdo a unas leyes de movimiento que rigen el sistema.
Esto lleva a la conclusión a que el cambio de estado de un sistema fı́sico puede ser considerado
como un procesamiento de información, ya que el estado final depende de las condiciones iniciales
del sistema -estado inicial o entrada en un computador- y puede decirse que se procesa de acuerdo
a unas reglas o condiciones del mismo; en el caso de la computación está regida por el hardware y
los lı́mites que fı́sicamente imponen sobre el mismo. El análisis previo puede resumirse en la tabla
1.1.
Computación Fı́sica
Computador Sistema Fı́sico
Cálculo Movimiento
Entrada Estado Inicial
Reglas Leyes de Movimiento
Salida Estado Final
Tabla 1.1: Relación entre la Computación y la Fı́sica.
7
crecimiento del poder computacional se doblará a un costo constante aproximadamente una vez
cada dos años (ley de Moore) [7]. Nielen y Chuang dicen al respecto:
Es bastante motivante el hecho de poder hacer eficientemente cálculos por medio de la com-
putación cuántica como respuesta a las deficiencias presentes en su contra parte clásica. Como
mencionaré más adelante el problema de la factorización puede hacerse eficientemente en un com-
putador cuántico.
|1i , |0i .
Estos estados en la práctica forman una base, más estrictamente, una base computacional. Es
decir, cualquier estado posible puede ser hecho a partir de una combinación de estos dos. Un
fenómeno interesante de esto es que los estados |1i y |0i pueden superponerse; se denomina usual-
mente como Superposición Coherente
Algoritmo de Shor
El algoritmo de Shor es apropiado para factorizar un número N en sus factores primos, incluso
para el problema del logaritmo discreto. El algoritmo usa la ayuda de la Transformación Cuántica
7
Es la forma compacta de Quantum Bit, en inglés, o Bit cuántico.
8
de Fourier. Ahora presentaremos la forma básica del algoritmo para encontrar los factores de N
reduciendo el problema a la búsqueda del orden de la función f (x) = ax (mód N ).
El pseudo algoritmo es [8]
Consideraremos ahora el problema de encontrar el periodo r de una función f (x) [9], con
f (x + r) = f (x). Particularmente vamos a considerar el caso en el que r divide a N , siendo esta
el número de puntos sobre los que la función f (x) es evaluada, esto es N/r = m, con m entero
y N = 2n . En el caso que no lo divida exactamente agrega unas complicaciones al problema que
no consideraremos ya que no cambia la idea general de lo que describiremos. Necesitamos dos
registros el primero es la superposición de los 2n − 1 posibles estados, el segundo guarda la función
f (x), luego el estado total resulta
n
2 −1
1 X
√ |xi |f (x)i (1.2.2)
2n x=0
De lo que mencionábamos previamente, la diferencia entre la computación clásica y la cuántica
es el paralelismo presente inherentemente en este régimen. Por lo tanto en el segundo estado va es-
tar contenido la evaluación de f (x) para todo x en una sola iteración. Luego de la evaluación de la
función ambos registros resultan enredados, y además no tenemos acceso directo a la información
de todos los valores de la función f (x). Con el propósito de entender lo que sigue, voy a introducir
un concepto de la mecánica cuántica denominado como una observación o medida de un estado. En
pocas palabras observar un estado significa extraer toda la información contenida en ese estado en
particular perdiendo toda información adicional que se encuentre en la superposición de los demás
estados ya que lo hemos observado8 . Un ejemplo famoso que puede evidenciar este hecho es el
gato de Schrödinger, en el que luego de exponer a un gato dentro de una caja tapada, no se sabı́a si
el gato estaba vivo o muerto, estos son los estados del gato, estar vivo o muerto, y medir el estado
del gato lleva a un resultado, sea vivo o muerto, sin embargo la información distinta al resultado,
digamos vivo, se ha perdido.
Con lo anterior en mente cuando hacemos una medición sobre el segundo registro, este se
colapsa a un estado particular f (x0 ),
m−1
1 X
√ |x0 + jri |f (x0 )i (1.2.3)
m j=0
en donde m = N/r es el número de los valores de x tal que f (x) = f (x0 ). Ahora, puesto que
r es el periodo de f (x) entonces f (x0 ) = f (x0 + r) = f (x0 + 2r) = . . . = f (x0 + (m − 1) r).
8
Esta es una definición muy burda y solo pretende ayudar al lector a entender cómo funciona el algoritmo. Re-
comiendo el libro de Cohen-Tannoudji, Diu y Laloe para aquellos que quieran profundizar en el tema de la mecánica
cuántica desde lo básico hasta temas avanzados [10]
9
Ya que el barrido de la sumatoria afecta el primer registro, podemos ignorar el segundo, luego de
aplicar la transformación cuántica de Fourier, tenemos:
r−1
1 X 2πix0 k N
√ exp r k
r (1.2.4)
r k=0
Al medir el primer registro obtendremos un múltiplo de la forma λN/r del valor medido, dig-
amos c, esto satisface que c/N = λ/r, de la expresión conocemos N , y si λ y r no tienen factores en
común, es decir gcd (λ, r) = 1, entonces podremos reducir la fracción a una irreducible y entonces
determinaremos r.
10
Capı́tulo 2
Diseño y Especificación
Con el fin de hallar el periodo nos veremos enfrentados entonces a hacer eficientemente la
exponenciación modular, exponenciación, sacar el máximo común divisor y en general a manejar
valores de números inmensos en memoria para lograr el objetivo.
2.2. Especificaciones
Lo necesario para el algoritmo adaptado es que calcule eficientemente el máximo común divi-
sor, potenciación, potenciación modular entre números dados.
Requerimientos Funcionales
En primera instancia es necesario que el usuario interesado en averiguar los factores primos
de un número, ingrese manualmente el número por el cual necesita el algoritmo. Este número no
deberı́a en principio tener una restricción adicional a la que sea, entero y que además sea mayor
que 0.
Para que el algoritmo funcione, necesitamos calcular el máximo común divisor. Esto es aquel
número, máximo, que divide a un conjunto de números generalmente. Para nuestro problema única-
mente nos limitaremos a calcular el máximo común divisor entre un par de números enteros, ası́ co-
mo se aprecia en el algoritmo de Shor 1.2.1. Esta función, la de calcular el máximo común divisor,
se usa en la mayorı́a de los requerimientos, por tal razón es imperativo tenerla.
11
primero la exponenciación, ası́ como la vimos arriba, y luego sacar el módulo. Por ejemplo, 35
(mód 7), primero obtenemos el resultado de la exponenciación, 35 = 243 y ahora el módulo, 243
(mód 7) = 5. Aunque veremos que esta forma es válida, no es la mejor desde un punto de vista
computacional práctico.
Por último, es indispensable que la implementación del algoritmo se ejecute lo más rápido
posible, luego necesitamos medir el tiempo que toma la factorización.
Requerimientos No Funcionales
La implementación final debe ser fácil de usar. Ası́ como tener un mı́nimo de interacción con
el usuario, ya que se espera que para números grandes el sistema se demore en dar el resultado, en
otras palabras muy usable. También, es indispensable tener suficiente memoria RAM en el sistema
en donde serán almacenados temporalmente los resultados de los distintos cálculos durante el pro-
ceso. Y por último, debe existir un lugar en donde los resultados puedan persistir en forma fı́sica
para una posterior evaluación.
Las circunstancias en que el algoritmo podrı́a desarrollar respuestas falsas o inexactas proven-
drı́an de fallas en el sistema durante la ejecución del mismo, ya sea que se perturben los datos que
se manipulan de forma poco común o que se generen números aleatorios que no necesariamente
llevan a la respuesta y siempre se quede en un ciclo lo cual serı́a poco aceptable.
12
2.3.1. Fuentes de Información
Fase I
Con el fin de desarrollar el diseño teniendo en cuenta las especificaciones resaltadas en la sec-
ción pasada, decidimos consultar un libro que estudie las diversas formas de implementar un algo-
ritmo en particular. El libro consultado es The Art of Computer Programming [11], el cual se ajusta
a lo que necesitábamos.
Empezamos buscando la mejor forma de calcular el máximo común divisor entre un par de
números. Un clásico, es usar el Algoritmo de Euclides y aunque existe una versión moderna del al-
goritmo, este no provee la suficiente eficiencia a nivel computacional, ya que siempre vamos a tener
que dividir en algún punto, lo cual resulta costoso. Entonces, deberı́a existir un algoritmo que usara
operaciones que para un computador sean fáciles de ejecutar. Luego de buscar las diferentes formas
de calcular el máximo común divisor, nos encontramos con un método binario el cual está basado
únicamente en operaciones de resta, prueba de paridad, y dividir a la mitad números pares; lo que
corresponderı́a a un corrimiento hacia la derecha en notación binaria. [12]
En cuanto a la exponenciación Knuth nos muestra un algoritmo binario para elevar a cualquier
potencia positiva, un número. La idea es expresar el exponente en su representación binaria e irlo
leyendo de izquierda a derecha y tomar decisiones respecto a si el bit es par o no. Sin embargo,
como Knuth nos menciona este proceso es poco frecuente en un computador, luego el algoritmo
es modificado para que se lea de derecha a izquierda. Para que surta el mismo efecto el exponente
se divide en dos sistemáticamente y si este resulta ser par o no, se eleva al cuadrado la base o se
multiplica por sı́ mismo.
Fase II
CONDOR es un sistema de administración de cargas para trabajos intensivos computacional-
mente [13]. En adición, un usuario puede enviar trabajos paralelos o seriales por medio de CON-
DOR, estos trabajos pueden ser enviado basado en polı́ticas definidas por el usuario a un conjunto
de computadores que estén conectados por medio de CONDOR.
13
hace por medio de un intermediador que está diseñado particularmente para la creación de sistemas
Grid [14]. La combinación de estas dos tecnologı́as proporciona un ambiente descomunal para tipos
de trabajos en batch sobre múltiples computadores.
Por esta y otras razones, como por ejemplo la ayuda a muchas instituciones a desarrollar proyec-
tos en donde se aproveche el poder de múltiples supercomputadores haciendo cálculos en parale-
lo [15] [16], se han elegido estas dos herramientas para desarrollar este proyecto.
2.3.2. Diseño
Para determinar el diseño final elegido para el desarrollo del proyecto empezaremos por desvir-
tuar las alternativas que pasaron en consideración. Empezamos considerando el enfoque en el cual
queremos emular el comportamiento del paralelismo cuántico que se incluye en el algoritmo de
Shor; este considera el hecho de tener múltiples estados cuánticos por “separado” enredados dentro
de una gran función o estado.
Como vimos en la sección 1.2.1, cada estado dentro de la sumatoria nos representaba una can-
tidad considerable de información la cual existe, aunque no tengamos acceso a toda ella. Con el
fin de hacer más clara la idea, mostraré un ejemplo [17]. Vamos a factorizar el número 15 y hemos
obtenido como número aleatorio el 7:
1. Preparamos el estado total
15
1X
|xi |f (x)i
4 x=0
1
(|0i |1i + |1i |7i + |2i |4i + |3i |13i + |4i |1i + . . . + |15i |13i)
4
Agrupando estados iguales lo que está entre los paréntesis
14
A partir de los resultados anteriores se puede hallar el orden 1 . Sin embargo, el punto de la
discusión tiene como objetivo mostrar que si bien la gran cantidad de datos en un único registro
cuántico que contiene la información necesaria para calcular el orden implı́citamente dentro de este
régimen, a la hora de implementar el algoritmo sobre un computador clásico, este deberá hacer to-
dos los cálculos necesarios para determinar aquellas entradas que describimos en los dos últimos
numerales previos. Esto quiere decir que, además de tener que calcular la exponenciación modular
de TODOS los términos2 , vamos a tener que guardarlo para poder hacer aquella factorización de
estados que contenı́an el resultado de dicha operación y luego poder tener el orden. Esta aprox-
imación resultarı́a extremadamente costosa, ya que la cantidad de información que tenemos que
guardar es proporcional a N ası́ como el número de operaciones que deberı́amos hacer.
Por el hecho anterior fue que decidimos no implementar la solución del problema de esa man-
era. Tenı́amos la opción de implementar cada estado que se representara en el algoritmo dentro de
un computador por separado dentro de la Grid, y aunque esto se asemejarı́a bastante al paralelismo
manejado cuánticamente, lo ideal serı́a que cada computador hiciera el cálculo de la función selec-
cionando equitativamente la exponenciación a manejar. El problema de esta aproximación reside
en que finalmente todos los computadores van a tener que trabajar en lo mismo, y cuando N sea
de magnitudes significativas, simplemente gran parte de los cálculos hechos serán de poca utilidad
pues solo nos representa un estado adicional dentro del total.
Otra aproximación considerada para resolver el problema eficientemente es que todos los com-
putadores que hacen parte de la grid se comunicaran entre ellos durante la ejecución del algoritmo.
Sin embargo, como se dijo antes todos estarı́an trabajando para llegar al resultado y en la circun-
stancia en que el numero a factorizar sea muy grande puede que no sea fructı́fero en adición a las
situaciones previamente mencionadas.
Elección Final
El diseño final sobre el cual va a estar montado todo el proyecto se generó como respuesta
a las deficiencias que se presentaban previamente. Aunque el enfoque parezca exhaustivo intenta
dilucidar un punto importante de una de las operaciones que se realizan en el algoritmo de Shor; la
exponenciación modular. Como vimos, el problema de la factorización podı́a reducirse al de encon-
trar el periodo de una función y en el enfoque cuántico ı́bamos a tener que, finalmente, hacer tantas
operaciones como el número a factorizar. Luego, si finalmente vamos a realizar tales operaciones,
porqué no hallar el orden al vuelo y ası́ efectuar menos operaciones que el algoritmo cuántico solo
que siguiendo su perspectiva.
A manera de ejemplo, remitámonos al segundo paso del ejemplo anterior 2.3.2, si lo hiciéramos
de manera explı́cita como sugiere el algoritmo cuántico, tendrı́amos que hacer 15 cálculos antes de
al menos hacer el tercer paso, y encontrar finalmente el orden. Sin embargo, mientras los calcu-
labamos, nos hubiésemos dado cuenta del orden de la función para a = 7, ya que los estados
|0i |1i y |4i |1i cumplen con la definición de orden, e inclusive ocurrió a la cuarta operación, en
otras palabras el orden es 4. Habı́amos dicho que el orden de una función se comporta como el
periodo, aquel número que hace que la función devuelva un mismo resultado, en el ejemplo 70
1
Para obtener una visión más detallada sobre los pasos que se realizaron recomiendo [18] [19] y la fuente del
ejemplo [17]
2
Desde el a0 (mód N ) hasta aN −1 (mód N )
15
(mód 15) = 74 (mód 15) = 1 lo que nos sugiere que 4 es el orden de la función con ese a. Este
hecho es bastante significativo, ya que realizamos y realizarı́amos una cantidad inferior de cálculos
para encontrar el orden.
Con lo anterior en mente, añadimos un elemento extra; el hecho que dependiendo de la elección
de a podrı́amos llegar al resultado deseado, factorizar el número de entrada en sus factores primos,
aún mas rápido. El argumento considera que aunque exista una altı́sima probabilidad al tomar un
número al azar menor que N y que sean co-primos, también existe una buena probabilidad de que
no lo sean y por lo tanto obtendrı́amos factores no triviales directamente. La forma que usamos para
aproximarnos a esta solución es usar cada máquina de la grid por separado, trabajando conjunta-
mente para encontrar los factores primos, solo que todas parten con un a distinto. Como nuestro
interés reside en competir contra el mejor algoritmo actual, por lo tanto es indispensable terminar
(factorizar) lo más pronto posible usando el enfoque mecánico cuántico.
4. [k&1 = 0?] Esto pregunta si k es par, en caso afirmativo continua, de lo contrario se devuelve
al 1. Como es par, k ← k/2 y u ← ak − 1, v ← ak + 1.
Con el algoritmo ya creado, estamos preparados para la implementación, que como se men-
cionó en la introducción se realizará en C/C++ sobre Condor para enviar los trabajos sobre la Grid
hecha en Globus.
16
Capı́tulo 3
Implementación
Describiremos además, cómo usamos CONDOR para distribuir los trabajos en la GRID ası́ co-
mo las especificaciones de cada una de las máquinas dentro de la misma.
Etapas
Las etapas de la implementación fueron ordenadas por la importancia que representaban para el
proyecto, ası́ fue como empezamos la implementación eficiente del máximo común divisor entre un
par de números enteros positivos; es muy importante porque se usa finalmente para determinar los
factores. Luego implementamos la exponenciación, y la exponenciación modular como funciones
aparte. Con las funciones previamente definidas procedimos a implementar el cálculo del orden
de una función, en este caso de la función vista en el capitulo 1. Finalmente integramos todas la
funciones de acuerdo al pseudo algoritmo 2.3.2.
17
además, que aunque eficazmente varios algoritmos funcionan para determinar el máximo común di-
visor, en un computador clásico podemos eficientemente determinar el resultado ya que la división
entre 2, o la multiplicación por 2, inclusive determinar si un número es par o no corresponden
a operaciones básicas que el procesador maneja naturalmente como un corrimiento a la derecha,
izquierda, o AND.
11 if(u == 0 || v == 0)
12 {
13 return u|v;
14 }
16 while(pot==1)
17 {
18 if((u & 1)==0 && (v & 1)==0)
19 {
20 k++;
21 u>>=1;
22 v>>=1;
23 }
24 else
25 {
26 pot=0;
27 }
28 }
18
44 {
45 v = -t;
46 }
48 t = u - v; //restar
50 if(t != 0)
51 {
52 n1=1;
53 }
54 else n1=0;
55 }
56 }
57 else
58 {
59 t = u;
60 n1=1;
61 }
63 while(n1==1)
64 {
65 t>>=1; // dividir t en 2
66 n1=0;
83 t = u - v; //restar
85 if(t != 0)
86 {
87 n1=1;
88 }
89 else n1=0;
90 }
91 }
93 if(k != 0 )
94 {
19
95 while(n2 <= k)
96 {
97 p <<= 1;
98 n2++;
99 }
100 return p*u;
101 }
102 else return u;
103 }
Aunque el código anterior es relativamente más largo que una implementación del Algoritmo
de Euclides, las operaciones que ejecuta son realmente eficientes. Por ejemplo entre la lı́nea 16 y la
28, se usa el operador & de C para verificar que sea par el número, y >> para hacer un corrimiento
a la derecha en una unidad, es decir dividir entre 2.
3.2.2. Potenciación-C++
Ası́ como para la implementación nos basamos en Knuth, para la potenciación no sucede al-
go diferente. Esta potenciación como mencionamos en el capitulo anterior puede hacerse bastante
rápido una vez sabemos que lo único que debemos hacer es dividir entre 2 y multiplicar por la base
un número.
10 p = x;
11 r = 1;
12 while (n > 0)
13 {
14 if (n & 1){
15 r *= p;
16 n-=1;
17 }
18 p *= p;
19 n >>= 1;
20 }
22 return r;
23 }
Con esta forma de operar matemáticamente a dos números, la base y el exponente, está garan-
tizado que se ejecutan menos cálculos que de la forma tradicional (i.e. multiplicar la base tantas
veces como indica el exponente).
20
3.2.3. Exponenciación Modular-C++
El código 2 nos permite darnos cuenta de lo mencionado en la sección 2.3.1. Lo importante
es notar que en la lı́nea 15 y 18 del código 2 potemos directamente calcular el módulo y estar
seguros que el resultado nos dará el mismo al que obtendrı́amos luego de tener el resultado final
de la exponenciación y después tomar el módulo. Esto sucede porque la operación módulo forma
un grupo cı́clico y por lo tanto siempre que lo tomemos bajo la misma operación nos acercaremos
gradualmente al resultado final.
Código 3: Exponenciación Modular Binaria
1 /**
2 *Devuelve el resultado de calcular xˆ{n}mod(b), x, n y b
3 *son enteros positivos.
4 **/
5 long long int modPot(long long int x, unsigned n, long long int b)
6 {
7 long long int p;
8 long long int r;
10 p = x;
11 r = 1;
12 while (n > 0)
13 {
14 if (n & 1){
15 r *= p;
16 r = r %b;
17 n-=1;
18 }
19 p *= p;
20 p = p %b;
21 n >>= 1;
22 }
24 return r;
25 }
De esta manera tenemos una forma eficiente de calcular la exponenciación modular combinando
la teorı́a de grupos con el algoritmo binario para hacer la exponenciación.
3.2.4. Orden-C++
Por los argumentos que dimos en la sección 2.3.2 debemos calcular el orden usando la función
modPot hasta que encontremos el periodo de la función. Aunque el resultado lo obtengamos de
manera exhaustiva, es de esta forma que podemos lograr encontrar el orden usando menos cálculos
y menos memoria.
Código 4: Orden de la función f (x) = ax (mód b)
1 /**
2 *Devuelve en cont (contador) el orden de la función f(x)=aˆ{x}mod(b)
3 *a y b (b:= el número a factorizar) son enteros positivos.
4 **/
21
5 unsigned orden(long long int a, long long int b)
6 {
7 int i=1;
8 long long int res;
9 unsigned cont=0;
10 res=modPot(a,cont,b);
12 do
13 {
14 cont++;
15 res=modPot(a,cont,b);
17 }while(i!=res);
19 return cont;
20 }
Junto con las demás funciones definidas a lo largo del capı́tulo, es hora de implementar el
programa completo integrando todas las funciones y teniendo en cuenta el pseudo algoritmo de la
sección 2.3.2.
3.2.5. Main-C++
El siguiente es la implementación del programa para factorizar un número que es ingresado por
el usuario y este devuelve los factores.
Código 5: Factorizar en Primos
1 //para linux:
2 //compilar como gcc -o <nom ejecutable> <nombre.cpp> -lstdc++
3 //ejecutar como ./<nom ejecutable> <número a factorizar>
4 //#include "stdafx.h" //comentar esta lı́nea para que funcione en linux
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <iostream>
8 #include <math.h>
9 #include <time.h>
10 #include <unistd.h> //comentar esta lı́nea para que funcione en windows
11 #include <pthread.h> //comentar esta lı́nea para que funcione en windows
15 //
16 //
17 // Insertar las funciones previamente declaradas acá.
18 //
19 //
21 /**
22 *Devuelve por consola un par de factores que componen al número
23 *n=p.q ingresado por consola como argumentos al momento de llamar
24 *el programa ya compilado.
22
25 **/
26 int main(int argc, char* argv[])
27 {
35 while(tt==1)
36 {
37 //para tener un numero aleatorio bueno
38 time_t seconds;
39 time(&seconds);
40 srand((unsigned int) seconds);
42 u=1;
43 while(u==1||u==0||u==anterior) //nos aseguramos que el numero
generado
44 u = rand() %v;
//no sea el mismo que el anterior
46 anterior = u;
47 if(u==0) gd=v;
48 else if(u==1) gd=v;
49 else {
50 gd=gcd(u,v); //resultado de llamar a la función gcd
59 if((ord & 1)==0) //es necesario que el orden sea par, para que
funcione.
60 {
61 cout << "orden:" << ord <<endl;
62 ord>>=1;
63 f1 = potencia(u,ord)-1;
64 f2 = potencia(u,ord)+1;
65 f1 = gcd(v, f1);
66 f2 = gcd(v, f2);
68 if(f1==1||f2==v||f1==v||f2==1)
69 {
70 tt=1;
71 }
23
72 else
73 {
74 cout << "factor 1: " << f1 <<endl;
75 cout << "factor 2: " << f2 <<endl;
76 tt=0;
77 }
78 }
79 }
80 else if(gd==v)//el máximo común divisor es el numero a factorizar
mismo
81 {
82 tt=1;
83 }
84 else //es un factor trivial
85 {
86 f1 = gd;
87 f2 = v/gd;
88 cout << "factor 1: " << f1 <<endl;
89 cout << "factor 2: " << f2 <<endl;
90 tt=0;
91 }
92 }
94 return 0;
95 }
A la hora de usar GMP, es importante tener en cuenta que muchas de las operaciones que
nosotros definimos como funciones, es decir MCD, Potenciación y Potenciación Modular, ya se
hacen eficientemente en esta librerı́a. Por lo que únicamente nos esperaba implementar el cálculo
del orden, y el main con esta nueva librerı́a.
A la hora de usar GMP hay que tener unos cuantos aspectos presentes. El primero, toda vari-
able que se instancie debe ser inicializada explı́citamente, ası́ como debe ser igualmente des-
referenciada. Las funciones funcionan un poco distinto a como lo venı́amos haciendo, ya que el
valor en donde queda guardado el resultado es pasado por referencia.
3.3.1. Orden-GMP
Esta función básicamente emula el funcionamiento de la que creamos previamente implemen-
tado en el código 4. En este caso como argumentos los mismos números, solo que por referencia se
24
pasa la variable (ord) en la que quedará el resultado, es decir el orden de la función.
13 do
14 {
15 mpz_add_ui(ord, ord,1);//le suma a ord, una unidad.
16 mpz_powm(res,a,ord,b); //deja en res el resultado de (aˆorden)
mod(b)
18 }while(mpz_cmp_ui(res,1)!=0);
20 mpz_clear(res);
21 }
El uso de GMP simplificó significativamente la tarea de programar varias de las funciones que
usábamos en el programa que factoriza un número dado.
3.3.2. Main-GMP
Con este programa, obtenemos los factores primos que hacen a un número n = p ∗ q. Cabe
resaltar que la lógica es la misma que usamos en el código 5 e igualmente basados en 2.3.2.
10 /**
11 *Devuelve por consola un par de factores que componen al número
25
12 *n=p.q ingresado por consola como argumentos al momento de llamar
13 *el programa ya compilado.
14 **/int main (int argc, char* argv[]) {
16 //Variables generales
17 unsigned long int i, seed;
18 int tt=1;
19 //Variables de Tiempo
20 time_t seconds, ini, fin;
22 //variables GMP
23 /*u:= numero aleatorio, v:= numero a factorizar,
24 anterior:= numero aleatorio anterior, gcd := máximo común divisor,
25 ord := orden, f1,f2:= factores, r_state:= para generar numero aleatorio
26 */
27 mpz_t u,v, anterior, gcd, ord, f1, f2;
28 gmp_randstate_t r_state;
49 mpz_init(u);
50 mpz_init(gcd);
51 mpz_init(ord);
52 mpz_init(f1);
53 mpz_init(f2);
55 mpz_set_ui(u,1);
56 while(mpz_cmp_ui(u,1)==0||mpz_cmp_ui(u,0)==0||mpz_cmp(u,anterior)==0)
57 {
58 mpz_urandomm(u,r_state,v);
59 gmp_printf("numero aleatorio: %Zd\n", u);
60 }
62 mpz_set(anterior, u);
26
63 mpz_set_ui(ord,0);
64 mpz_gcd(gcd, u, v);
66 if(mpz_cmp_ui(gcd,1)==0)
67 {
69 orden(u,v,ord);
71 if(mpz_even_p(ord)!=0)
72 {
73 mpz_divexact_ui(ord,ord,2);
75 mpz_pow_ui(f1, u, mpz_get_ui(ord));
76 mpz_set(f2,f1);
78 mpz_sub_ui(f1,f1,1);
79 mpz_add_ui(f2,f2,1);
81 mpz_gcd(f1, v, f1);
82 mpz_gcd(f2, v, f2);
84 if(mpz_cmp_ui(f1,1)==0||mpz_cmp_ui(f2,1)==0||mpz_cmp(f1,v)==0||
mpz_cmp(f2,v)==0)
85 {
86 tt=1;
87 }
88 else
89 {
90 gmp_printf("Factor 1: %Zd\n", f1);
91 gmp_printf("Factor 2: %Zd\n", f2);
92 fin=clock();//terminamos medición del tiempo
93 tt=0;
94 }
95 }
96 }
97 else if(mpz_cmp(gcd,v)==0)
98 {
99 tt=1;
100 }
101 else
102 {
103 mpz_set(f1,gcd);
104 mpz_divexact(v,v,gcd);
105 mpz_set(f2,v);
106 gmp_printf("Factor 1: %Zd\n", f1);
107 gmp_printf("Factor 2: %Zd\n", f2);
108 fin=clock();
109 tt=0;
110 }
112 gmp_randclear(r_state);
27
113 mpz_clear(u);
114 mpz_clear(gcd);
115 mpz_clear(ord);
116 mpz_clear(f1);
117 mpz_clear(f2);
118 }
119 mpz_clear(v);
120 mpz_clear(anterior);
124 return 0;
125 }
Para cada una de las pruebas usamos el mismo archivo, en el cual se indica la ubicación del
ejecutable, el universo y bajo qué nombre y en qué lugar guarda los archivos de salida, de registro
y de error (Output, Log, Error). En estos archivos queda, lo que escribe el programa en la consola
(Output), el registro de qué máquina envió a cuál un trabajo, ası́ como su posterior resultado (Log),
y en caso de que el programa termine de manera no esperada registra este evento (Error). Además
el número de veces que enviará el trabajo a las distintas máquinas que componen la grid.
Código 8: Archivo .condor que indica a CONDOR cómo distribuir los trabajos.
1 #moo.condor
2 Universe = vanilla
3 initialdir = /usr/programa
4 Executable = /usr/programa/factor
8 Log = /usr/prueba/factorLog.$(process)
9 Output = /usr/prueba/factorOut.$(process)
10 Error = /usr/prueba/factorError.$(process)
12 Queue 15
En el código anterior, aparece $(process) esto indica que el archivo, ya sea de Log, Output
o Error, tendrá un número que indica el lugar en la cola en el que ése proceso en particular estaba.
28
Esto ayuda a asociar los archivos de salida más fácilmente.
29
Capı́tulo 4
Análisis y Validación
4.1. Pruebas
Con el fin de determinar la eficiencia de nuestra implementación, decidimos realizar la fac-
torización de distintos números basados en el siguiente criterio. Elegimos una serie de números
producidos a partir de un par de números primos, distintos a los triviales, es decir 2, 3, 5 o 7; en
general escogimos números primos grandes, que como producto de su multiplicación resultara uno
con una cantidad de dı́gitos mayor a la de estos números.
El proceso se realizó en dos fases. La primera consistió en factorizar números que no superaran
un orden de magnitud superior al millón, midiendo sus tiempos y asegurándonos que la respuesta
producida por el programa fuese la correcta. Con el fin de verificar tal respuesta, se usaron dos
métodos; en primera instancia nosotros creamos los números, por lo tanto sabı́amos de antemano
cuáles eran sus factores. En segundo lugar usamos al sitio web WolframAlpha [21] que es un Mo-
tor de Conocimiento Computacional, el cual presta un servicio web para consultas de todo tipo
optimizadas para cálculos matemáticos1 . Cuando introducimos un número de los elegidos para las
pruebas en el banner de búsqueda, este nos retorna una cantidad descomunal de información detal-
lada que se relaciona con el número ingresado; incluyendo sus factores primos, en caso de tenerlos.
Durante la segunda fase, buscábamos factorizar números mucho más grandes y que estuvieran
dentro del rango, en magnitud, del billón. La forma de verificar los resultados es consistente con
las descritas en el párrafo anterior. La razón de elegir tales números era poner a prueba nuestro
algoritmo con números cada vez más y más grandes, sin dejar a un lado sus logros a la hora de fac-
torizar números de tamaño menor al que se usarı́a en ámbitos profesionales, en donde el estándar
es manipular números de más de 150 dı́gitos, luego para el orden manejado en las pruebas el tiem-
po no deberı́a superar el segundo, en caso contrario podrı́amos esperar una cantidad de tiempo
desmesurada para números como el mencionado previamente.
30
presentes en la Tabla 4.1.
17897
40753
52907
88637
95129
97183
112451
126629
223733
333967
Con este archivo en la máquina maestro de CONDOR, iniciamos sesión con el usuario globus
y nos ubicábamos en la carpeta designada a este usuario conteniendo los comandos ejecutables de
CONDOR. Usamos el comando ./condor submit <ruta del Archivo .condor>. Tras
presionar Enter, el Maestro envı́a los trabajos a las tres máquinas disponibles, que están en modo
esclavo. Para acceder a los resultados, nos dirigimos a los directorios compartidos, definidos en el
código 8. Ejecutamos este mismo procedimiento con todos los número presentes en la tabla 4.1.
De la primera fase de pruebas, podemos ver que en ninguna de las pruebas, el tiempo supera
al segundo. La resolución elegida para medir el tiempo en el programa es en milisegundos. Dado
al rango elegido de los números a factorizar, podemos aseverar que en este rango existe una alta
probabilidad de encontrar los factores de un número, en menos de un segundo dependiendo del
número elegido al azar. Por ejemplo para factorizar al número 88637 (ver Tabla 4.1), en la mayorı́a
de los casos, el tiempo es menor que medio segundo, o 500 milisegundos, sin embargo cuando se
elige de forma aleatoria el número 10141, se demora mucho más que los demás números elegidos.
Esto es por dos razones que extraemos de los datos, la primera es que luego de las iteraciones cor-
respondientes con este número resulta un resultado invalidado por el programa y vuelve a elegir
aleatoriamente un número hasta que llegue al resultado esperado. En este caso particular luego de
14 iteraciones, elije al número 57242 el cual es co-primo con 886372 y eficazmente obtiene los re-
sultados esperados. Aun con este gran número de iteraciones a tan bajo nivel (i.e. número pequeño)
se hizo en un tiempo menor a un segundo. La segunda está directamente relacionada con el número
elegido, en caso que este luego de muchas iteraciones llegue al resultado, nos querrı́a decir que hay
una estrecha relación entre el número elegido y el orden de la función. Este argumento se sustenta
en todos los gráficos y los datos, aún cuando no hay iteraciones adicionales a la inicial, con algunos
números se llega al resultado más rápido que con otros aunque con una diferencia no muy notoria
2
Por lo tanto el resultado no es trivial.
31
y hasta despreciable, cuando tenemos que esperar menos de un segundo para obtener el resultado.
Los resultados en cuanto al tiempo, varı́an en la mayorı́a de los datos para esta primera fase.
Es decir, para un mismo número elegido al azar, el cálculo se realizó en un tiempo mayor a otro
mediante la elección del mismo número. Esto puede deberse a que durante una ejecución dentro de
un esclavo, los recursos no eran suficientes para efectuar la operación más eficientemente y eran
por consiguiente usados por el otro esclavo mientras hacia los cálculos para encontrar la solución.
Sin embargo, en todos los casos de esta primera fase, hay una diferencia de a lo sumo 10 milise-
gundos entre experimentos que eligen un mismo número aleatorio inicial para factorizar un número.
32
3080681
6980653
19783333
31363807
42446629
88544111
109822469
224929877
631563797
1176621989
el segundo en tiempo de ejecución, el cual era nuestro lı́mite durante las pruebas. De hecho, la
tendencia del tiempo que pasa a medida que incrementamos el número a factorizar, aumenta igual-
mente. Sin embargo, como lo notamos en la Fase I existen números que resultan de la elección
aleatoria con un tiempo reducido en cuando a la ejecución del programa para obtener los factores
aún cuando este número es co-primo con el número a factorizar, lo que finalmente hace el cálculo
más difı́cil. Lo interesante en este caso, es que estos números nos dan una visión más clara de la
ventaja de usarlos. Por ejemplo al factorizar el número 88544111 (ver Figura 4.2, en promedio
se demora el programa un tiempo de 94952 milisegundos para encontrar los factores aún si tiene
que iterar más de una vez hasta que encuentre los factores primos. Cuando el programa obtuvo
29865109 se demoró tan solo alrededor de medio segundo en obtener los números que componı́an
a 88544111.
Cuando la magnitud del número anteriormente escogido se duplicó, obtuvimos un resultado que
33
se sujeta a nuestras sospechas de la Fase I. El número a factorizar es 224929877 (ver Figura 4.3,
el tiempo mayor para encontrar los factores fue de casi media hora con 36373283 aleatoriamente
escogido. Sin embargo, cuando el programa eligió 43265289 se demoró tan solo 20 milisegundos,
superando todas nuestras expectativas.
Basados en estos hechos buscamos confrontar nuestros resultados con las demás pruebas y
aunque no siempre obtuvimos tiempos satisfactorios, se podı́a encontrar un elemento dentro de es-
ta fase que resultara con un tiempo mı́nimo de ejecución; respecto a los valores aleatorios obtenidos
por el programa.
A pesar de los resultados encontrados previamente, incluyendo la relación entre los números,
evidenciamos un problema al poner a prueba el programa para que intentara factorizar el número
1176621989 (ver Figura 4.4. Dentro de nuestro número de iteraciones en solo dos oportunidades
llegó al resultado esperado, uno de ellos con un tiempo cercano a los 20 minutos en tiempo de
ejecución y el otro a los 5 minutos. En el resto de pruebas se generaba un error informando que la
librerı́a GMP no puede alocar memoria. Esto en principio no nos generaba un indicio sobre lo que
realmente pasaba ya que considerábamos4 que la librerı́a llenaba a cabalidad nuestras necesidades.
Examinando el algoritmo rigurosamente e intentando dilucidar en qué punto podrı́a fallar, la re-
spuesta daba como origen el orden mismo. Aunque no la generación como tal, pero lo que sucedı́a
3
Esto se evidencia en la arquitectura del programa
4
Y aun lo hacemos.
34
luego de tenerlo.
Dentro del algoritmo y luego su implementación en 2.3.2 y 3.3.2 respectivamente, vimos que la
memoria que usan las operaciones que el programa implementa, como la exponenciación modular
y el máximo común divisor, no superan al número más grande dentro de las entradas del programa
y las generadas por el mismo. El número más grande que el programa maneja es aquel ingresado
para ser factorizado n y el aleatorio a siempre será menor que este. Más aún, el orden puede ser
tan grande como n siendo n − 1 el valor que tome en el peor de los casos. Esto refleja un problema
dentro del algoritmo, ya que en el peor de los casos va a terminar haciendo una exponenciación -a
la hora determinar los factores teniendo previamente el orden-, de un número casi tan grande como
n y elevándolo a un número tan grande como este; ocupando una gran cantidad de espacio junto
con lo que ya venı́a haciendo.
Con el fin de verificar este hecho decidimos implementar un programa que únicamente se en-
cargara de ejecutar la exponenciación entre dos números indicando su base y el exponente, usando
la librerı́a GMP. Evidenciamos que al efectuar esta operación con números del orden mencionados
previamente, el tiempo consumido era muy significativo y el tamaño de lo que imprimı́a como re-
sultado superó por mucho lo que esperábamos. El manejar tal cantidad de información se vuelve
poco práctico ya que se mantienen en memoria ambos números que toca operar, y ası́ sacar los
factores primos del número a factorizar.
35
Conclusiones y Comentarios Finales
Conclusiones
Durante este trabajo nos preocupamos por implementar eficientemente una versión adaptada
del algoritmo de Shor, que como describimos, dentro del régimen cuántico resuelve el problema
de la factorización de un número eficientemente; de manera más rápida que el mejor algoritmo
clásico que conocemos5 . Lo anterior con el fin de analizar la efectividad con la que el sistema de
encripción de llaves públicas RSA funciona y su vigencia que indiscutiblemente mantiene actual-
mente. Su efectividad como vimos reside en el hecho de cuán intratable es clásicamente factorizar
un número de magnitudes colosales. Por lo cual creemos que por una cantidad considerable de
tiempo seguirá siendo la elección preferida por muchos a la hora de encriptar mensajes entre un par
de interesados.
Durante la fase de pruebas buscábamos encontrar los factores de los números propuestos en
sus factores primos lo más rápido posible. Establecimos un tiempo lı́mite el cual considerábamos
crı́tico a la hora de evaluar el rendimiento de la implementación realizada. En base a este obtuvimos
resultados que nos condujeron a afirmar que el orden iba a depender del a inicialmente elegido. Por
lo tanto, buscar estos números que como resultado permiten que el algoritmo devuelva la respuesta
buscada de forma rápida, es una tarea en la cual podrı́a profundizarse. Encontramos además, que
aunque con el método del orden de manera eficaz encontramos la factorización prima, este número
-el que nos indica el periodo de la función- puede ser tan grande como el número que buscamos
5
Recomiendo referirse a Case [23] para aquellos que estén interesados en saber el funcionamiento de este algoritmo.
36
factorizar. Esto genera un problema a nivel práctico ya que uno de los pasos dentro del algoritmo
es hacer una exponenciación, la cual en el peor de los casos va a terminar generando un número
con billones de dı́gitos. Indicándonos que vamos a estar limitados por la memoria principal del
computador y, claramente por el tamaño del número a factorizar.
Durante la fase de pruebas, tanto la I como la II, se produjo en todos los experimentos resultados
que conducı́an a la identificación de aquellos factores primos que componı́an a los números elegidos
durante este par de etapas, menos en un caso. En este último solo fue satisfactoria en un par de
oportunidades, pero nos ayudó a dilucidar un problema que, como ya lo mencionamos, está presente
en el algoritmo y amerita un estudio futuro con el fin de abordar este problema el cual para un
ciertos números produce resultados favorables, en otras palabras el algoritmo es eficaz y eficiente
con estos números en particular. El hecho de tener complicaciones al manejar números muy grandes
en un único computador nos sugiere que el algoritmo podrı́a ser igualmente práctico si lográramos
manejar números mucho más grandes mientras incorporamos aquellos números que generan un
orden pequeño. Esto podrı́a hacerse intercomunicando procesos entre computadores y ası́ el tamaño
y las operaciones de la memoria podrı́an compartirse para lograr manejar números más grandes
que los ya manejados por GMP. Este nuevo diseño no cambiarı́a drásticamente la elección hecha
inicialmente, ya que finalmente vamos a designar a varios computadores dentro de la grid a que
calculen por separado los factores, solo que en este caso trabajarı́an un par o más computadores,
compartiendo los recursos.
Trabajo Futuro
Hemos seleccionado una lista que consideramos esencial a la hora de continuar en este proyecto:
6
Respecto al número a factorizar.
37
Anexos
38
Iteraciones nFinal Aleatorio Tiempo
1 15374 3310 20
15877 0
3764 0
3764 0
1944 0
15877 0
13921 10
1944 0
15877 0
1944 0
13921 0
1 15301 11083 10
1 17711 17046 20
13921 0
1 15301 11083 10
Tabla 5.3: Resultados para Factorizar 17897
39
Figura 5.6: Tiempo para Factorizar 40753
40
Iteraciones Número Final Número Aleatorio Tiempo
16599 0
17273 20
17 25727 23796 700
17273 20
16599 0
20836 20
1 9986 31502 80
20836 20
1 25727 21257 40
1 9986 31502 80
20836 30
1 25727 21257 40
1 9986 31502 80
19689 30
1 25727 21257 20
Tabla 5.4: Resultados para Factorizar 40753
41
Iteraciones Número Final Número Aleatorio Tiempo
37334 10
771 0
1 15159 26016 0
1 15159 26016 10
28278 30
3439 0
28278 30
14624 10
3439 0
28278 30
14624 20
3439 0
52030 40
14624 10
29685 50
Tabla 5.5: Resultados para Factorizar 52907
42
Iteraciones Número Final Número Aleatorio Tiempo
5 45242 30743 410
7 16684 29064 200
2 38143 2355 170
77972 70
1 38143 75858 130
77972 60
1 57242 9685 50
70858 90
16684 60
45242 2355 140
16684 60
14 57242 10141 940
1 45242 2355 140
16684 60
Tabla 5.6: Resultados para Factorizar 88637
43
Iteraciones Número Final Número Aleatorio Tiempo
1 88528 64891 200
24289 20
82535 10
11224 80
1 55350 79371 150
82535 0
1 10153 41492 20
84004 100
84004 90
19 55350 73535 910
71880 10
71880 20
11224 90
11224 90
71880 10
Tabla 5.7: Resultados para Factorizar 95129
44
Iteraciones Número Final Número Aleatorio Tiempo
59711 0
63482 0
36270 61593 10
87578 0
8633 20
87578 0
57619 72435 10
25032 20
25032 30
36270 61593 10
56711 10
56711 30
87578 0
36270 61593 10
56711 30
Tabla 5.8: Resultados para Factorizar 97183
45
Iteraciones Número Final Número Aleatorio Tiempo
91123 130
77064 80
28257 80
109905 30
43991 20
110865 0
28257 80
46296 110
59601 80
64140 120
46296 120
59601 80
64140 120
109905 20
64140 120
Tabla 5.9: Resultados para Factorizar 112451
46
Iteraciones Número Final Número Aleatorio Tiempo
118389 30
1 33431 12363 100
1 48473 105228 140
25569 10
1 48473 105228 1470
1 33431 12363 100
25569 20
101 48643 60479 800
1 33431 12363 100
25569 0
25569 0
1 116639 108391 20
1 60707 46654 10
1 116639 108391 20
1 116639 108391 20
Tabla 5.10: Resultados para Factorizar 126629
47
Iteraciones Número Final Número Aleatorio Tiempo
112986 0
150654 10
141997 10
150654 20
109553 130
1 40424 40325 20
150654 20
1 40424 40325 30
109553 110
150654 20
1 220751 52681 80
1 40424 40325 20
37042 110
1 220751 52681 90
55486 0
Tabla 5.11: Resultados para Factorizar 223733
48
Iteraciones Número Final Número Aleatorio Tiempo
270195 0
157544 0
19180 100
264895 0
270195 0
19180 100
264895 0
200759 40
264895 0
200759 30
59091 120
264895 0
200759 40
192901 0
59091 80
Tabla 5.12: Resultados para Factorizar 333967
49
Iteraciones Número Final Número Aleatorio Tiempo
386012 480
179860 0
2758540 150
179860 0
2758540 150
179860 0
3053322 950
2502374 60
743737 20
2502374 60
743737 20
743737 20
2502374 70
2732003 30
132657 300
Tabla 5.13: Resultados para Factorizar 3080681
50
Figura 5.16: Tiempo para Factorizar 6980653
51
Figura 5.17: Tiempo para Factorizar 19783333
52
Figura 5.18: Tiempo para Factorizar 31363807
53
Figura 5.19: Tiempo para Factorizar 42446629
54
Figura 5.20: Tiempo para Factorizar 88544111
55
Figura 5.21: Tiempo para Factorizar 109822469
56
Figura 5.22: Tiempo para Factorizar 224929877
57
Figura 5.23: Tiempo para Factorizar 631563797
58
Iteraciones Número Final Número Aleatorio Tiempo
198973529 284580
1138410736 1180520
Tabla 5.22: Resultados para Factorizar 1176621989
59
Bibliografı́a
[1] D. Kahn, The Codebreakers: The Story of Secret Writing. Scribner, New York, 1996, p. 77.
[2] M. A. Nielsen and I. L. Chuang, Quantum Computation and Quantum Information, 1st ed.
Cambridge University Press, October 2000, pp. 641–643.
[3] D. E. Knuth, Art of Computer Programming, Volume 2: Seminumerical Algorithms (3rd Edi-
tion) (Art of Computer Programming Volume 2), 3rd ed. Addison-Wesley Professional,
November 1997, p. 403.
[4] A. K. Lenstra, H. W. Lenstra, M. S. Manasse, and J. Pollard, “The factorization of the ninth
fermat number,” Mathematics of Computation, vol. 61, no. 203, pp. 319–349, 1993. [En
Lı́nea]. Disponible: http://www.jstor.org/stable/2152957
[5] Integer factorization source code. Accedido el 10 de Noviembre de 2009. [En Lı́nea].
Disponible: http://www.boo.net/∼jasonp/qs.html
[6] D. Deutsch, A. Ekert, and R. Lupacchini, “Machines, logic and quantum physics,” 1999. [En
Lı́nea]. Disponible: http://www.citebase.org/abstract?id=oai:arXiv.org:math/9911150
[7] M. A. Nielsen and I. L. Chuang, Quantum Computation and Quantum Information, 1st ed.
Cambridge University Press, October 2000.
[8] ——, Quantum Computation and Quantum Information, 1st ed. Cambridge University Press,
October 2000, pp. 233 – 234.
[9] G. Benenti, G. Casati, and G. Strini, Principles of Quantum Computation And Information:
Basic Tools And Special Topics. River Edge, NJ, USA: World Scientific Publishing Co., Inc.,
2007.
[10] C. Cohen-Tannoudji, B. Diu, and F. Laloe, Quantum Mechanics (2 vol. set). Wiley-
Interscience, October 2006.
[11] D. E. Knuth, Art of Computer Programming, Volume 2: Seminumerical Algorithms (3rd Edi-
tion) (Art of Computer Programming Volume 2), 3rd ed. Addison-Wesley Professional,
November 1997.
[12] ——, Art of Computer Programming, Volume 2: Seminumerical Algorithms (3rd Edition) (Art
of Computer Programming Volume 2), 3rd ed. Addison-Wesley Professional, November
1997, p. 338.
60
[14] The globus alliance. Accedido el 17 de Noviembre de 2009. [En Lı́nea]. Disponible:
http://www.globus.org/
[15] Condor’s success stories. Accedido el 17 de Noviembre de 2009. [En Lı́nea]. Disponible:
http://www.cs.wisc.edu/condor/success/index.html
[16] Examples of the globus alliance’s impact. Accedido el 17 de Noviembre de 2009. [En Lı́nea].
Disponible: http://www.globus.org/alliance/impact/
[17] M. Hirvensalo, Quantum Computing, ser. Natural Computing Series. Springer, 2004.
[18] S. J. Lomonaco, “Shor’s quantum factoring algorithm,” 2000. [En Lı́nea]. Disponible:
http://arxiv.org/abs/quant-ph/0010034
[19] J. Gruska, Quantum Computing. Shoppenhangers Road, MaidenHead, Berkshire, SL6 2QL,
ENGLAND: McGraw-Hill, 1999.
[20] Gnu multiple precision arithmetic library. Accedido el 26 de Noviembre de 2009. [En Lı́nea].
Disponible: http://gmplib.org/
[23] M. Case. A beginner’s guide to the general number field sieve. Accedido el 30 de Noviembre
de 2009. [En Lı́nea]. Disponible: http://islab.oregonstate.edu/koc/ece575/03Project/Case/
paper.pdf
[24] Open mpi: Open source high performance computing. Accedido el 30 de Noviembre de
2009. [En Lı́nea]. Disponible: http://www.open-mpi.org/
61