Desciframiento RSA desde un punto de vista mecánico cuántico aplicado en Grid
Texto completo
(2) Agradecimientos. A mi familia, amigos, a todas las personas especiales que estuvieron a mi lado durante la elaboración de este trabajo, y a Marcela.. i.
(3) 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 discernir 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 ineficiencia 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.
(4) Índice general Prólogo. II. Introducción. 1. 1. RSA: el Problema de la Factorización y Computación Cuántica 1.1. RSA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.1. ¿Qué es? . . . . . . . . . . . . . . . . . . . . . . . . 1.1.2. Principios Teóricos . . . . . . . . . . . . . . . . . . . 1.1.3. Funcionamiento . . . . . . . . . . . . . . . . . . . . 1.2. Computación Cuántica . . . . . . . . . . . . . . . . . . . . . 1.2.1. El Qubit y El Algorı́tmo de Shor . . . . . . . . . . . . 2. Diseño y Especificación 2.1. Definición del problema . . . . 2.2. Especificaciones . . . . . . . . . 2.3. Desarrollo del Diseño . . . . . . 2.3.1. Fuentes de Información . 2.3.2. Diseño . . . . . . . . .. . . . . . .. . . . . . .. . . . . . .. . . . . . .. . . . . . .. . . . . . .. . . . . . .. . . . . . .. . . . . . .. . . . . . .. . . . . . .. 3 3 4 4 5 7 8. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. 11 11 11 12 13 14. 3. Implementación 3.1. Descripción de la Implementación . . 3.2. Implementación en C++ . . . . . . . 3.2.1. MCD-C++ . . . . . . . . . . 3.2.2. Potenciación-C++ . . . . . . 3.2.3. Exponenciación Modular-C++ 3.2.4. Orden-C++ . . . . . . . . . . 3.2.5. Main-C++ . . . . . . . . . . . 3.3. Implementación con Librerı́a GMP . . 3.3.1. Orden-GMP . . . . . . . . . . 3.3.2. Main-GMP . . . . . . . . . . 3.4. Trabajos (jobs) en CONDOR . . . . .. . . . . . . . . . . .. . . . . . . . . . . .. . . . . . . . . . . .. . . . . . . . . . . .. . . . . . . . . . . .. . . . . . . . . . . .. . . . . . . . . . . .. . . . . . . . . . . .. . . . . . . . . . . .. . . . . . . . . . . .. . . . . . . . . . . .. . . . . . . . . . . .. . . . . . . . . . . .. . . . . . . . . . . .. . . . . . . . . . . .. . . . . . . . . . . .. . . . . . . . . . . .. . . . . . . . . . . .. . . . . . . . . . . .. . . . . . . . . . . .. . . . . . . . . . . .. . . . . . . . . . . .. . . . . . . . . . . .. . . . . . . . . . . .. 17 17 17 17 20 21 21 22 24 24 25 28. . . . . .. 30 30 30 31 32 32. . . . . .. . . . . .. 4. Análisis y Validación 4.1. Pruebas . . . . . . . . . . . . . . . 4.1.1. Fase de Pruebas I . . . . . . 4.1.2. Resultados y Análisis I . . . 4.1.3. Fase de Pruebas II . . . . . 4.1.4. Resultados y Análisis Fase II. . . . . .. . . . . .. III. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . .. . . . . ..
(5) Comentarios Finales. 36. Anexos 5.1. Gráficas de Resultados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 38 38. Bibliografia. 61. IV.
(6) Indice de Códigos 1. 2. 3. 4. 5. 6. 7. 8.. MCD Binario . . . . . . . . . . . . . . . . Exponenciación Binaria . . . . . . . . . . . Exponenciación Modular . . . . . . . . . . Orden de f (x) = ax (mód b) . . . . . . . Factorizar en primos . . . . . . . . . . . . Orden de f (x) = ax (mód b) usando GMP Factorizar en primos con GMP . . . . . . . Trabajos en CONDOR (moo.condor) . . . .. V. . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . .. 18 20 21 21 22 25 25 28.
(7) Índice de figuras 4.1. 4.2. 4.3. 4.4. 5.5. 5.6. 5.7. 5.8. 5.9. 5.10. 5.11. 5.12. 5.13. 5.14. 5.15. 5.16. 5.17. 5.18. 5.19. 5.20. 5.21. 5.22. 5.23. 5.24.. Tiempo para Factorizar N=88637 . . . . Tiempo para Factorizar N=88544111 . . Tiempo para Factorizar N=224929877 . Tiempo para Factorizar N=1176621989 Tiempo para Factorizar 17897 . . . . . Tiempo para Factorizar 40753 . . . . . Tiempo para Factorizar 52907 . . . . . Tiempo para Factorizar 88637 . . . . . Tiempo para Factorizar 95129 . . . . . Tiempo para Factorizar 97183 . . . . . Tiempo para Factorizar 112451 . . . . . Tiempo para Factorizar 126629 . . . . . Tiempo para Factorizar 223733 . . . . . Tiempo para Factorizar 333967 . . . . . Tiempo para Factorizar 3080681 . . . . Tiempo para Factorizar 6980653 . . . . Tiempo para Factorizar 19783333 . . . Tiempo para Factorizar 31363807 . . . Tiempo para Factorizar 42446629 . . . Tiempo para Factorizar 88544111 . . . Tiempo para Factorizar 109822469 . . . Tiempo para Factorizar 224929877 . . . Tiempo para Factorizar 631563797 . . . Tiempo para Factorizar 1176621989 . .. VI. . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . .. 32 33 34 35 38 40 41 42 43 44 45 46 47 48 50 51 52 53 54 55 56 57 58 59.
(8) Índice de tablas 1.1. Relación entre la Computación y la Fı́sica. . . . . . . . . . . . . . . . . . . . . . . 4.1. Números a Factorizar en la Fase I . . . 4.2. Numeros a Factorizar en la Fase II . . . 5.3. Resultados para Factorizar 17897 . . . . 5.4. Resultados para Factorizar 40753 . . . . 5.5. Resultados para Factorizar 52907 . . . . 5.6. Resultados para Factorizar 88637 . . . . 5.7. Resultados para Factorizar 95129 . . . . 5.8. Resultados para Factorizar 97183 . . . . 5.9. Resultados para Factorizar 112451 . . . 5.10. Resultados para Factorizar 126629 . . . 5.11. Resultados para Factorizar 223733 . . . 5.12. Resultados para Factorizar 333967 . . . 5.13. Resultados para Factorizar 3080681 . . 5.14. Resultados para Factorizar 6980653 . . 5.15. Resultados para Factorizar 19783333 . . 5.16. Resultados para Factorizar 31363807 . . 5.17. Resultados para Factorizar 42446629 . . 5.18. Resultados para Factorizar 88544111 . . 5.19. Resultados para Factorizar 109822469 . 5.20. Resultados para Factorizar 224929877 . 5.21. Resultados para Factorizar 631563797 . 5.22. Resultados para Factorizar 1176621989. VII. . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . .. 7 31 33 39 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59.
(9) Introducción La transmisión de información en forma segura tratando de mantener una alta confidencialidad ha sido una constante preocupación para los hombres, todo esto como un resultado de problemas de comunicación militar, territorial, ideológicos, polı́ticos, entre otros. De manera indiscutible e incluso fundamental, “Debe ser que tan pronto como una cultura ha alcanzado un cierto nivel, probablemente medido por su nivel de alfabetización, la criptografı́a aparece de forma espontánea [...] Las múltiples necesidades y deseos de los humanos que demandan privacidad entre dos o más personas en el medio de la vida social debe inevitablemente llevar hacia la criptologı́a allı́ en donde los hombres prosperan y donde quieran escribir...” [1]. En otras palabras la privacidad es inherente entre los humanos, y estamos dispuestos a mejorarla tanto como podamos con tal de mantener nuestros intereses asegurados. Kahn muestra uno de los métodos que más ha sido usado por criptógrafos para cifrar mensajes; 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 informació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 transmitirse información, la llave usada por Bob para cifrar la información no es la misma que Alice 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 aprovechamiento de efectos cuánticos en gran escala compleja como el enredamiento e interferencia. Lo que queremos 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.
(10) 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 factorizació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 2 se desarrolla el diseño del problema basados en el algorı́tmo cuántico. En el capı́tulo 3 se muestra los detalles de la implementación desarrollada. 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.
(11) Capı́tulo 1 RSA: el Problema de la Factorización y Computación Cuántica Iniciamos el proyecto introduciendo el propósito del proyecto y el problema a resolver. Esto incluye 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 Computació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 investigació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 funcionamiento 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.
(12) 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 ampliamente 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 garantizar 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étricas, 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.. 1.1.2.. Principios Teóricos. Para un funcionamiento óptimo RSA usa el principio de Funciones en una dirección las cuáles son fáciles de calcular, pero su operación inversa es difı́cil. Por ejemplo, sea f una función, la descripción de f es conocida. Dado un x, serı́a trivial calcular f (x). Sin embargo, dado un y en el rango de f , es difı́cil encontrar el x tal que f (x) = y. Suponga a manera de ejemplo que a y b son número de 150 bits, resultarı́a fácil calcular c = a∗b, paro dado c es difı́cil de determinar a y b. Las Funciones Trampa son Funciones en una dirección pero con una “trampa”, esto es información adicional que puede desbloquear el problema inverso. En pocas palabras, una función es de tipo trampa si: 1. Es una función en una dirección. 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.
(13) 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: 1. Selecciona un par de números primos p y q grandes. 2. Calcula el producto N ≡ pq. 3. Selecciona un número aleatorio pequeño e impar, e, el cual es coprimo a ϕ (N ) = (p − 1) (q − 1). 4. Calcula d, que es el inverso multiplicativo de e, módulo ϕ (N ).3 5. La llave Pública RSA es el par P = (e, N ). La llave Secreta es el par S = (d, N ). 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 mensaje, 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: 1. Supongamos que selecciona p = 109 y q = 127, por lo tanto N = 13843. 4 2. Calcula ϕ (N ) = ϕ (13843) = (109 − 1) (127 − 1) = 13608, y un e = 11 co-primo. 3. Ahora d = 12371, ed = 136081 = 1 (mód 13608). 3. Esto es un d que satisfaga ed = 1 (mód ϕ (N )) El par de números primos elegidos al azar no son tan grandes. Sin embargo es a modo de ilustración y funciona del mismo modo sin importar la longitud de los mismos. 4. 5.
(14) 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 corresponderı́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 desencripció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 existentes6 , es usando Exponenciación Modular. En donde usando el Teorema de Euler tenemos que: aϕ(N )−1 = a−1 (módN ) 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. Es ampliamente conocido que el problema de factorización es intratable en la computación clásica. Y es gracias a este hecho que el sistema de encripción RSA poco vulnerable hoy en dı́a, ya que no existe un computador tal y como lo conocemos actualmente que resuelva el problema 5. En donde A → 5, L → 0, P → 2 y T → 9. De hecho este serı́a el procedimiento que deberı́a usar una persona para enviar texto de manera cifrada. 6 El otro es el Algoritmo de Euclides Extendido. 6.
(15) 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.. 1.2.. Computación Cuántica. Esta sección no intenta de ninguna forma hacer un desarrollo amplio sobre la teorı́a que hay por detrás de la factorización de un número desde un punto de vista Mecánico cuántico, pero sı́ proveer lo necesario para evidenciar cómo a partir de esta se procedió con el diseño e implementación del algoritmo, ası́ como las variantes y problemas encontrados. Sin embargo, es conveniente relacionar las ciencias de la computación con la fı́sica. 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 Universal 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 empieza 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 Computador Cálculo Entrada Reglas Salida. Fı́sica Sistema Fı́sico Movimiento Estado Inicial Leyes de Movimiento Estado Final. Tabla 1.1: Relación entre la Computación y la Fı́sica.. La teorı́a de la computación puede entonces incorporarse a la fı́sica; de hecho ha sido incorporado por la teorı́a Cuántica de la Computación con el fin de resolver un problema crı́tico, el 7.
(16) 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: “Una posible solución al problema [...] es cambiarse a un paradigma distinto de computación. Uno de esos paradigmas es provisto por la teorı́a cuántica de la computación, la cual está basada en la idea del uso de mecánica cuántica para ejecutar cálculos, en vez de fı́sica clásica.” [7] Es bastante motivante el hecho de poder hacer eficientemente cálculos por medio de la computació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 computador cuántico.. 1.2.1.. El Qubit y El Algorı́tmo de Shor. El bit es el concepto fundamental de la computación clásica, en este sentido el qubit7 es su contraparte cuántica. Más aún, ası́ como el bit clásico tiene un estado 1 o 0 el bit cuántico también tiene los estados |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 usualmente como Superposición Coherente |Ψi = α |1i + β |0i. (1.2.1). Esto presenta una gran diferencia ya que la cantidad de posibles estados es colosal. Más aún, sabemos que clásicamente podemos en n bits guardar 2n números en cualquier instante. Sin embargo cuánticamente podemos en n bits guardar a todas los posibles 2n números. Supongamos por ejemplo que clásicamente en una variable vamos a guardar un número de 10 bits, como es de esperarse en esta variable solo tendremos dicho número. Sin embargo, en una variable cuántica podrı́amos tener a todos los números de 10 bits; esto representa un incremento exponencial en información guardada y por lo tanto cualquier cálculo que se haga sobre esta superposición, es hecho en paralelo haciendo que problemas complejos sean mucho más fáciles de resolver. De lo mencionado previamente es importante notar que los efectos de superposición, enredamiento e interferencia, resultan ser la clave del desempeño de los distintos algoritmos que cuánticamente se llevan a cabo como respuesta a los problemas que se enfrenta la computación clásica. Pasemos al algoritmo por el cual estamos interesados. 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.
(17) 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] 1. Elegir un número x aleatorio que sea menor que N y co-primo. 2. Ahora, calcular el orden de x (mód N ), definido como el r mı́nimo dentro de un conjunto a tal que al elevar x a la r-ésima potencia módulo N resulta igual a 1, siendo r distinto de 0; r ← mı́n a : xa (mód N ) = 1. 3. Sı́, gcd xr/2 − 1, N = 1 será un factor no trivial de N y 4. Sı́, gcd xr/2 + 1, N = 1 será el otro factor de N . 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 2n x=0. (1.2.2). 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 estar 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 m j=0. (1.2.3). 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. Recomiendo 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.
(18) 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. 2πix0 k 1 X N √ exp r k r r k=0. (1.2.4). Al medir el primer registro obtendremos un múltiplo de la forma λN/r del valor medido, digamos 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.
(19) Capı́tulo 2 Diseño y Especificación 2.1.. Definición del problema. Como vimos en el capitulo pasado existe una forma de factorizar un número de manera alternativa ya que se reduce el problema de la factorización al encontrar el orden de una función, en particular ax (mód N ) en donde N es el número a factorizar y a un número aleatorio. Por lo tanto el problema es factorizar un número encontrando su orden. 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 divisor, 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 únicamente nos limitaremos a calcular el máximo común divisor entre un par de números enteros, ası́ como 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. También es necesario poder hacer la exponenciación de un número, es decir an = a | ∗ a ∗ a ∗{za ∗ . . . ∗ a} n. ya que a la hora de sacar uno de los factores debemos elevar el número elegido al azar a la mitad del orden encontrado, luego de haberlo encontrado. La exponenciación modular es tomar el módulo luego de obtener el resultado de la exponenciación, es decir si queremos hacer: an (mód p), entonces intuitivamente pensamos en hacer 11.
(20) 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. Adicionalmente, necesitamos un generador de números aleatorios, ya que como parte esencial del pseudo algoritmo reside en el hecho de tener como base para el inicio del proceso un número aleatorio y menor que el número a factorizar. En adición a este hecho, es importante que el número sea lo más aleatorio posible, es decir, al momento de repetir el proceso aquel número aleatorio generado siempre sea distinto en cada iteración. 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 proceso. Y por último, debe existir un lugar en donde los resultados puedan persistir en forma fı́sica para una posterior evaluación. Para el problema de la factorización, es indispensable que el resultado sea exacto, es deseable entonces que siempre obtengamos como respuesta los factores que componen el número de entrada. Puesto que la respuesta es fácil de verificar cualquier resultado erróneo serı́a fácilmente detectable, ya que lo difı́cil, como se mencionó previamente, es encontrar dichos factores. Las circunstancias en que el algoritmo podrı́a desarrollar respuestas falsas o inexactas provendrı́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.. 2.3.. Desarrollo del Diseño. El proceso que seguimos para el desarrollo del diseño se dividió en varias etapas. La primera se basó en recopilar información sobre las distintas formas de implementar eficientemente los distintos módulos, o métodos que la máquina finalmente debe usar, entre estos el máximo común divisor, exponenciación, exponenciación modular y generación de números aleatorios. La segunda fase se centró en estudiar cómo funciona CONDOR, y en cómo mandar trabajos (jobs) a los demás miembros de la Grid, básicamente la arquitectura de CONDOR. El tercero y último, revisar profundamente el algoritmo cuántico para tomar una decisión final a la hora de hacer el modelaje.. 12.
(21) 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 sección pasada, decidimos consultar un libro que estudie las diversas formas de implementar un algoritmo 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 algoritmo, 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. Un tema de importante discusión es la exponenciación modular que debe hacerse de manera eficiente. En esta situación Knuth no nos proporciona información que de solución al problema. Sin embargo, conjunto al algoritmo que él nos propone combinándolo con un poco de teorı́a de grupos podemos obtener un algoritmo eficiente para hacer la exponenciación modular de manera distinta a como se presentó en la sección de requerimientos 2.2, ya que sacar el módulo del numero que resulta luego de haber hecho la exponenciación puede ser inmanejable. En pocas palabras, la operación entre números bajo el módulo forma un grupo cı́clico, y en adición, siempre que apliquemos el módulo m a un número, el resultado siempre será menor que m y será un elemento del grupo. Se sigue entonces que podrı́amos hacer luego de cada operación del algoritmo binario para la exponenciación, una aplicación de la operación módulo y estar seguros que nos llevará al mismo resultado como mostramos en la sección 2.2. Fase II CONDOR es un sistema de administración de cargas para trabajos intensivos computacionalmente [13]. En adición, un usuario puede enviar trabajos paralelos o seriales por medio de CONDOR, 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. Dentro del sistema de administración CONDOR, se establece un computador maestro o Scheduler el cual se comporta como la central de distribución de trabajos a los demás computadores, que comúnmente son denominados esclavos. Esta conexión entre computadores, ya que en principio CONDOR podrı́a trabajar sobre la máquina en la cual está instalado -procesos en batch-, se 13.
(22) 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 proyectos en donde se aproveche el poder de múltiples supercomputadores haciendo cálculos en paralelo [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 desvirtuar 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 cantidad 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 2. El cálculo de la función y la sumatoria resulta 1 (|0i |1i + |1i |7i + |2i |4i + |3i |13i + |4i |1i + . . . + |15i |13i) 4 Agrupando estados iguales lo que está entre los paréntesis. (|0i + |4i + |8i + |12i) |1i + (|1i + |5i + |9i + |13i) |7i + (|2i + |6i + |10i + |14i) |4i + (|3i + |7i + |11i + |15i) |13i 3. Luego de la transformación cuántica de Fourier, dentro del paréntesis queda. (|0i + |4i + |8i + |12i) |1i + (|0i + i |4i − |8i − i |12i) |7i + (|0i − |4i + |8i − |12i) |4i + (|0i − i |4i − |8i + i |12i) |13i 14.
(23) 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 todos 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 aproximació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 manera. 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 seleccionando 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 computadores 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 circunstancia 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 encontrar 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 calculabamos, 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.
(24) (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 conjuntamente 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. El pseudo-algoritmo de la solución consiste en, dado un número positivo n hecho a partir de dos primos f 1 y f 2, encuentra los factores que componen a n: 1. [Inicializar] Generar a de forma aleatoria, k, u, v ← 0, g ← M CD (a, n). 2. [g = 1?] En caso afirmativo, continúe al paso 3, de lo contrario o es el mismo n o es un factor trivial por lo tanto f 1 ← g, f 2 ← v/g y finaliza. 3. [Orden] k ← orden (a, n) Calcular el orden de a y n como lo hace el algoritmo de Shor. 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. 5. [V erif icar] En f 1 ← M CD (u, n), f 2 ← M CD (v, n). Si f 1 o f 2 son resultan ser 1 o n, vaya al paso 1, de lo contrario finaliza. Con el algoritmo ya creado, estamos preparados para la implementación, que como se mencionó en la introducción se realizará en C/C++ sobre Condor para enviar los trabajos sobre la Grid hecha en Globus.. 16.
(25) Capı́tulo 3 Implementación 3.1.. Descripción de la Implementación. En este capı́tulo mostraremos cómo implementamos la solución en C/C++ y en CONDOR. Empezamos construyendo en C++ la solución de los distintos módulos o funciones que servirı́an dentro del programa principal, incluyendo la obtención del Máximo Común Divisor, exponenciación modular, entre otros. Detallamos en cada una de estas funciones lo que hacemos con el fin de entender mejor el código y ası́ pueda reutilizarse en un futuro. Luego de la implementación del programa en C++, hacemos una implementación en C con la librerı́a GMP la cual nos permite manejar números de tamaños colosales [20]. La librerı́a contiene funciones que implementan eficientemente las distintas operaciones que necesitamos para la implementación del algoritmo descrito en el capitulo 2 y ası́ encontrar los factores de números más grandes que aquellos manejados nativamente por C. Describiremos además, cómo usamos CONDOR para distribuir los trabajos en la GRID ası́ como 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.. 3.2.. Implementación en C++. 3.2.1.. MCD-C++. Para la implementación del máximo común divisor nos basamos en el trabajo del Señor Knuth, en donde estudia cómo eficientemente puede implementarse el cálculo de este valor. Describe. 17.
(26) además, que aunque eficazmente varios algoritmos funcionan para determinar el máximo común divisor, 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. Código 1: Máximo Común Divisor Binario 1 2 3 4. /** *Función que retorna el máximo común divisor de u, y v; *ambos deben ser enteros y retorna un entero. **/. 6 long long int gcd(long long int u, long long int v) 7 { 8 int k=0, pot=1, p=1; 9 long int t, n1=0, n2=1; 11 12 13 14. if(u == 0 || v == 0) { return u|v; }. 16 17 18 19 20 21 22 23 24 25 26 27 28. while(pot==1) { if((u & 1)==0 && (v & 1)==0) { k++; u>>=1; v>>=1; } else { pot=0; } }. 30 31 32 33 34 35 36 37 38 39 40 41 42 43. if((u & 1) == 1) // inicialización { t = -v; if((t & 1)==0) { n1=1; } else if(n1!=1) //max(u,v) { if(t>0) { u = t; } else. 18.
(27) 44 45 46. {. 48. t = u - v; //restar. 50 51 52 53 54 55 56 57 58 59 60 61. if(t != 0) { n1=1; } else n1=0;. 63 64 65 66. v = -t; }. } } else { t = u; n1=1; } while(n1==1) { t>>=1; // dividir t en 2 n1=0;. 68 69 70 71 72 73 74 75 76 77 78 79 80 81. if((t & 1)==0) // t par? { n1=1; } else if(n1!=1) //max(u,v) { if(t>0) { u = t; } else { v = -t; }. 83. t = u - v; //restar. 85 86 87 88 89 90 91. if(t != 0) { n1=1; } else n1=0;. 93 94. } } if(k != 0 ) {. 19.
(28) 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 algo 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. Código 2: Exponenciación Binaria 1 2 3 4 5 6 7 8. /** *Devuelve el resultado de calcular xˆ{n}, tanto x como n *son enteros positivos. **/ long long int potencia(long long int x, unsigned n) { long long int p; long long int r;. 10 11 12 13 14 15 16 17 18 19 20. p = x; r = 1; while (n > 0) { if (n & 1){ r *= p; n-=1; } p *= p; n >>= 1; }. 22 23 }. return r;. Con esta forma de operar matemáticamente a dos números, la base y el exponente, está garantizado que se ejecutan menos cálculos que de la forma tradicional (i.e. multiplicar la base tantas veces como indica el exponente). 20.
(29) 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 3 4 5 6 7 8. /** *Devuelve el resultado de calcular xˆ{n}mod(b), x, n y b *son enteros positivos. **/ long long int modPot(long long int x, unsigned n, long long int b) { long long int p; long long int r;. 10 11 12 13 14 15 16 17 18 19 20 21 22. p = x; r = 1; while (n > 0) { if (n & 1){ r *= p; r = r %b; n-=1; } p *= p; p = p %b; n >>= 1; }. 24 25 }. return r;. 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 3 4. /** *Devuelve en cont (contador) el orden de la función f(x)=aˆ{x}mod(b) *a y b (b:= el número a factorizar) son enteros positivos. **/. 21.
(30) 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 13 14 15. do {. 17. }while(i!=res);. 19 20 }. return cont;. cont++; res=modPot(a,cont,b);. 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 2 3 4 5 6 7 8 9 10 11. //para linux: //compilar como gcc -o <nom ejecutable> <nombre.cpp> -lstdc++ //ejecutar como ./<nom ejecutable> <número a factorizar> //#include "stdafx.h" //comentar esta lı́nea para que funcione en linux #include <stdio.h> #include <stdlib.h> #include <iostream> #include <math.h> #include <time.h> #include <unistd.h> //comentar esta lı́nea para que funcione en windows #include <pthread.h> //comentar esta lı́nea para que funcione en windows. 13 using namespace std; 15 16 17 18 19. // // // Insertar las funciones previamente declaradas acá. // //. 21 22 23 24. /** *Devuelve por consola un par de factores que componen al número *n=p.q ingresado por consola como argumentos al momento de llamar *el programa ya compilado.. 22.
(31) 25 **/ 26 int main(int argc, char* argv[]) 27 {. 30 31. long long int u, v, gd, f1, f2, anterior=1; unsigned ord, tt=1;. 33. v=atoll(argv[1]); //número ingresado al llamar el programa. 35 36 37 38 39 40 42 43 44. while(tt==1) { //para tener un numero aleatorio bueno time_t seconds; time(&seconds); srand((unsigned int) seconds); u=1; while(u==1||u==0||u==anterior) //nos aseguramos que el numero generado u = rand() %v; //no sea el mismo que el anterior. 46 47 48 49 50. anterior = u; if(u==0) gd=v; else if(u==1) gd=v; else { gd=gcd(u,v); //resultado de llamar a la función gcd. 52 53. cout << "Numero generado: " << u <<endl; }. 55. if(gd==1) //si el número generado y el número a factorizar son coprimos { ord = orden(u, v); //resultado de llamar a la función orden. 56 57 59 60 61 62 63 64 65 66 68 69 70 71. if((ord & 1)==0) //es necesario que el orden sea par, para que funcione. { cout << "orden:" << ord <<endl; ord>>=1; f1 = potencia(u,ord)-1; f2 = potencia(u,ord)+1; f1 = gcd(v, f1); f2 = gcd(v, f2); if(f1==1||f2==v||f1==v||f2==1) { tt=1; }. 23.
(32) 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92. else { cout << "factor 1: " << f1 <<endl; cout << "factor 2: " << f2 <<endl; tt=0; } } } else if(gd==v)//el máximo común divisor es el numero a factorizar mismo { tt=1; } else //es un factor trivial { f1 = gd; f2 = v/gd; cout << "factor 1: " << f1 <<endl; cout << "factor 2: " << f2 <<endl; tt=0; } }. 94 return 0; 95 }. 3.3.. Implementación con Librerı́a GMP. El código 5 funciona perfectamente para números pequeños. Sin embargo, luego de unas pruebas realizadas al tratar de factorizar números de alrededor 5 cifras el programa no funcionaba, lo que nos hizo pensar que se debı́a a un problema de tamaños manejados por C++. Entonces empezamos a usar GMP. 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 variable que se instancie debe ser inicializada explı́citamente, ası́ como debe ser igualmente desreferenciada. 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 implementado en el código 4. En este caso como argumentos los mismos números, solo que por referencia se 24.
(33) pasa la variable (ord) en la que quedará el resultado, es decir el orden de la función. Código 6: Orden de la función f (x) = ax (mód b) usando la librerı́a GMP 1 /** 2 *Asigna a ord, el orden de la función aˆ{ord}mod(b), en donde 3 *a:=número aleatorio, b:= el número a factorizar y ord son enteros positivos. 4 *ord debe llegar en 0. 5 **/ 6 void orden(mpz_t a, mpz_t b, mpz_t ord) 7 { 8 mpz_t res; 9 mpz_init(res); 11. mpz_powm(res,a,ord,b); //deja en res el resultado de (aˆorden)mod(b ). 13 14 15 16. do {. 18. }while(mpz_cmp_ui(res,1)!=0);. 20 21 }. mpz_clear(res);. mpz_add_ui(ord, ord,1);//le suma a ord, una unidad. mpz_powm(res,a,ord,b); //deja en res el resultado de (aˆorden) mod(b). 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. Código 7: Factorizar en Primos usando GMP 1 2 3 4 5 6 7 8. #include <stdio.h> #include <gmp.h> #include <time.h> // // // Insertar la función previamente declarada acá. // //. 10 /** 11 *Devuelve por consola un par de factores que componen al número. 25.
(34) 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 17 18 19 20. //Variables generales unsigned long int i, seed; int tt=1; //Variables de Tiempo time_t seconds, ini, fin;. 22 23 24 25 26 27 28. //variables GMP /*u:= numero aleatorio, v:= numero a factorizar, anterior:= numero aleatorio anterior, gcd := máximo común divisor, ord := orden, f1,f2:= factores, r_state:= para generar numero aleatorio */ mpz_t u,v, anterior, gcd, ord, f1, f2; gmp_randstate_t r_state;. 30 //se inicializa anterior 31 mpz_init(anterior); 33 //se inicializa el numero a factorizar 34 mpz_init(v); 35 mpz_set_ui(v, atoll(argv[1]));. 38 ini=clock();//incia medición del tiempo 39 while(tt==1) 40 { 41 // 42 time(&seconds); 43 srand((unsigned int) seconds); 44 // 45 seed = rand(); 46 gmp_randinit_default(r_state); 47 gmp_randseed_ui(r_state, seed); 49 50 51 52 53. mpz_init(u); mpz_init(gcd); mpz_init(ord); mpz_init(f1); mpz_init(f2);. 55 56 57 58 59 60. mpz_set_ui(u,1); while(mpz_cmp_ui(u,1)==0||mpz_cmp_ui(u,0)==0||mpz_cmp(u,anterior)==0) { mpz_urandomm(u,r_state,v); gmp_printf("numero aleatorio: %Zd\n", u); }. 62. mpz_set(anterior, u);. 26.
(35) 63 64. mpz_set_ui(ord,0); mpz_gcd(gcd, u, v);. 66 67. if(mpz_cmp_ui(gcd,1)==0) {. 69. orden(u,v,ord);. 71 72 73. if(mpz_even_p(ord)!=0) { mpz_divexact_ui(ord,ord,2);. 75 76. mpz_pow_ui(f1, u, mpz_get_ui(ord)); mpz_set(f2,f1);. 78 79. mpz_sub_ui(f1,f1,1); mpz_add_ui(f2,f2,1);. 81 82. mpz_gcd(f1, v, f1); 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) { tt=1; } else { gmp_printf("Factor 1: %Zd\n", f1); gmp_printf("Factor 2: %Zd\n", f2); fin=clock();//terminamos medición del tiempo tt=0; } }. 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110. } else if(mpz_cmp(gcd,v)==0) { tt=1; } else { mpz_set(f1,gcd); mpz_divexact(v,v,gcd); mpz_set(f2,v); gmp_printf("Factor 1: %Zd\n", f1); gmp_printf("Factor 2: %Zd\n", f2); fin=clock(); tt=0; }. 112. gmp_randclear(r_state);. 27.
(36) 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); 122 printf("Tiempo(ms): %6.3Lf\n",(long double)(fin-ini)/(CLOCKS_PER_SEC /1000)); 124 return 0; 125 }. 3.4.. Trabajos (jobs) en CONDOR. Vale la pena mostrar cómo hicimos para distribuir los distintos trabajos sobre la grid. CONDOR se encarga de mandar trabajos a los computadores que están en la grid de acuerdo a un archivo que le indica qué hacer y bajo qué polı́ticas. Esta información está contenida en un archivo .condor y está escrito en un lenguaje propio de Condor. El formato de este archivo es similar al .properties que se usa en JAVA. 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 2 3 4. #moo.condor Universe = vanilla initialdir = /usr/programa Executable = /usr/programa/factor. 6 Arguments = //insertar aquı́ el número a factorizar e.g. Arguments = 11766219 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.
(37) Esto ayuda a asociar los archivos de salida más fácilmente.. 29.
(38) 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 factorizació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 Motor 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 detallada 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 factorizar 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 tiempo no deberı́a superar el segundo, en caso contrario podrı́amos esperar una cantidad de tiempo desmesurada para números como el mencionado previamente.. 4.1.1.. Fase de Pruebas I. Documentación Con el ambiente listo en el laboratorio de redes, subimos el archivo moo.condor descrito previamente en 3.4 con su argumento cambiado a uno de los número elegidos. Estos números son los 1. Como es bien sabido Wolfram es la empresa creadora del software matemático Mathematica [22]. 30.
(39) presentes en la Tabla 4.1. 17897 40753 52907 88637 95129 97183 112451 126629 223733 333967 Tabla 4.1: Números a Factorizar en la Fase I 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.. 4.1.2.. Resultados y Análisis I. Presentamos los resultados obtenidos durante el proceso de pruebas realizado durante la etapa de pruebas I en los Anexos. Los resultados están resumidos en forma de gráfico, en donde mostramos lo que se demoró el algoritmo en determinar los factores que componen a los números de 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 correspondientes 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 resultados 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.
(40) y hasta despreciable, cuando tenemos que esperar menos de un segundo para obtener el resultado.. Figura 4.1: Tiempo para Factorizar N=88637. 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 milisegundos entre experimentos que eligen un mismo número aleatorio inicial para factorizar un número.. 4.1.3.. Fase de Pruebas II. Documentación Con el mismo ambiente de la fase I en el laboratorio de redes, modificamos el archivo moo.condor con los siguientes números como argumentos del archivo ası́ como se describió en 3.4. Los números elegidos, son: Con este archivo nuevamente modificado y ubicado en la máquina maestro de CONDOR, seguimos los mismos pasos descritos en la fase I.. 4.1.4.. Resultados y Análisis Fase II. Durante la segunda fase, los resultados obtenidos nos muestran una serie de información fundamental para el análisis del proyecto. Un hecho importante es que superamos en el 93 % de los casos 32.
(41) 3080681 6980653 19783333 31363807 42446629 88544111 109822469 224929877 631563797 1176621989 Tabla 4.2: Numeros a Factorizar en la Fase II 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 igualmente. 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.. Figura 4.2: Tiempo para Factorizar N=88544111. Cuando la magnitud del número anteriormente escogido se duplicó, obtuvimos un resultado que 33.
Documento similar
Para ello, trabajaremos con una colección de cartas redactadas desde allí, impresa en Évora en 1598 y otros documentos jesuitas: el Sumario de las cosas de Japón (1583),
dente: algunas decían que doña Leonor, "con muy grand rescelo e miedo que avía del rey don Pedro que nueva- mente regnaba, e de la reyna doña María, su madre del dicho rey,
Entre nosotros anda un escritor de cosas de filología, paisano de Costa, que no deja de tener ingenio y garbo; pero cuyas obras tienen de todo menos de ciencia, y aun
d) que haya «identidad de órgano» (con identidad de Sala y Sección); e) que haya alteridad, es decir, que las sentencias aportadas sean de persona distinta a la recurrente, e) que
Ciaurriz quien, durante su primer arlo de estancia en Loyola 40 , catalogó sus fondos siguiendo la división previa a la que nos hemos referido; y si esta labor fue de
En este trabajo estudiamos la obra poética en español del escritor y profesor argelino Salah Négaoui, a través de la recuperación textual y análisis de Poemas la voz, texto pu-
Las manifestaciones musicales y su organización institucional a lo largo de los siglos XVI al XVIII son aspectos poco conocidos de la cultura alicantina. Analizar el alcance y
En la parte central de la línea, entre los planes de gobierno o dirección política, en el extremo izquierdo, y los planes reguladores del uso del suelo (urbanísticos y