< Return to Video

Algoritmos aritméticos (13 min)

  • 0:00 - 0:05
    El próximo tema que trataremos, es como usar la aritmética modular de grandes enteros.
  • 0:05 - 0:09
    La primera pregunta es como representar grandes enteros en un ordenador,
  • 0:09 - 0:14
    Eso resulta relatívamente simple. Imaginemos que estamos en una máquina de 64 bits,
  • 0:14 - 0:18
    lo que haríamos es separar el número que queremos representar en bloques de 32 bits.
  • 0:18 - 0:23
    Y básicamente tendríamos N en bloques de 32 bits que juntos
  • 0:23 - 0:27
    representan el número que queremos almacenar en el ordenador. Debo mencionar
  • 0:27 - 0:31
    que estoy usando 64 bits como ejemplo. De hecho, muchos procesadores
  • 0:31 - 0:35
    modernos tienen registros de 128 bits o más, y podemos incluso hacer multiplicaciones con ellos.
  • 0:35 - 0:39
    Así que normalmente, usaríamos bloques mucho mayores de 32 bits.
  • 0:39 - 0:43
    La razón por la que quremos limitarnos a 32 bits, es para poder multiplicar
  • 0:43 - 0:47
    2 bloques, ya que el resultado será menor de 64 bits,
  • 0:47 - 0:51
    menor que el tamaño de palabra de la máquina. Veamos ahora algunas operaciones
  • 0:51 - 0:55
    aritméticas y veamos el tiempo que precisan. La suma y la resta
  • 0:55 - 0:59
    básicamente usarían el acarreo para la suma y el préstamo
  • 0:59 - 1:03
    para la resta, y son básicamente operaciones de tiempo lineal. En otras palabras,
  • 1:03 - 1:07
    si sumamos o restamos 2 enteros de tamaño N, el tiempo necesario es básicamente
  • 1:07 - 1:13
    lineal en N. La multiplicación mas simple precisará un tiempo cuadrático. De hecho
  • 1:13 - 1:17
    es lo que se conoce por el algorítmo "del instituto". Es aproximadamente lo
  • 1:17 - 1:21
    que aprendimos en la escuela, y si pensamos un minuto, veremos
  • 1:21 - 1:26
    que el algoritmo es básicamente cuadrático en relación a la longitud de los números que estamos
  • 1:26 - 1:30
    multiplicando. Una gran sorpresa en los años 60 fué el algoritmo de Karatsuba que
  • 1:30 - 1:34
    consigue resultado mucho mejor que tiempo cuadrático. De hecho, consiguió
  • 1:34 - 1:39
    un tiempo de n elevado a 1,585. No es realmente interesante enseñar
  • 1:39 - 1:43
    como funciona exactamente el algoritmo, y por tanto mostraré solo la idea principal. Lo que
  • 1:43 - 1:48
    Karatsuba advirtió es que para multiplicar 2 números podemos
  • 1:48 - 1:53
    tomar el primer número X, y escribirlo como 2 elevado a B por X2 más X1.
  • 1:53 - 1:58
    Donde X2 y X1 son del tamaño aproximado de la raiz de X. Así que
  • 1:58 - 2:03
    podemos separar el número X en parte izquierda de Z y parte derecha de X.
  • 2:03 - 2:08
    Y básicamente, escribimos X como si estuviera escrito en base 2 a la b. Eso son
  • 2:08 - 2:12
    dos dígitos en base 2 a la b. Y se hace los mismo con y. Se escribe y en base
  • 2:12 - 2:17
    2 a la b. Una vez más, se debería escribir como la suma de la mitad izquierda más la
  • 2:17 - 2:22
    mitad derecha, y luego, normalmente, si se intenta hacer esta multiplicación, al desplegar
  • 2:22 - 2:27
    los paréntesis se ve que esto requeriría 4 multiplicaciones.
  • 2:27 - 2:33
    Requeriría x2 por y2, x2 por y1, x1 por y2 y x1 por y1.
  • 2:33 - 2:40
    Karatsuba se dio cuenta de que hay una manera de hacer esta multiplicación de x por y usando sólo
  • 2:40 - 2:46
    tres multiplicaciones de x1 x2 y1 y2, por lo que una gran multiplicación de x por y
  • 2:46 - 2:50
    sólo necesita tres pequeñas multiplicaciones. Ahora, recursivamente, se puede
  • 2:50 - 2:55
    aplicar exactamente el mismo procedimiento para multiplicar x2 por y2 y x2 por y1,
  • 2:55 - 3:00
    y así sucesivamente, y se obtiene este algoritmo recursivo. Y si se hace un
  • 3:00 - 3:05
    análisis recursivo, se verá que básicamente, se consigue un tiempo de ejecución de n elevado a 1,585.
  • 3:05 - 3:14
    Este número 1,585 es, básicamente, el logaritmo de 3 en base 2.
  • 3:14 - 3:18
    Sorprendentemente, resulta que Karatsuba no es el mejor algoritmo de
  • 3:18 - 3:24
    multiplicación que existe. De hecho, se puede multiplicar en un tiempo
  • 3:24 - 3:29
    aproximadamente n*log n, por tanto en tiempo casi lineal. Sin embargo, este es
  • 3:29 - 3:31
    un resultado extremadamente asintótico. La O esconde constantes muy grandes. Y por
  • 3:31 - 3:35
    tanto, el algoritmo solo es práctico cuando los números son absolutamente
  • 3:35 - 3:39
    enormes. Por tanto el algoritmo no se usa muy a menudo. Pero
  • 3:39 - 3:43
    el algoritmo Karatsuba es muy práctico, y de hecho, la mayoria de librerías de criptografia
  • 3:43 - 3:48
    lo implementan para la multiplicación. Sin embargo, para simplificar
  • 3:48 - 3:52
    voy a ignorar el algoritmo de Karatsuba y por simplicidad, voy a
  • 3:52 - 3:56
    asumir que la multiplicación se ejecuta en tiempo cuadrático. Pero debemos recordar
  • 3:56 - 4:00
    siempre que la multiplicación es algo más rápida
  • 4:00 - 4:05
    que cuadrática. Y la siguiente pregunta después de la multiplicación
  • 4:05 - 4:10
    es sobre la división con resto, que resulta ser también un algoritmo cuadrático. Así que la
  • 4:10 - 4:15
    operación principal que nos queda, y que hemos usado muchas veces hasta el momento,
  • 4:15 - 4:20
    y que nunca he explicado como calcular es
  • 4:20 - 4:26
    la exponenciación. Vamos pués a resolver el problema de la exponenciacón
  • 4:26 - 4:32
    de un modo abstracto. Imaginemos que tenemos un grupo cíclico G. Esto significa que este
  • 4:32 - 4:37
    grupo es generado de las potencias de un generador g minúscula. Por ejemplo
  • 4:37 - 4:43
    imaginemos un grupo Zp estrella, y supongamos que g minúscula es un generador de G mayúscula.
  • 4:43 - 4:49
    La razón de establecer esto de esta manera es que quiero que se empiece a utilizar
  • 4:49 - 4:54
    esta abstracción donde tratamos con un grupo genérico g y ZP que es realmente
  • 4:54 - 4:59
    un ejemplo de dicho grupo. Pero, de hecho, hay muchos otros ejemplos de
  • 4:59 - 5:03
    grupos cíclicos finitos. Y una vez más quiero enfatizar básicamente que el grupo G
  • 5:03 - 5:08
    simplemente es el conjunto de potencias del generador hasta el orden del grupo.
  • 5:08 - 5:15
    Voy a escribirlo como g elevado a q. Así que nuestra meta ahora es, dado este elemento g y algún
  • 5:15 - 5:21
    exponente x, nuestro objetivo es calcular el valor de g a la x. Ahora normalmente lo que
  • 5:21 - 5:25
    podría decirse que se podría pensar, si x es igual a 3, entonces voy
  • 5:25 - 5:29
    a calcular g elevado al cubo. Bueno, realmente hay nada que hacer. Todo lo que hago es
  • 5:29 - 5:33
    g por g por g y obtengo g elevado al cubo, que es lo que quería. Por lo que
  • 5:33 - 5:37
    en realidad es bastante fácil. sin embargo, este no es el caso que nos interesa. En
  • 5:37 - 5:41
    nuestro caso, nuestros exponentes van a ser enormes. Y por tanto si
  • 5:41 - 5:46
    suponemos un número 500 bits, si se intenta calcular g elevado a un
  • 5:46 - 5:51
    número de 500 bits, simplemente multiplicando g por g por g por g esto va a tomar bastante tiempo.
  • 5:51 - 5:56
    De hecho llevará tiempo exponencial que no es algo que interese
  • 5:56 - 6:01
    hacer. Así que la pregunta es si a pesar de que x es enorme, podemos aún calcular
  • 6:01 - 6:06
    g elevado a x relativamente rápido, y la respuesta es sí y el algoritmo que lo hace
  • 6:06 - 6:11
    se llama un algoritmo cuadrado repetitivo.
    Así que permítanme explicar cómo funciona
  • 6:11 - 6:16
    el algoritmo cuadrado repetitivo. Vamos a tomar como ejemplo, 53. Ingenuamente tendrían que hacer
  • 6:16 - 6:20
    53 multiplicaciones de g por g por g por g hasta llegar a g por el 53 pero quiero
  • 6:20 - 6:25
    mostrarles cómo puede hacerse muy rápidamente.
    Por tanto lo que haremos es escribiremos 53
  • 6:25 - 6:30
    binario. Se trata de la representación binaria de 53 y todo lo que implica.
  • 6:30 - 6:36
    Nótese que este corresponde a 32, este a dieciséis, éste
  • 6:36 - 6:41
    corresponde a cuatro, y éste corresponde a uno. Así que realmente 53 es 32
  • 6:41 - 6:47
    más 16 más 4 más 1. Pero lo que esto significa es que g a la potencia 53
  • 6:47 - 6:52
    es g elevado a 32+16+4+1. Y lo podemos desglosar, una vez más, utilizando las reglas de
  • 6:52 - 6:57
    exponenciación. Podemos desglosarlo como g elevado a 32 por g elevado a 16 por
  • 6:57 - 7:03
    g elevado a 4 por g elevado a 1. Esto debería dar una idea de cómo calcular muy rápidamente
  • 7:03 - 7:07
    g elevado a 53. Lo que haremos es, simplemente, tomamos g y empezamos
  • 7:07 - 7:11
    a elevarlo al cuadrado. Así obtenemos g cuadrado. Elevamos de nuevo al cuadrado todo lo necesario
  • 7:11 - 7:16
    obteniendo g elevado a 4, g elevado a 8, g elevado a 16 y g elevado a 32. Por tanto
  • 7:16 - 7:21
    hemos calculado todas estos cuadrados de g. Y ahora, simplemente vamos a
  • 7:21 - 7:26
    multiplicar las potencias apropiadas para obtener g elevado a 53.
  • 7:26 - 7:30
    Así, g elevado a uno por g elevado a 4 por g elevado a 16 por g elevado a 32, básicamente
  • 7:30 - 7:35
    va a darnos el valor que queremos, que es g elevado a 53. Aquí vemos que
  • 7:35 - 7:40
    simplemente tuvimos que calcular, tuvimos que hacer uno, dos, tres, cuatro,
  • 7:40 - 7:49
    cinco cuadrados, además de cuatro multiplicaciones. Así, con 9 multiplicaciones calculamos
  • 7:49 - 7:54
    g elevado a 53. Esto es bastante interesante. Y resulta que esto es un
  • 7:54 - 7:58
    fenómenos general que nos permite elevar g a potencias muy altas y hacerlo muy
  • 7:58 - 8:03
    rápidamente. Así que permítanme mostrarles el algoritmo. Como ya he dicho, esto se llama algoritmo del cuadrado repetido
  • 8:03 - 8:06
    Así, la entrada al algoritmo es el elemento g y el
  • 8:06 - 8:11
    exponente x. Y el resultado es g elevado a x.
    Así que lo que vamos a hacer es
  • 8:11 - 8:15
    escribir x en notación binaria. Vamos a decir que x tiene n bits. Y esta es la
  • 8:15 - 8:20
    representación real en bits de x como un número binario. Entonces haremos lo
  • 8:20 - 8:24
    siguiente: Tendremos estos dos registros:
    y va a ser un registro constantemente
  • 8:24 - 8:28
    elevado al cuadrado. Y z va a ser un acumulador que se multiplica en las
  • 8:28 - 8:33
    potencias apropiadas de g según sea necesario. Así, lo que haremos es lo siguiente: recorreremos los
  • 8:33 - 8:37
    bits de x comenzando por los bits menos significativos, y a continuación, hacemos lo
  • 8:37 - 8:41
    siguiente: en cada iteración simplemente elevamos al cuadrado y. Bien, sólo
  • 8:41 - 8:46
    elevar al cuadrado en cada iteración. Y entonces, siempre que el correspondiente bit del
  • 8:46 - 8:51
    exponente x sea uno, simplemente acumulamos el valor de y en
  • 8:51 - 8:55
    este acumulador z, y al final, simplemente entregamos z. Eso es todo. Eso es
  • 8:55 - 9:00
    el algoritmo completo, y eso es el algoritmo del cuadrado repetido. Así, vamos a ver un
  • 9:00 - 9:04
    ejemplo con g elevado a 53. Se pueden ver las dos columnas. y es una
  • 9:04 - 9:08
    columna, que evoluciona a durante las iteraciones y z es otra columna, que de nuevo
  • 9:08 - 9:13
    evoluciona durante las iteraciones. Así, y no es muy interesante. Básicamente, todo
  • 9:13 - 9:17
    lo que le ocurre a y es que en cada iteración, simplemente se obtiene su cuadrado. Y así
  • 9:17 - 9:22
    sólo recorre las potencias de dos y los exponentes, y eso es todo. z es
  • 9:22 - 9:27
    un registro más interesante que acumula
  • 9:27 - 9:32
    las potencias adecuadas de g cuando el bit correspondiente al exponente es uno. Así, por ejemplo,
  • 9:32 - 9:36
    el primer bit del exponente es uno, por tanto, al final de la primera
  • 9:36 - 9:41
    iteración el valor z es simplemente igual a g. El segundo bit del exponente es cero
  • 9:41 - 9:46
    por lo que el valor de z no cambia después de la segunda iteración. Y al final de la
  • 9:46 - 9:52
    tercera iteración el tercer bit del exponente es uno, así acumulamos g elevado
  • 9:52 - 9:57
    a 4 en z. El siguiente bit del exponente es cero, por lo que z no cambia. El
  • 9:57 - 10:02
    siguiente bit del exponente es uno. Ahora suponemos que se acumula el anterior
  • 10:02 - 10:07
    valor de y en el acumulador z así que permítame preguntar cual va a ser el valor de z?
  • 10:11 - 10:14
    Bueno, simplemente acumulamos g elevado a 16 en z, simplemente calculamos la suma
  • 10:14 - 10:20
    de 16 y 5 y obtenemos g elevado a 21.
    Finalmente, el último bit se establece también en uno
  • 10:20 - 10:25
    por lo que se acumula en z, hacemos 32 más 21 y conseguimos finalmente como salida g elevado a 53.
  • 10:25 - 10:30
    Muy bien, esto nos da una idea de cómo funciona el algoritmo del cuadrado repetido.
  • 10:30 - 10:36
    Es es un algorism interesante y nos permite calcular potencias enormes de
  • 10:36 - 10:41
    g muy, muy, muy rápidamente. Por lo que el número de iteraciones, esencialmente, sería
  • 10:41 - 10:46
    el logaritmo en base 2 de x. Bien. Tenga en cuenta que el número de iteraciones depende simplemente del
  • 10:46 - 10:52
    número de dígitos de x, que es básicamente el logaritmo en base 2 de x. Así que incluso si x es un
  • 10:52 - 10:57
    número de 500 bits, en 500 multiplicaciones -bueno, 500 iteraciones, realmente 1.000
  • 10:57 - 11:02
    multiplicaciones, porque tenemos que elevar al cuadrado y acumular-. En 1.000
  • 11:02 - 11:07
    multiplicaciones podremos plantear g elevado a la potencia de un exponente de 500 bits.
  • 11:07 - 11:13
    Vale ahora podemos analizar el tiempo de ejecución. Supongamos
  • 11:13 - 11:18
    que tenemos un módulo de N bits como N mayúscula. Como dijimos, la suma y la resta de ZN consume un
  • 11:18 - 11:22
    tiempo lineal. La multiplicación, como he dicho, Karatsuba la hace
  • 11:22 - 11:27
    más eficientemente, y por simplicidad sólo decimos que consume un tiempo cuadrático. Y la
  • 11:27 - 11:32
    exponenciación, como he dicho, básicamente consume logaritmo de x repeticiones, y en cada
  • 11:32 - 11:36
    iteración básicamente hacemos dos multiplicaciones. Por tanto, es O (log (x))
  • 11:36 - 11:40
    veces el tiempo para multiplicar. Y vamos a decir que el tiempo para multiplicar es cuadrático. Por lo tanto
  • 11:40 - 11:45
    el tiempo de ejecución sería, realmente, N cuadrado logaritmo de x. Y dado que x es siempre
  • 11:45 - 11:49
    inferior a N, por el teorema de Fermat, no hay ningún punto elevando g a una potencia
  • 11:49 - 11:54
    que sea mayor que el módulo. Así x será menor que N. Supongamos que x
  • 11:54 - 11:59
    también es un entero de n bits. Entonces, la exponenciación es realmente un algoritmo de tiempo cúbico.
  • 11:59 - 12:03
    Está bien, eso es lo que quería recordar, la exponenciación es realmente
  • 12:03 - 12:07
    relativamente lenta. Actualmente, realmente tarda unos microsegundos en un
  • 12:07 - 12:11
    ordenador moderno. Pero incluso microsegundos para -digamos- un procesador de cuatro gigahercios es
  • 12:11 - 12:15
    un poco de trabajo. Y sólo tenga en cuenta que en toda la exponenciación
  • 12:15 - 12:20
    hablamos de operaciones. Por ejemplo, para determinar si un número es un
  • 12:20 - 12:24
    residuo cuadrático o no, toda esa exponenciación básicamente consume un tiempo cúbico.
  • 12:25 - 12:30
    Muy bien, así terminé nuestra discusión de algoritmos aritméticos y luego en el
  • 12:30 - 12:34
    segmento siguiente comenzaremos hablando sobre problemas difíciles, módulo, números primos y compuestos.
Title:
Algoritmos aritméticos (13 min)
Video Language:
English

Spanish subtitles

Revisions