<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Mathematics on kenji.blog</title><link>http://kenji.blog/es/categories/mathematics/</link><description>Recent content in Mathematics on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>es</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 21:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/es/categories/mathematics/index.xml" rel="self" type="application/rss+xml"/><item><title>Intuición matemática de la criptografía basada en retículos (Lattice-based cryptography)</title><link>http://kenji.blog/es/p/lattice-based-cryptography-math-intuition/</link><pubDate>Fri, 11 Sep 2026 21:00:00 +0900</pubDate><guid>http://kenji.blog/es/p/lattice-based-cryptography-math-intuition/</guid><description>&lt;img src="http://kenji.blog/p/lattice-based-cryptography-math-intuition/img/eyecatch.jpg" alt="Featured image of post Intuición matemática de la criptografía basada en retículos (Lattice-based cryptography)" />&lt;h1 id="1-introducción-el-amanecer-de-la-criptografía-poscuántica-pqc-y-el-auge-de-la-criptografía-basada-en-retículos">1. Introducción: El amanecer de la criptografía poscuántica (PQC) y el auge de la criptografía basada en retículos
&lt;/h1>&lt;p>La infraestructura digital de la sociedad moderna se basa en tecnologías de criptografía de clave pública, como el cifrado RSA y la criptografía de curva elíptica (ECC). Estos métodos basan su seguridad en la dificultad matemática de problemas como la &amp;ldquo;factorización de enteros&amp;rdquo; o el &amp;ldquo;logaritmo discreto&amp;rdquo;, los cuales se cree que no pueden resolverse eficientemente (requieren tiempo exponencial) con computadoras clásicas convencionales.&lt;/p>
&lt;p>Sin embargo, el &amp;ldquo;Algoritmo de Shor&amp;rdquo;, publicado por Peter Shor en 1994, sacudió el mundo de la criptografía. Este algoritmo demostró matemáticamente que una vez que se construyan computadoras cuánticas a gran escala, estas podrán resolver los problemas de factorización de enteros y de logaritmo discreto en tiempo polinomial. Esto significa que la criptografía de clave pública ampliamente utilizada en la actualidad se volverá completamente descifrable en el futuro.&lt;/p>
&lt;p>Para contrarrestar esta &amp;ldquo;Amenaza Cuántica (Quantum Threat)&amp;rdquo;, se hizo imperativo investigar nuevos métodos de cifrado que sean difíciles de descifrar incluso utilizando computadoras cuánticas. Este es el campo conocido como &amp;ldquo;Criptografía Poscuántica (Post-Quantum Cryptography: PQC)&amp;rdquo; o &amp;ldquo;Criptografía resistente a la computación cuántica&amp;rdquo;.&lt;/p>
&lt;p>Existen varios candidatos prometedores para la PQC, como la criptografía basada en hashes, la criptografía basada en códigos, la criptografía multivariable y la criptografía basada en isogenias. Sin embargo, el método que actualmente atrae más la atención y que está en el centro del proceso de estandarización de PQC por parte del NIST (Instituto Nacional de Estándares y Tecnología de EE. UU.) es la &amp;ldquo;Criptografía basada en retículos (Lattice-based cryptography)&amp;rdquo;. En comparación con otros métodos, la criptografía de retículos ofrece velocidades de cifrado y descifrado muy rápidas, y cuenta con una característica destacada: una prueba de seguridad extremadamente sólida en la teoría criptográfica basada en la reducción de la &amp;ldquo;complejidad en el peor de los casos (Worst-case complexity)&amp;rdquo; a la &amp;ldquo;complejidad en el caso promedio (Average-case complexity)&amp;rdquo;.&lt;/p>
&lt;p>En este artículo, partiremos de la definición matemática de &amp;ldquo;Retículo (Lattice)&amp;rdquo;, que es la base de esta criptografía, y explicaremos profundamente mediante fórmulas, intuición geométrica y ejemplos numéricos específicos los problemas difíciles sobre retículos como SVP (Problema del vector más corto) y CVP (Problema del vector más cercano), y el corazón de la criptografía de retículos moderna, el &amp;ldquo;Problema LWE (Learning With Errors)&amp;rdquo;.&lt;/p>
&lt;h1 id="2-definición-matemática-e-intuición-geométrica-de-un-retículo-lattice">2. Definición matemática e intuición geométrica de un Retículo (Lattice)
&lt;/h1>&lt;h2 id="21-espacio-vectorial-y-retículos">2.1 Espacio vectorial y retículos
&lt;/h2>&lt;p>En matemáticas, un &amp;ldquo;Retículo (Lattice)&amp;rdquo; es un conjunto discreto de puntos dispuestos regularmente en un espacio vectorial real de $n$ dimensiones $\mathbb{R}^n$. Es similar al espacio vectorial (Vector Space) que se estudia en álgebra lineal, pero existe una diferencia crucial. Mientras que un espacio vectorial es un espacio continuo representado por combinaciones lineales de vectores base con &amp;ldquo;coeficientes reales&amp;rdquo;, un retículo es un espacio discreto representado por combinaciones lineales de vectores base con &amp;ldquo;coeficientes enteros&amp;rdquo;.&lt;/p>
&lt;p>Demos una definición matemática rigurosa. Consideremos $n$ vectores linealmente independientes $\mathbf{b}_1, \mathbf{b}_2, \dots, \mathbf{b}_n$ (donde $n \le m$) en un espacio vectorial real de $m$ dimensiones $\mathbb{R}^m$. Sea $B = [\mathbf{b}_1, \mathbf{b}_2, \dots, \mathbf{b}_n] \in \mathbb{R}^{m \times n}$ una matriz que tiene estos vectores como columnas. Llamamos a esta $B$ la &amp;ldquo;Base (Basis)&amp;rdquo; del retículo.&lt;/p>
&lt;p>El retículo $\mathcal{L}(B)$ generado por esta base $B$ se define de la siguiente manera:&lt;/p>
$$
\mathcal{L}(B) = \left\{ \sum_{i=1}^{n} x_i \mathbf{b}_i \mathrel{\bigg|} x_i \in \mathbb{Z} \right\} = \{ B \mathbf{x} \mid \mathbf{x} \in \mathbb{Z}^n \}
$$
&lt;p>Lo importante aquí es que los coeficientes $x_i$ están limitados a números enteros $\mathbb{Z}$, en lugar de números reales $\mathbb{R}$. Como resultado, en lugar de puntos continuos que existen en un número infinito en el espacio, se forma un &amp;ldquo;conjunto discreto de puntos&amp;rdquo; similar a las intersecciones espaciadas uniformemente en una cuadrícula.&lt;/p>
&lt;h2 id="22-imagen-geométrica">2.2 Imagen geométrica
&lt;/h2>&lt;p>Consideremos un ejemplo en el plano bidimensional $\mathbb{R}^2$. Si elegimos los vectores base $\mathbf{b}_1 = \begin{pmatrix} 1 \\ 0 \end{pmatrix}$ y $\mathbf{b}_2 = \begin{pmatrix} 0 \\ 1 \end{pmatrix}$, el retículo generado por ellos será el conjunto de todas las coordenadas enteras $(x, y) \in \mathbb{Z}^2$ en el plano cartesiano. Este es el &amp;ldquo;retículo cuadrado&amp;rdquo; más simple.&lt;/p>
&lt;p>Sin embargo, los retículos no siempre son ortogonales. Por ejemplo, considerando una base como $\mathbf{b}_1 = \begin{pmatrix} 2 \\ 1 \end{pmatrix}$ y $\mathbf{b}_2 = \begin{pmatrix} 1 \\ 3 \end{pmatrix}$, los puntos generados se parecerán a las intersecciones de una malla sesgada diagonalmente.&lt;/p>
&lt;h2 id="23-no-unicidad-de-la-base-y-transformación-unimodular">2.3 No unicidad de la base y transformación unimodular
&lt;/h2>&lt;p>Existe una propiedad importante relacionada con la base de la seguridad de la criptografía de retículos: &amp;ldquo;existen infinitas bases que generan el mismo retículo&amp;rdquo;.&lt;/p>
&lt;p>Por ejemplo, el retículo $\mathbb{Z}^2$ generado por la base $\mathbf{b}_1 = (1, 0)^T, \mathbf{b}_2 = (0, 1)^T$ mencionada anteriormente es exactamente el mismo retículo $\mathbb{Z}^2$ que el generado por la base $\mathbf{b}'_1 = (1, 1)^T, \mathbf{b}'_2 = (2, 3)^T$.&lt;/p>
&lt;p>La condición necesaria y suficiente para que una base $B$ y otra base $B'$ generen el mismo retículo es que exista una matriz con componentes enteros $U \in \mathbb{Z}^{n \times n}$ cuyo determinante sea $\det(U) = \pm 1$, tal que:
&lt;/p>
$$ B' = B U $$
&lt;p>
A una matriz $U$ de este tipo se le llama &amp;ldquo;Matriz unimodular (Unimodular matrix)&amp;rdquo;.&lt;/p>
&lt;p>La idea básica en la aplicación a la criptografía es utilizar una &amp;ldquo;buena base&amp;rdquo; (una base casi ortogonal formada por vectores cortos) como clave privada, y una &amp;ldquo;mala base&amp;rdquo; (una base con vectores extremadamente oblicuos entre sí y muy largos) como clave pública. Calcular una buena base a partir de una mala base se vuelve muy difícil a medida que aumentan las dimensiones. Esta es la intuición básica de la criptografía basada en retículos.&lt;/p>
&lt;h1 id="3-problemas-computacionalmente-difíciles-en-retículos">3. Problemas computacionalmente difíciles en retículos
&lt;/h1>&lt;p>La seguridad de la criptografía basada en retículos depende de la dificultad de resolver problemas matemáticos específicos en los retículos. Aquí presentaremos dos de los problemas más fundamentales y conocidos.&lt;/p>
&lt;h2 id="31-problema-del-vector-más-corto-shortest-vector-problem-svp">3.1 Problema del vector más corto (Shortest Vector Problem: SVP)
&lt;/h2>&lt;p>SVP es el problema más clásico y famoso de la teoría de retículos.&lt;/p>
&lt;p>&lt;strong>Definición (SVP):&lt;/strong>
Dada una base de retículo arbitraria $B$, encontrar el vector $\mathbf{v}$ distinto de cero que pertenece al retículo $\mathcal{L}(B)$ y tiene la norma euclidiana (longitud) mínima.&lt;/p>
&lt;p>Expresado en fórmula matemática, es el problema de encontrar $\mathbf{v}$ tal que $\min_{\mathbf{v} \in \mathcal{L}(B) \setminus \{\mathbf{0}\}} \| \mathbf{v} \|$. A esta longitud mínima se le denota como $\lambda_1(\mathcal{L})$ y se le llama &amp;ldquo;Primer mínimo sucesivo (First successive minimum) del retículo&amp;rdquo;.&lt;/p>
&lt;p>En dimensiones bajas como 2 o 3, podemos encontrar el vector más corto visualmente dibujando un gráfico, o podemos resolverlo eficientemente usando algoritmos como el algoritmo de reducción de bases de Gauss. Sin embargo, cuando la dimensión $n$ alcanza valores altos como cientos o miles, se sabe que resolver estrictamente SVP es NP-difícil.&lt;/p>
&lt;p>En criptografía real, no se busca el vector más corto estricto, sino que se utiliza un SVP aproximado ($\gamma$-SVP) para encontrar un &amp;ldquo;vector aproximadamente corto&amp;rdquo;. Si el factor de aproximación $\gamma$ es de tamaño polinomial, este problema se sigue considerando extremadamente difícil.&lt;/p>
&lt;h2 id="32-problema-del-vector-más-cercano-closest-vector-problem-cvp">3.2 Problema del vector más cercano (Closest Vector Problem: CVP)
&lt;/h2>&lt;p>CVP es también un problema de extrema importancia en la criptografía basada en retículos.&lt;/p>
&lt;p>&lt;strong>Definición (CVP):&lt;/strong>
Dada una base de retículo arbitraria $B$ y un vector objetivo arbitrario $\mathbf{t} \in \mathbb{R}^m$ en el espacio (que no necesariamente es un punto del retículo), encontrar el punto del retículo $\mathbf{v} \in \mathcal{L}(B)$ más cercano a $\mathbf{t}$.&lt;/p>
&lt;p>Expresado matemáticamente, es el problema de buscar un punto de retículo $\mathbf{v}$ tal que $\min_{\mathbf{v} \in \mathcal{L}(B)} \| \mathbf{v} - \mathbf{t} \|$.&lt;/p>
&lt;p>Al igual que SVP, CVP también es NP-difícil en altas dimensiones. Desde la perspectiva de la aplicación criptográfica, el problema LWE que discutiremos más adelante está estrechamente relacionado con una variante especial de este CVP (Decodificación de distancia acotada o Bounded Distance Decoding: BDD).&lt;/p>
&lt;h2 id="33-por-qué-es-insoluble-en-altas-dimensiones-los-límites-de-lll-y-bkz">3.3 ¿Por qué es insoluble en altas dimensiones? (Los límites de LLL y BKZ)
&lt;/h2>&lt;p>Un algoritmo famoso para resolver problemas de retículos de altas dimensiones es el algoritmo LLL (Lenstra-Lenstra-Lovász algorithm). El algoritmo LLL se ejecuta en tiempo polinomial y puede reducir en cierta medida la base del retículo a una &amp;ldquo;buena base&amp;rdquo;. Sin embargo, el vector más corto que el algoritmo LLL puede encontrar tiene un factor de aproximación exponencial ($2^{\mathcal{O}(n)}$) en relación con la longitud del vector más corto verdadero, por lo que no es suficiente para romper la seguridad criptográfica.&lt;/p>
&lt;p>Si se utiliza un algoritmo de reducción de bases más potente como el algoritmo BKZ (Block Korkine-Zolotarev), que es una mejora de LLL, se pueden encontrar vectores más cortos, pero su complejidad computacional aumenta exponencialmente con respecto al tamaño del bloque. En la criptografía de retículos, los parámetros seguros (como el tamaño de la dimensión $n$) se determinan estimando el tiempo de ejecución de este algoritmo BKZ. Con los parámetros estándar actuales de PQC, se eligen dimensiones $n$ de 500 a 1000 o más, y se considera que descifrarlos tomaría más tiempo que la edad del universo, incluso utilizando supercomputadoras o futuras computadoras cuánticas.&lt;/p>
&lt;h1 id="4-formulación-matemática-del-problema-lwe-learning-with-errors">4. Formulación matemática del problema LWE (Learning With Errors)
&lt;/h1>&lt;p>La mayor parte de la criptografía de retículos moderna se basa en el &amp;ldquo;Problema LWE (Learning With Errors)&amp;rdquo; propuesto por Oded Regev en 2005. La belleza del problema LWE radica en la simplicidad de su formulación y en el hecho de que cuenta con una poderosa prueba matemática de &amp;ldquo;reducción de la complejidad en el peor caso a la complejidad en el caso promedio&amp;rdquo;.&lt;/p>
&lt;h2 id="41-sistema-de-ecuaciones-lineales-sin-ruido">4.1 Sistema de ecuaciones lineales sin ruido
&lt;/h2>&lt;p>Para entender el problema LWE, primero consideremos un simple sistema de ecuaciones lineales sin ruido.
Supongamos que hay un vector secreto desconocido $\mathbf{s} \in \mathbb{Z}_q^n$ (donde cada componente es un número entero de $0$ a $q-1$). Aquí $q$ es un número primo.&lt;/p>
&lt;p>Elegimos vectores de coeficientes aleatorios $\mathbf{a}_1, \mathbf{a}_2, \dots \in \mathbb{Z}_q^n$ y calculamos su producto interno con el vector secreto $\mathbf{s}$ módulo $q$.
$b_1 = \langle \mathbf{a}_1, \mathbf{s} \rangle \pmod q$
$b_2 = \langle \mathbf{a}_2, \mathbf{s} \rangle \pmod q$
$\vdots$&lt;/p>
&lt;p>Si se nos da un número suficiente (al menos $n$) de pares $(\mathbf{a}_i, b_i)$, podemos recuperar fácilmente el vector secreto $\mathbf{s}$ usando la &amp;ldquo;Eliminación de Gauss (Gaussian elimination)&amp;rdquo; de álgebra lineal. Este es un problema que puede resolverse fácilmente en tiempo polinomial.&lt;/p>
&lt;h2 id="42-definición-del-problema-lwe-agregando-ruido">4.2 Definición del problema LWE: Agregando ruido
&lt;/h2>&lt;p>Entonces, ¿qué sucedería si añadimos un ligero &amp;ldquo;ruido (error)&amp;rdquo; a este problema?
Esta es la esencia del problema LWE.&lt;/p>
&lt;p>Para el vector secreto desconocido $\mathbf{s} \in \mathbb{Z}_q^n$, añadimos un pequeño error $e_i \in \mathbb{Z}_q$ al resultado de cada ecuación.
$b_i = \langle \mathbf{a}_i, \mathbf{s} \rangle + e_i \pmod q$&lt;/p>
&lt;p>Aquí, $e_i$ es un pequeño valor entero con media 0 y una desviación estándar relativamente pequeña (por ejemplo, elegido de una distribución gaussiana discreta, similar a una distribución normal).
La información proporcionada es una lista de pares de un vector aleatorio $\mathbf{a}_i$ y $b_i$ calculado añadiendo un error.
$( \mathbf{a}_1, b_1 ), ( \mathbf{a}_2, b_2 ), \dots, ( \mathbf{a}_m, b_m )$&lt;/p>
&lt;p>Representar esto en forma matricial lo hace muy claro.
Usando una matriz aleatoria $A \in \mathbb{Z}_q^{m \times n}$, un vector secreto $\mathbf{s} \in \mathbb{Z}_q^n$ y un vector de error $\mathbf{e} \in \mathbb{Z}_q^m$:
&lt;/p>
$$ \mathbf{b} = A \mathbf{s} + \mathbf{e} \pmod q $$
&lt;p>
Solo se nos proporciona $A$ y $\mathbf{b}$. Encontrar $\mathbf{s}$ a partir de aquí es el &amp;ldquo;Problema de búsqueda LWE (Search LWE problem)&amp;rdquo;.&lt;/p>
&lt;p>Debido a que el error $e_i$ está presente, intentar usar la eliminación de Gauss amplificaría el error de forma exponencial durante el proceso de sumar y restar ecuaciones, haciendo imposible llegar a la respuesta correcta. A primera vista, parece un simple sistema de ecuaciones lineales, pero al agregar este pequeño ruido, la dificultad del problema se eleva al nivel de NP-difícil.&lt;/p>
&lt;h2 id="43-problema-lwe-de-decisión-decision-lwe">4.3 Problema LWE de decisión (Decision LWE)
&lt;/h2>&lt;p>En las pruebas de la teoría criptográfica, la variante frecuentemente utilizada es el &amp;ldquo;Problema LWE de decisión (Decision LWE problem)&amp;rdquo;, que es una variante del problema de búsqueda LWE.&lt;/p>
&lt;p>El problema LWE de decisión es el problema de determinar de cuál de las siguientes dos distribuciones proviene una lista de muestras dada:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Distribución LWE&lt;/strong>: $(A, \mathbf{b} = A\mathbf{s} + \mathbf{e} \pmod q)$ calculada intencionadamente.&lt;/li>
&lt;li>&lt;strong>Distribución aleatoria uniforme&lt;/strong>: $(A, \mathbf{u})$ formada por una matriz $A$ y un vector $\mathbf{u}$ elegidos de forma completamente aleatoria.&lt;/li>
&lt;/ol>
&lt;p>Sorprendentemente, si se eligen adecuadamente los parámetros del problema LWE, los pares obtenidos de la distribución LWE se vuelven &amp;ldquo;computacionalmente indistinguibles (Computationally Indistinguishable)&amp;rdquo; de los pares de datos completamente aleatorios. Esta propiedad es la base que permite a la criptografía basada en LWE generar &amp;ldquo;textos cifrados indistinguibles de números aleatorios&amp;rdquo;.&lt;/p>
&lt;h2 id="44-reducción-de-la-complejidad-en-el-peor-caso-a-la-del-caso-promedio-teorema-de-regev">4.4 Reducción de la complejidad en el peor caso a la del caso promedio (Teorema de Regev)
&lt;/h2>&lt;p>El mayor logro de Oded Regev fue vincular matemáticamente la dificultad de este problema LWE con la dificultad de los problemas de retículos mencionados anteriormente (SVP y CVP).&lt;/p>
&lt;p>Utilizó la reducción cuántica (Quantum reduction) para demostrar que &amp;ldquo;si existe un algoritmo de tiempo polinomial capaz de resolver el problema LWE en el caso promedio (para $A$ y $\mathbf{e}$ elegidos al azar), entonces existe un algoritmo cuántico de tiempo polinomial capaz de resolver el Gap-SVP en el peor caso (el caso más difícil) para cualquier retículo&amp;rdquo;. (Posteriormente, Peikert y otros también demostraron una reducción clásica).&lt;/p>
&lt;p>Esta es una propiedad soñada en la teoría criptográfica. Esto se debe a que disipa la preocupación de que &amp;ldquo;la criptografía podría romperse si por casualidad elegimos una clave débil (una parte del caso promedio)&amp;rdquo; y ofrece una fuerte garantía de que &amp;ldquo;si se puede resolver el LWE promedio, entonces se pueden resolver todos los problemas difíciles del retículo (por lo tanto, el LWE es absolutamente difícil)&amp;rdquo;.&lt;/p>
&lt;div class="mermaid">graph TD
A["Problemas de retículos en el peor caso (Gap-SVP, SIVP)"] -->|Reducción Cuántica/Clásica| B["Problema LWE en el caso promedio"]
B -->|Construcción Criptográfica| C["Criptosistemas basados en LWE (PKE, KEM, FHE)"]
style A fill:#ffcccc,stroke:#ff0000,stroke-width:2px,color:#000
style B fill:#ccffcc,stroke:#00aa00,stroke-width:2px,color:#000
style C fill:#ccccff,stroke:#0000ff,stroke-width:2px,color:#000&lt;/div>
&lt;h1 id="5-construcción-de-un-sistema-de-criptografía-de-clave-pública-utilizando-lwe-criptografía-de-regev">5. Construcción de un sistema de criptografía de clave pública utilizando LWE (Criptografía de Regev)
&lt;/h1>&lt;p>Habiendo entendido la dificultad del problema LWE, veamos cómo funciona el cifrado y descifrado usándolo en el sistema de criptografía de clave pública básico propuesto por Oded Regev. Aquí explicaremos el mecanismo más básico para cifrar un mensaje de 1 bit $M \in \{0, 1\}$.&lt;/p>
&lt;h2 id="51-generación-de-claves-key-generation">5.1 Generación de claves (Key Generation)
&lt;/h2>&lt;ol>
&lt;li>Como parámetros del sistema, se determinan el módulo primo $q$, la dimensión $n$ y el número de ecuaciones $m$ ($m > n \log q$).&lt;/li>
&lt;li>Se elige al azar el vector secreto $\mathbf{s} \in \mathbb{Z}_q^n$ como clave privada.&lt;/li>
&lt;li>Se genera una matriz aleatoria $A \in \mathbb{Z}_q^{m \times n}$.&lt;/li>
&lt;li>Se selecciona un pequeño vector de error $\mathbf{e} \in \mathbb{Z}_q^m$ a partir de una distribución de errores como la distribución gaussiana discreta.&lt;/li>
&lt;li>Se calcula el vector $\mathbf{b} = A \mathbf{s} + \mathbf{e} \pmod q$.&lt;/li>
&lt;li>La clave pública (Public Key) será $(A, \mathbf{b})$.&lt;/li>
&lt;li>La clave secreta o privada (Secret Key) será $\mathbf{s}$.&lt;/li>
&lt;/ol>
&lt;p>La clave pública es precisamente una &amp;ldquo;instancia del problema LWE&amp;rdquo;. Dado que derivar la clave secreta $\mathbf{s}$ a partir de la clave pública $(A, \mathbf{b})$ equivale a resolver el problema de búsqueda LWE, la seguridad está garantizada.&lt;/p>
&lt;h2 id="52-cifrado-encryption">5.2 Cifrado (Encryption)
&lt;/h2>&lt;p>Alice cifra un mensaje de 1 bit $M \in \{0, 1\}$ utilizando la clave pública de Bob $(A, \mathbf{b})$.&lt;/p>
&lt;ol>
&lt;li>Elige un vector binario aleatorio (con componentes 0 o 1) $\mathbf{r} \in \{0, 1\}^m$.&lt;/li>
&lt;li>Como primera parte del texto cifrado, calcula el vector $\mathbf{u} = A^T \mathbf{r} \pmod q$. ($A^T$ es la matriz transpuesta de $A$. Es decir, suma las filas de $A$ donde el componente de $\mathbf{r}$ es 1).&lt;/li>
&lt;li>Como segunda parte del texto cifrado, calcula el escalar $v = \mathbf{b}^T \mathbf{r} + M \cdot \lfloor \frac{q}{2} \rfloor \pmod q$.
(Si el mensaje $M$ es 0, no suma nada; si es $1$, suma exactamente la mitad del valor de $q$, $\lfloor \frac{q}{2} \rfloor$).&lt;/li>
&lt;li>El texto cifrado (Ciphertext) será $(\mathbf{u}, v)$.&lt;/li>
&lt;/ol>
&lt;p>La implicación intuitiva del cifrado es tomar la &amp;ldquo;suma de un subconjunto aleatorio&amp;rdquo; de la matriz de la clave pública $A$ y del vector $\mathbf{b}$. Debido a la dificultad del problema LWE de decisión, este texto cifrado $(\mathbf{u}, v)$ parece indistinguible de un vector completamente aleatorio y de un número aleatorio uniforme (Seguridad semántica: Semantic Security).&lt;/p>
&lt;div class="mermaid">flowchart LR
M["Mensaje M en {0,1}"] --> Enc
PK["Clave Pública (A, b)"] --> Enc
r["Vector binario aleatorio r"] --> Enc
subgraph Enc ["Proceso de Cifrado"]
direction TB
u_calc["u = A^T * r mod q"]
v_calc["v = b^T * r + M * floor(q/2) mod q"]
end
Enc --> CT["Texto Cifrado (u, v)"]&lt;/div>
&lt;h2 id="53-descifrado-decryption">5.3 Descifrado (Decryption)
&lt;/h2>&lt;p>Bob descifra el texto cifrado $(\mathbf{u}, v)$ usando su clave secreta $\mathbf{s}$.&lt;/p>
&lt;ol>
&lt;li>Calcula el siguiente valor: $D = v - \mathbf{s}^T \mathbf{u} \pmod q$&lt;/li>
&lt;li>Si el resultado calculado está cerca de $0$, la salida es $M=0$; si está cerca de $\lfloor \frac{q}{2} \rfloor$, la salida es $M=1$.&lt;/li>
&lt;/ol>
&lt;p>Desarrollemos matemáticamente por qué se puede descifrar con esto.
Recuerde que $\mathbf{b} = A \mathbf{s} + \mathbf{e}$.&lt;/p>
$$
\begin{aligned}
v - \mathbf{s}^T \mathbf{u} &amp;= (\mathbf{b}^T \mathbf{r} + M \cdot \lfloor \frac{q}{2} \rfloor) - \mathbf{s}^T (A^T \mathbf{r}) \\
&amp;= ((A \mathbf{s} + \mathbf{e})^T \mathbf{r} + M \cdot \lfloor \frac{q}{2} \rfloor) - \mathbf{s}^T A^T \mathbf{r} \\
&amp;= (\mathbf{s}^T A^T \mathbf{r} + \mathbf{e}^T \mathbf{r} + M \cdot \lfloor \frac{q}{2} \rfloor) - \mathbf{s}^T A^T \mathbf{r} \\
&amp;= \mathbf{e}^T \mathbf{r} + M \cdot \lfloor \frac{q}{2} \rfloor \pmod q
\end{aligned}
$$
&lt;p>Aquí, $\mathbf{s}^T A^T \mathbf{r}$ se cancela claramente y desaparece de la ecuación.
Lo que queda es $\mathbf{e}^T \mathbf{r} + M \cdot \lfloor \frac{q}{2} \rfloor$.&lt;/p>
&lt;p>$\mathbf{e}$ es un vector de ruido con componentes muy pequeños, y $\mathbf{r}$ es un vector binario con componentes de 0 o 1. Por lo tanto, su producto interno $\mathbf{e}^T \mathbf{r}$ también permanece como un valor relativamente pequeño (si los parámetros se eligen adecuadamente).&lt;/p>
&lt;ul>
&lt;li>Si $M=0$, el resultado es $\mathbf{e}^T \mathbf{r}$, que será un valor pequeño cerca de $0$.&lt;/li>
&lt;li>Si $M=1$, el resultado es $\mathbf{e}^T \mathbf{r} + \lfloor \frac{q}{2} \rfloor$, que se situará alrededor de la mitad del valor de $q$, $\lfloor \frac{q}{2} \rfloor$.&lt;/li>
&lt;/ul>
&lt;p>Si los parámetros están diseñados para que el valor absoluto del error $\mathbf{e}^T \mathbf{r}$ sea menor a $\frac{q}{4}$, Bob puede determinar (descifrar) con precisión el mensaje $M$ simplemente verificando si el resultado del cálculo está más cerca de $0$ o de $\lfloor \frac{q}{2} \rfloor$. Este es el hermoso mecanismo por el cual funciona la criptografía basada en LWE.&lt;/p>
&lt;div class="mermaid">flowchart LR
CT["Texto Cifrado (u, v)"] --> Dec
SK["Clave Secreta s"] --> Dec
subgraph Dec ["Proceso de Descifrado"]
direction TB
calc["Calcular D = v - s^T * u mod q"]
check["Comprobar si D está más cerca de 0 o q/2"]
end
calc --> check
Dec --> M_out["Mensaje Recuperado M"]&lt;/div>
&lt;h1 id="6-ejemplo-ilustrativo-de-criptografía-lwe-usando-valores-numéricos-específicos">6. Ejemplo ilustrativo de criptografía LWE usando valores numéricos específicos
&lt;/h1>&lt;p>Es posible que resulte difícil comprender solo con una lista de fórmulas matemáticas, así que intentemos establecer parámetros numéricos muy pequeños y seguir los cálculos desde el cifrado hasta el descifrado.
(* En un sistema de criptografía real, se utilizan valores para $n$ mayores de 500 y para $q$ valores de miles o más para garantizar la seguridad).&lt;/p>
&lt;p>&lt;strong>[Configuración de parámetros]&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Módulo $q = 17$ (Un número primo. Por tanto, los valores están en el rango de $0$ a $16$).&lt;/li>
&lt;li>Dimensión $n = 2$&lt;/li>
&lt;li>Número de ecuaciones $m = 4$&lt;/li>
&lt;li>Supondremos que se cifrará el mensaje $M = 1$.&lt;/li>
&lt;li>Cantidad de desplazamiento del mensaje: $\lfloor \frac{q}{2} \rfloor = \lfloor \frac{17}{2} \rfloor = 8$&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>[1. Fase de generación de claves]&lt;/strong>
Bob elige su clave secreta $\mathbf{s}$, la matriz $A$ y el vector de error $\mathbf{e}$ al azar.
&lt;/p>
$$ \mathbf{s} = \begin{pmatrix} 3 \\ 4 \end{pmatrix} \in \mathbb{Z}_{17}^2 $$
$$ A = \begin{pmatrix} 2 &amp; 15 \\ 1 &amp; 8 \\ 14 &amp; 5 \\ 9 &amp; 10 \end{pmatrix} \in \mathbb{Z}_{17}^{4 \times 2} $$
$$ \mathbf{e} = \begin{pmatrix} 1 \\ -1 \\ 0 \\ 2 \end{pmatrix} \equiv \begin{pmatrix} 1 \\ 16 \\ 0 \\ 2 \end{pmatrix} \pmod{17} $$
&lt;p>Luego calcula la clave pública $\mathbf{b}$.
&lt;/p>
$$ A \mathbf{s} = \begin{pmatrix} 2 &amp; 15 \\ 1 &amp; 8 \\ 14 &amp; 5 \\ 9 &amp; 10 \end{pmatrix} \begin{pmatrix} 3 \\ 4 \end{pmatrix} = \begin{pmatrix} 2\times 3 + 15\times 4 \\ 1\times 3 + 8\times 4 \\ 14\times 3 + 5\times 4 \\ 9\times 3 + 10\times 4 \end{pmatrix} = \begin{pmatrix} 6 + 60 \\ 3 + 32 \\ 42 + 20 \\ 27 + 40 \end{pmatrix} = \begin{pmatrix} 66 \\ 35 \\ 62 \\ 67 \end{pmatrix} $$
&lt;p>
Calculando esto módulo 17 (como $66 = 17 \times 3 + 15$ etc.):
&lt;/p>
$$ A \mathbf{s} \pmod{17} = \begin{pmatrix} 15 \\ 1 \\ 11 \\ 16 \end{pmatrix} $$
&lt;p>
Sumando el vector de error $\mathbf{e}$:
&lt;/p>
$$ \mathbf{b} = A \mathbf{s} + \mathbf{e} = \begin{pmatrix} 15 \\ 1 \\ 11 \\ 16 \end{pmatrix} + \begin{pmatrix} 1 \\ 16 \\ 0 \\ 2 \end{pmatrix} = \begin{pmatrix} 16 \\ 17 \\ 11 \\ 18 \end{pmatrix} \equiv \begin{pmatrix} 16 \\ 0 \\ 11 \\ 1 \end{pmatrix} \pmod{17} $$
&lt;p>La clave pública es $A$ y $\mathbf{b} = (16, 0, 11, 1)^T$.&lt;/p>
&lt;p>&lt;strong>[2. Fase de cifrado]&lt;/strong>
Alice cifra el mensaje $M = 1$.
Elige un vector aleatorio $\mathbf{r}$. Aquí usaremos $\mathbf{r} = (1, 0, 1, 0)^T$.&lt;/p>
&lt;p>Calcula $\mathbf{u}$:
&lt;/p>
$$ \mathbf{u} = A^T \mathbf{r} = \begin{pmatrix} 2 &amp; 1 &amp; 14 &amp; 9 \\ 15 &amp; 8 &amp; 5 &amp; 10 \end{pmatrix} \begin{pmatrix} 1 \\ 0 \\ 1 \\ 0 \end{pmatrix} = \begin{pmatrix} 2 \times 1 + 14 \times 1 \\ 15 \times 1 + 5 \times 1 \end{pmatrix} = \begin{pmatrix} 16 \\ 20 \end{pmatrix} \equiv \begin{pmatrix} 16 \\ 3 \end{pmatrix} \pmod{17} $$
&lt;p>Calcula $v$:
&lt;/p>
$$ \mathbf{b}^T \mathbf{r} = (16, 0, 11, 1) \begin{pmatrix} 1 \\ 0 \\ 1 \\ 0 \end{pmatrix} = 16 \times 1 + 11 \times 1 = 27 \equiv 10 \pmod{17} $$
&lt;p>
Suma el valor correspondiente al mensaje $M=1$, $\lfloor 17/2 \rfloor = 8$:
&lt;/p>
$$ v = \mathbf{b}^T \mathbf{r} + M \cdot 8 = 10 + 1 \times 8 = 18 \equiv 1 \pmod{17} $$
&lt;p>Alice envía el texto cifrado $(\mathbf{u}, v) = \left( \begin{pmatrix} 16 \\ 3 \end{pmatrix}, 1 \right)$ a Bob.&lt;/p>
&lt;p>&lt;strong>[3. Fase de descifrado]&lt;/strong>
Bob, al recibir el texto cifrado, lo descifra usando su clave secreta $\mathbf{s} = (3, 4)^T$.
Fórmula de descifrado: Calcula $D = v - \mathbf{s}^T \mathbf{u} \pmod{17}$.&lt;/p>
$$ \mathbf{s}^T \mathbf{u} = (3, 4) \begin{pmatrix} 16 \\ 3 \end{pmatrix} = 3 \times 16 + 4 \times 3 = 48 + 12 = 60 \equiv 9 \pmod{17} $$
$$ D = v - \mathbf{s}^T \mathbf{u} = 1 - 9 = -8 \pmod{17} $$
&lt;p>Aquí, en el mundo del módulo 17, $-8$ es igual a $9$ ($-8 + 17 = 9$).
Determina si el valor obtenido $D = 9$ está más cerca de $0$ o de $8$ ($\lfloor 17/2 \rfloor$).
Dado que $9$ está claramente más cerca de $8$ que de $0$, ¡Bob logra restaurar correctamente $M = 1$!&lt;/p>
&lt;p>¿Por qué resultó 9? Recordemos la prueba anterior.
La parte de error es $\mathbf{e}^T \mathbf{r} = (1, -1, 0, 2) (1, 0, 1, 0)^T = 1 \times 1 + 0 \times 1 = 1$.
Por lo tanto, el resultado del cálculo es $\mathbf{e}^T \mathbf{r} + M \cdot 8 = 1 + 8 = 9$, confirmando que se ha calculado el valor tal como en la teoría.&lt;/p>
&lt;h1 id="7-evolución-hacia-la-aplicación-práctica-ring-lwe-y-module-lwe">7. Evolución hacia la aplicación práctica: Ring-LWE y Module-LWE
&lt;/h1>&lt;p>El problema LWE estándar (Standard LWE) explicado hasta ahora tiene pruebas de seguridad muy sólidas, pero en la práctica tiene defectos fatales. Estos son el &amp;ldquo;tamaño gigante de las claves&amp;rdquo; y el &amp;ldquo;alto costo computacional&amp;rdquo;.&lt;/p>
&lt;p>En Standard LWE, la clave pública incluye una matriz gigante $A \in \mathbb{Z}_q^{m \times n}$. Cuando el parámetro $n$ alcanza cifras entre cientos y miles, el tamaño de esta matriz llega a varios megabytes, lo que la hace demasiado pesada para enviar y recibir cada vez a través de protocolos de comunicación en internet (como TLS). Además, la multiplicación de matrices y vectores requiere una complejidad computacional de $\mathcal{O}(n^2)$.&lt;/p>
&lt;p>Para resolver estos problemas se introdujeron el &amp;ldquo;Ring-LWE (RLWE)&amp;rdquo; y el &amp;ldquo;Module-LWE (MLWE)&amp;rdquo;, que incorporan estructuras algebraicas llamadas anillos de polinomios (Polynomial rings) en los retículos.&lt;/p>
&lt;h2 id="71-intuición-de-ring-lwe">7.1 Intuición de Ring-LWE
&lt;/h2>&lt;p>En Ring-LWE, los vectores y matrices se sustituyen por elementos (polinomios) de un anillo de polinomios $\mathcal{R}_q = \mathbb{Z}_q[X]/(X^n + 1)$. (Donde $n$ se elige como una potencia de 2).&lt;/p>
&lt;p>Mientras que la clave pública en Standard LWE era una matriz $A$, en Ring-LWE se utiliza un solo polinomio $a(x)$. La clave secreta $s(x)$ y el error $e(x)$ también se convierten en polinomios.
La ecuación queda de la siguiente manera:
&lt;/p>
$$ b(x) = a(x) \cdot s(x) + e(x) \pmod q $$
&lt;p>Al tratarse de multiplicación de polinomios, utilizando la &amp;ldquo;Transformada Teórica de Números (Number Theoretic Transform: NTT)&amp;rdquo;, que es similar a la Transformada Rápida de Fourier (FFT), la complejidad computacional se puede reducir drásticamente a $\mathcal{O}(n \log n)$. Además, debido a que el tamaño de la clave pública se reduce de una matriz a un solo polinomio, el tamaño de los datos se reduce a $\mathcal{O}(n)$. Esto proporciona una ventaja abrumadora en términos de ancho de banda de comunicación.&lt;/p>
&lt;p>Matemáticamente, Ring-LWE se reduce a problemas en retículos con una simetría especial conocidos como &amp;ldquo;Retículos Ideales (Ideal Lattices)&amp;rdquo; en lugar de retículos generales.&lt;/p>
&lt;h2 id="72-module-lwe-y-estandarización-del-nist-kyber--ml-kem">7.2 Module-LWE y Estandarización del NIST (Kyber / ML-KEM)
&lt;/h2>&lt;p>Aunque Ring-LWE es eficiente, había una ligera preocupación de que la estructura algebraica especial del retículo ideal pudiera ser la base para futuros ataques. Por lo tanto, el &amp;ldquo;Module-LWE (MLWE)&amp;rdquo; tomó &amp;ldquo;lo mejor de ambos mundos&amp;rdquo;: la seguridad conservadora de Standard LWE y la eficiencia de Ring-LWE.&lt;/p>
&lt;p>En Module-LWE, consideramos matrices y vectores pequeños cuyos elementos son polinomios. Es decir, manejamos módulos sobre anillos.
Actualmente, &amp;ldquo;CRYSTALS-Kyber&amp;rdquo; (Nombre estandarizado: ML-KEM), que ha sido seleccionado por el NIST como estándar para el algoritmo de intercambio de claves PQC (KEM), está construido precisamente en base a la dificultad de este problema Module-LWE.&lt;/p>
&lt;h1 id="8-por-qué-es-seguro-contra-las-computadoras-cuánticas">8. ¿Por qué es seguro contra las computadoras cuánticas?
&lt;/h1>&lt;p>Finalmente, tocaremos el núcleo de la pregunta: &amp;ldquo;¿Por qué se cree que la criptografía basada en retículos no se puede descifrar ni siquiera usando computadoras cuánticas?&amp;rdquo;&lt;/p>
&lt;p>El algoritmo de Shor, que hace que las computadoras cuánticas rompan RSA y ECC, es esencialmente un algoritmo para resolver el &amp;ldquo;Problema del Subgrupo Oculto (Hidden Subgroup Problem: HSP)&amp;rdquo;. La estructura matemática (grupos abelianos finitos) detrás de RSA y ECC tiene periodicidad, y al usar una operación cuántica específica llamada Transformada Cuántica de Fourier (QFT), este período (el subgrupo oculto) se puede extraer de una vez.&lt;/p>
&lt;p>Sin embargo, los problemas de retículos son fundamentalmente diferentes. Aunque los retículos también tienen periodicidad, lo que se busca en SVP y CVP son propiedades geométricas no lineales, como la &amp;ldquo;distancia más corta&amp;rdquo; o la &amp;ldquo;eliminación del ruido&amp;rdquo;. Incluso aplicando directamente la &amp;ldquo;Transformada Cuántica de Fourier sobre un grupo abeliano&amp;rdquo; como en el algoritmo de Shor, no se puede extraer eficientemente información útil que resuelva el problema del retículo. Hasta la fecha, no se han descubierto algoritmos cuánticos capaces de resolver SVP o LWE en tiempo polinomial, y se cree ampliamente que, a pesar del poder de cálculo paralelo de las computadoras cuánticas, el único método efectivo es una búsqueda casi exhaustiva (aproximadamente una aceleración de la raíz cuadrada mediante el algoritmo de Grover).&lt;/p>
&lt;h1 id="9-conclusión">9. Conclusión
&lt;/h1>&lt;p>En este artículo, explicamos detalladamente la intuición matemática de la criptografía de retículos, comenzando con la definición geométrica de los retículos, seguido de la formulación del problema LWE, hasta la construcción de la criptografía de clave pública.&lt;/p>
&lt;ol>
&lt;li>Un &lt;strong>Retículo (Lattice)&lt;/strong> es un espacio discreto representado por combinaciones lineales con coeficientes enteros de vectores base, y en altas dimensiones resulta difícil encontrar una &amp;ldquo;buena base&amp;rdquo; casi ortogonal (SVP).&lt;/li>
&lt;li>El &lt;strong>Problema LWE (Learning With Errors)&lt;/strong> es un problema de resolver sistemas de ecuaciones lineales con ruido, y dado que esto está ligado a la dificultad del problema del retículo en el peor de los casos, proporciona una base sólida para la seguridad.&lt;/li>
&lt;li>Utilizando el problema LWE, el cifrado y descifrado (&lt;strong>Criptografía de Regev&lt;/strong>) se logran mediante un ingenioso mecanismo que intencionalmente añade y elimina el ruido.&lt;/li>
&lt;li>En los protocolos reales, se emplean &lt;strong>Ring-LWE&lt;/strong> o &lt;strong>Module-LWE&lt;/strong> usando anillos de polinomios para aumentar la eficiencia de comunicación y la velocidad de cálculo, sirviendo como la base del estándar &lt;strong>ML-KEM&lt;/strong> del NIST.&lt;/li>
&lt;/ol>
&lt;p>Con el cambio de paradigma computacional sin precedentes que representan las computadoras cuánticas acercándose, es muy fascinante que la &amp;ldquo;criptografía de retículos&amp;rdquo;, nacida del abismo del álgebra lineal clásica y la teoría de números, se convierta en la base de la seguridad del internet del futuro. Las matemáticas subyacentes a la criptografía de retículos no son excesivamente complejas, y con conocimientos básicos de álgebra lineal y probabilidades, es perfectamente posible comprender su hermosa estructura. Esperamos que este artículo sirva de ayuda para comprender la criptografía de retículos, el núcleo de la PQC.&lt;/p></description></item><item><title>La Hipótesis de Riemann y la Distribución de los Números Primos: Su Profunda Relación con la Criptografía Moderna</title><link>http://kenji.blog/es/p/riemann-hypothesis-prime-distribution-cryptography/</link><pubDate>Fri, 11 Sep 2026 16:00:00 +0900</pubDate><guid>http://kenji.blog/es/p/riemann-hypothesis-prime-distribution-cryptography/</guid><description>&lt;img src="http://kenji.blog/p/riemann-hypothesis-prime-distribution-cryptography/img/eyecatch.jpg" alt="Featured image of post La Hipótesis de Riemann y la Distribución de los Números Primos: Su Profunda Relación con la Criptografía Moderna" />&lt;h1 id="1-introducción-el-misterio-cósmico-de-los-números-primos-y-la-hipótesis-de-riemann">1. Introducción: El misterio cósmico de los números primos y la Hipótesis de Riemann
&lt;/h1>&lt;p>Los &amp;ldquo;números primos&amp;rdquo; (Prime Numbers) son números naturales divisibles únicamente por 1 y por sí mismos, y también se les llama los &amp;ldquo;átomos&amp;rdquo; del mundo de las matemáticas. Esta secuencia que continúa con 2, 3, 5, 7, 11, 13&amp;hellip; parece a primera vista aparecer de manera caótica y aleatoria. Desde que el matemático de la antigua Grecia, Euclides, demostró que &amp;ldquo;los números primos son infinitos&amp;rdquo;, innumerables matemáticos han intentado desentrañar la regularidad oculta en esta disposición de números primos.&lt;/p>
&lt;p>Quien más se acercó a este misterio de los números primos fue el matemático alemán Bernhard Riemann, quien en 1859 propuso la &lt;strong>&amp;ldquo;Hipótesis de Riemann&amp;rdquo; (Riemann Hypothesis)&lt;/strong>. La Hipótesis de Riemann es uno de los problemas más importantes y no resueltos de las matemáticas modernas, y tiene una recompensa de 1 millón de dólares al ser uno de los Problemas del Milenio establecidos por el Instituto Clay de Matemáticas.&lt;/p>
&lt;p>A primera vista, un problema tan difícil de matemáticas puras relacionado con la distribución de los números primos puede parecer ajeno a nuestra vida diaria. Sin embargo, la seguridad de Internet que sustenta la infraestructura de la sociedad moderna, en particular &lt;strong>las tecnologías criptográficas modernas como la criptografía RSA y la criptografía de curva elíptica (ECC)&lt;/strong>, dependen profundamente de las propiedades de los números primos gigantes.&lt;/p>
&lt;p>En este artículo, emprenderemos un viaje matemático desde la distribución de los números primos, pasando por el Teorema de los Números Primos, la función zeta de Riemann, hasta llegar al núcleo de la Hipótesis de Riemann. Profundizaremos en detalle sobre cómo se relaciona con la criptografía moderna y qué sucedería en el mundo si se demostrara la Hipótesis de Riemann.&lt;/p>
&lt;hr>
&lt;h1 id="2-el-teorema-de-los-números-primos-y-la-distribución-de-los-números-primos-el-descubrimiento-de-gauss">2. El Teorema de los Números Primos y la distribución de los números primos: El descubrimiento de Gauss
&lt;/h1>&lt;p>Para comprender cómo se distribuyen los números primos, los matemáticos consideraron la &lt;strong>función contadora de números primos (Prime-counting function)&lt;/strong> $\pi(x)$, que representa &amp;ldquo;cuántos números primos existen menores o iguales a un cierto número $x$&amp;rdquo;.&lt;/p>
&lt;p>Por ejemplo:&lt;/p>
&lt;ul>
&lt;li>$\pi(10) = 4$ (2, 3, 5, 7)&lt;/li>
&lt;li>$\pi(100) = 25$&lt;/li>
&lt;li>$\pi(1000) = 168$&lt;/li>
&lt;/ul>
&lt;p>El matemático prodigio de 15 años Carl Friedrich Gauss calculó tablas masivas de números primos y descubrió que la frecuencia de aparición de los números primos disminuye en proporción inversa al logaritmo natural $\ln x$. En otras palabras, conjeturó que la probabilidad de encontrar un número primo cerca de un cierto número $x$ es aproximadamente $\frac{1}{\ln x}$.&lt;/p>
&lt;p>La expresión de esto utilizando integrales es la &lt;strong>integral logarítmica (Logarithmic integral)&lt;/strong> $\text{Li}(x)$.&lt;/p>
$$ \text{Li}(x) = \int_{2}^{x} \frac{dt}{\ln t} $$
&lt;p>La conjetura de Gauss fue demostrada de forma independiente en 1896 por Jacques Hadamard y Charles-Jean de La Vallée Poussin, y se estableció como el &lt;strong>Teorema de los Números Primos (Prime Number Theorem, PNT)&lt;/strong>.&lt;/p>
$$ \lim_{x \to \infty} \frac{\pi(x)}{\text{Li}(x)} = 1 $$
&lt;p>O se expresa de manera aproximada de la siguiente forma:&lt;/p>
$$ \pi(x) \sim \frac{x}{\ln x} $$
&lt;p>Gracias a este teorema, sabemos que, a nivel macroscópico, los números primos tienen una distribución muy suave y predecible. Sin embargo, a nivel microscópico, siempre existe un &amp;ldquo;error&amp;rdquo; o &amp;ldquo;fluctuación&amp;rdquo; entre $\pi(x)$ y $\text{Li}(x)$. La verdadera naturaleza de esta fluctuación es exactamente el mayor misterio que la Hipótesis de Riemann intenta desentrañar.&lt;/p>
&lt;hr>
&lt;h1 id="3-la-función-zeta-de-riemann-y-el-producto-de-euler">3. La función zeta de Riemann y el producto de Euler
&lt;/h1>&lt;p>El arma más poderosa para analizar la distribución de los números primos es la &lt;strong>función zeta de Riemann (Riemann Zeta Function)&lt;/strong>. Originalmente fue una serie infinita definida por Leonhard Euler para números reales $s > 1$.&lt;/p>
$$ \zeta(s) = \sum_{n=1}^\infty \frac{1}{n^s} = 1 + \frac{1}{2^s} + \frac{1}{3^s} + \frac{1}{4^s} + \dots $$
&lt;p>Uno de los mayores logros de Euler fue demostrar que esta serie infinita podía expresarse como un producto infinito sobre todos los números primos $p$. Este es el &lt;strong>producto de Euler (Euler Product Formula)&lt;/strong>.&lt;/p>
$$ \zeta(s) = \prod_{p \text{ prime}} \frac{1}{1 - p^{-s}} = \left( \frac{1}{1 - 2^{-s}} \right) \left( \frac{1}{1 - 3^{-s}} \right) \left( \frac{1}{1 - 5^{-s}} \right) \dots $$
&lt;p>Una comprensión intuitiva de la demostración es que si expandimos cada término del lado derecho como una serie geométrica y los multiplicamos, gracias al teorema fundamental de la aritmética (todo número natural se puede expresar de forma única como el producto de números primos), se reconstruye completamente la suma de los recíprocos de los números naturales en el lado izquierdo.&lt;/p>
&lt;p>&lt;strong>Esta única fórmula se convirtió en el puente que conecta el análisis matemático (series infinitas y funciones continuas) y la teoría de números (números primos y números discretos).&lt;/strong> Estudiar la función zeta es sinónimo de estudiar la distribución de los números primos.&lt;/p>
&lt;hr>
&lt;h1 id="4-continuación-analítica-y-la-extensión-al-plano-complejo">4. Continuación analítica y la extensión al plano complejo
&lt;/h1>&lt;p>La genialidad de Riemann radica en haber extendido la variable $s$ de $\zeta(s)$, que Euler había considerado únicamente para números reales, a &lt;strong>números complejos $s = \sigma + it$ ($\sigma$ es la parte real, $t$ es la parte imaginaria)&lt;/strong>.&lt;/p>
&lt;p>La serie infinita original converge solo para $\sigma > 1$, pero Riemann utilizó una técnica llamada &amp;ldquo;continuación analítica&amp;rdquo; (Analytic Continuation) para extender la definición de manera que $\zeta(s)$ tenga sentido en todo el plano complejo, excluyendo el polo en $s = 1$.&lt;/p>
&lt;p>Además, dedujo una hermosa ecuación funcional (Functional equation) que satisface la función zeta.&lt;/p>
$$ \zeta(s) = 2^s \pi^{s-1} \sin\left(\frac{\pi s}{2}\right) \Gamma(1-s) \zeta(1-s) $$
&lt;p>Donde $\Gamma(x)$ es la función gamma. A través de esta ecuación, podemos conocer las propiedades del semiplano izquierdo a partir de las propiedades del semiplano derecho.&lt;/p>
&lt;h3 id="ceros-de-la-función-zeta-zeros-of-the-zeta-function">Ceros de la Función Zeta (Zeros of the Zeta Function)
&lt;/h3>&lt;p>Los números complejos $s$ para los cuales el valor de la función zeta se vuelve 0 se denominan &amp;ldquo;ceros&amp;rdquo;.
A partir de la ecuación funcional, cuando $s$ es un número par negativo ($-2, -4, -6, \dots$), dado que $\sin(\pi s / 2)$ se vuelve 0, se tiene $\zeta(s) = 0$. Estos se conocen como &lt;strong>ceros triviales (Trivial zeros)&lt;/strong>.&lt;/p>
&lt;p>Sin embargo, lo que es importante para la distribución de los números primos son los otros ceros, es decir, los &lt;strong>ceros no triviales (Non-trivial zeros)&lt;/strong> que existen en la &amp;ldquo;banda crítica&amp;rdquo; (Critical strip) donde $0 \le \sigma \le 1$.&lt;/p>
&lt;hr>
&lt;h1 id="5-el-núcleo-de-la-hipótesis-de-riemann-y-la-fórmula-explícita">5. El núcleo de la Hipótesis de Riemann y la fórmula explícita
&lt;/h1>&lt;p>Riemann calculó unos pocos ceros y formuló una conjetura sorprendente. Esta es la &lt;strong>Hipótesis de Riemann&lt;/strong>.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Hipótesis de Riemann (Riemann Hypothesis)&lt;/strong>
Todos los ceros no triviales de la función zeta de Riemann $\zeta(s)$ se encuentran en la línea recta donde la parte real es igual a $1/2$ ($\text{Re}(s) = 1/2$).&lt;/p>
&lt;/blockquote>
&lt;p>A esta recta donde la parte real es 1/2 se le llama la &amp;ldquo;línea crítica&amp;rdquo; (Critical line).&lt;/p>
&lt;div class="mermaid">graph TD
A["Función zeta de Riemann ζ(s)"] --> B["Extensión al plano complejo mediante continuación analítica"]
B --> C["Ceros triviales (s = -2, -4, -6 ...)"]
B --> D["Ceros no triviales (0 &lt;= Re(s) &lt;= 1)"]
D --> E["Hipótesis de Riemann"]
E --> F["Todos los ceros no triviales están en Re(s) = 1/2"]
F --> G["Hacia la demostración del límite del término de error de la distribución de primos"]&lt;/div>
&lt;p>¿Por qué es tan importante la Hipótesis de Riemann? Porque los ceros de la función zeta determinan &lt;strong>completamente&lt;/strong> la distribución de los números primos.&lt;/p>
&lt;p>Riemann y el posterior matemático von Mangoldt derivaron una &amp;ldquo;fórmula explícita&amp;rdquo; (Explicit formula) que describe con precisión la distribución de los números primos. Utilizando la función de Chebyshev $\psi(x)$, se expresa de la siguiente manera:&lt;/p>
$$ \psi(x) = x - \sum_{\rho} \frac{x^\rho}{\rho} - \ln(2\pi) - \frac{1}{2}\ln(1 - x^{-2}) $$
&lt;p>Aquí, $\rho$ es la suma sobre todos los ceros no triviales de la función zeta.
El término principal es $x$ (que corresponde al Teorema de los Números Primos), y al sumar y restar los términos ondulatorios que dependen de los ceros $\rho$, se restaura la distribución precisa en forma de escalera de los números primos. Se puede decir que los ceros no triviales representan la &amp;ldquo;frecuencia (onda)&amp;rdquo; de la distribución de los números primos.&lt;/p>
&lt;p>Si la Hipótesis de Riemann es correcta y la parte real de todos los ceros no triviales $\rho$ es exactamente $1/2$, entonces el término de error del Teorema de los Números Primos se mantendrá dentro del rango mínimo teóricamente concebible.&lt;/p>
$$ |\pi(x) - \text{Li}(x)| \le \frac{1}{8\pi} \sqrt{x} \ln x \quad \text{for} \quad x \ge 2657 $$
&lt;p>En otras palabras, &lt;strong>si la Hipótesis de Riemann es verdadera, se demuestra que los números primos están distribuidos de la manera más &amp;ldquo;regular y hermosa&amp;rdquo; que podamos imaginar&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h1 id="6-la-relación-inseparable-entre-la-criptografía-moderna-y-los-números-primos">6. La relación inseparable entre la criptografía moderna y los números primos
&lt;/h1>&lt;p>Hasta aquí hemos estado en el profundo mundo de las matemáticas puras, pero las propiedades de estos números primos sostienen desde sus cimientos a la sociedad digital moderna. El principal representante de esto es la criptografía de clave pública, liderada por la &lt;strong>criptografía RSA&lt;/strong>.&lt;/p>
&lt;p>La seguridad de todas las comunicaciones, como los pagos con tarjeta de crédito en Internet, la transmisión de contraseñas y las firmas digitales de blockchain, depende de los &amp;ldquo;números primos&amp;rdquo;.&lt;/p>
&lt;h3 id="funcionamiento-de-la-criptografía-rsa">Funcionamiento de la criptografía RSA
&lt;/h3>&lt;p>La seguridad de la criptografía RSA se basa en el hecho matemático de que &amp;ldquo;la factorización en números primos de números compuestos de muchos dígitos es extremadamente difícil&amp;rdquo; (el problema de la factorización de enteros).&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Generación de claves&lt;/strong>:
Se eligen aleatoriamente dos números primos gigantes, $p$ y $q$ (por ejemplo, de 2048 bits cada uno).
Se multiplican para calcular $N = p \times q$. Este $N$ se convierte en parte de la clave pública.
Utilizando la función indicatriz de Euler $\phi(N) = (p-1)(q-1)$, se genera la clave privada $d$.&lt;/p>
$$ e \times d \equiv 1 \pmod{\phi(N)} $$
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Cifrado y descifrado&lt;/strong>:
El texto plano $M$ se transforma en el texto cifrado $C$ utilizando la clave pública $e, N$.
&lt;/p>
$$ C \equiv M^e \pmod{N} $$
&lt;p>
Solo quien posee la clave privada $d$ puede descifrarlo.
&lt;/p>
$$ M \equiv C^d \pmod{N} $$
&lt;/li>
&lt;/ol>
&lt;div class="mermaid">graph LR
A["Texto plano (Plaintext)"] --> B["Cifrado con clave pública (e, N)"]
B --> C["Texto cifrado (Ciphertext)"]
C --> D["Descifrado con clave privada (d)"]
D --> E["Texto plano original"]
F["Atacante (Attacker)"] -- "Intenta la factorización prima de N" --> C
F -.-> G["Si p y q son desconocidos, d es incalculable"]&lt;/div>
&lt;p>Para romper la criptografía RSA, es necesario encontrar los números primos originales $p$ y $q$ a partir del enorme $N$ (factorización prima). Incluso utilizando los algoritmos convencionales actuales (como la criba general del cuerpo de números: GNFS, por sus siglas en inglés), factorizar un número de cientos de dígitos tomaría un tiempo que supera con creces la edad del universo, incluso utilizando supercomputadoras.&lt;/p>
&lt;hr>
&lt;h1 id="7-el-impacto-de-la-hipótesis-de-riemann-en-la-criptografía">7. El impacto de la Hipótesis de Riemann en la criptografía
&lt;/h1>&lt;p>Entonces, ¿cómo se cruzan la &amp;ldquo;Hipótesis de Riemann&amp;rdquo;, que se encuentra en la cima de las matemáticas puras, y la &amp;ldquo;criptografía&amp;rdquo;?&lt;/p>
&lt;h3 id="71-algoritmos-de-generación-de-números-primos-test-de-primalidad-y-la-hipótesis-generalizada-de-riemann-grh">7.1. Algoritmos de generación de números primos (test de primalidad) y la Hipótesis Generalizada de Riemann (GRH)
&lt;/h3>&lt;p>Para operar la criptografía RSA, primero es necesario generar los primos gigantes $p$ y $q$. Sin embargo, no es fácil determinar de manera segura y rápida si &amp;ldquo;un número es primo o no&amp;rdquo;.&lt;/p>
&lt;p>Actualmente, lo que se utiliza de forma práctica es un algoritmo probabilístico llamado el &lt;strong>test de primalidad de Miller-Rabin (Miller-Rabin primality test)&lt;/strong>. Este algoritmo es rápido, pero conlleva el riesgo de identificar incorrectamente un número compuesto como primo con una probabilidad extremadamente baja (un &amp;ldquo;pseudoprimo&amp;rdquo;).&lt;/p>
&lt;p>Sin embargo, si asumimos como cierta la &lt;strong>&amp;ldquo;Hipótesis Generalizada de Riemann&amp;rdquo; (Generalized Riemann Hypothesis, GRH)&lt;/strong>, que extiende la Hipótesis de Riemann a las funciones L de Dirichlet, la historia cambia drásticamente.
Si la GRH es verdadera, se garantiza matemáticamente un límite superior en el número de pruebas en el test de Miller-Rabin, elevándolo de un algoritmo probabilístico a un &lt;strong>&amp;ldquo;algoritmo determinista de tiempo polinómico&amp;rdquo;&lt;/strong> (este era un hecho crucial conocido incluso antes de que se descubriera el test de primalidad AKS).&lt;/p>
&lt;p>En otras palabras, la Hipótesis de Riemann (y sus generalizaciones) desempeña el papel de otorgar una garantía directa a la creación de los cimientos de la criptografía: &amp;ldquo;si es posible generar números primos gigantes a gran velocidad y con absoluta confianza&amp;rdquo;.&lt;/p>
&lt;h3 id="72-relación-con-los-algoritmos-de-factorización">7.2. Relación con los algoritmos de factorización
&lt;/h3>&lt;p>A la hora de evaluar la complejidad computacional de los algoritmos de quienes intentan descifrar la criptografía (como la criba general del cuerpo de números), también es indispensable el conocimiento sobre la distribución de los números primos. Muchos algoritmos de factorización dependen de la distribución de &amp;ldquo;números lisos&amp;rdquo; (Smooth numbers: números que solo tienen factores primos pequeños).&lt;/p>
&lt;p>Para evaluar rigurosamente con qué frecuencia aparecen los números lisos, se requiere una profunda comprensión de la distribución de los números primos, y es aquí donde también se emplean técnicas de teoría analítica de números que están directamente vinculadas a la función zeta y a la Hipótesis de Riemann. Si se demuestra la Hipótesis de Riemann y se determina completamente el error en la distribución de los números primos, también será posible identificar de manera más precisa los límites de rendimiento de los algoritmos de factorización en números primos.&lt;/p>
&lt;hr>
&lt;h1 id="8-si-se-demuestra-la-hipótesis-de-riemann-se-romperá-la-criptografía">8. Si se demuestra la Hipótesis de Riemann, ¿se romperá la criptografía?
&lt;/h1>&lt;p>A veces se cuenta como una leyenda urbana que &amp;ldquo;si se resuelve la Hipótesis de Riemann, la criptografía RSA colapsará en un instante&amp;rdquo;, pero &lt;strong>esto es matemáticamente inexacto&lt;/strong>.&lt;/p>
&lt;p>La demostración de la Hipótesis de Riemann en sí misma no produciría inmediatamente un algoritmo mágico que acelere drásticamente la factorización de números primos. Esto se debe a que la Hipótesis de Riemann es simplemente un teorema sobre la &amp;ldquo;regularidad de la distribución macroscópica&amp;rdquo; de los números primos, y no nos indica directamente qué números primos dividen a un número particular $N$ (propiedad local).&lt;/p>
&lt;p>Sin embargo, el impacto no es nulo.
Existe una probabilidad extremadamente alta de que, en el proceso de demostrar la Hipótesis de Riemann, se descubran &lt;strong>&amp;ldquo;nuevas herramientas matemáticas&amp;rdquo; y &amp;ldquo;métodos analíticos desconocidos&amp;rdquo;&lt;/strong>. Si observamos la historia, cuando se demostró el Último Teorema de Fermat o la Conjetura de Poincaré, las nuevas teorías desarrolladas en el proceso hicieron avanzar enormemente a todas las matemáticas.&lt;/p>
&lt;p>Si se establecen métodos desconocidos de geometría algebraica o geometría no conmutativa que puedan manipular por completo las propiedades de los ceros de la función zeta de Riemann, no se puede descartar la posibilidad de que conduzcan al descubrimiento de algoritmos de factorización innovadores (por ejemplo, algoritmos clásicos que reduzcan la complejidad computacional a tiempo polinómico). En ese sentido, los criptógrafos nunca pueden quitarle los ojos de encima al progreso de la Hipótesis de Riemann.&lt;/p>
&lt;h3 id="las-computadoras-cuánticas-y-el-algoritmo-de-shor">Las computadoras cuánticas y el algoritmo de Shor
&lt;/h3>&lt;p>Una amenaza más directa y realista para la criptografía no es la demostración de la Hipótesis de Riemann, sino &lt;strong>las computadoras cuánticas&lt;/strong>. El &amp;ldquo;algoritmo de Shor&amp;rdquo;, publicado por Peter Shor en 1994, demostró que si existiera una computadora cuántica con suficiente capacidad, la factorización en números primos podría resolverse en tiempo polinómico. Con esto, la criptografía RSA y la criptografía de curva elíptica quedarían fundamentalmente rotas.&lt;/p>
&lt;p>Actualmente, en todo el mundo se está promoviendo la transición hacia la &amp;ldquo;criptografía post-cuántica (Post-Quantum Cryptography, PQC)&amp;rdquo; (como la criptografía basada en retículos), que no puede ser descifrada ni siquiera por computadoras cuánticas. La tecnología criptográfica que depende de los números primos puede, en cierto sentido, estar llegando al final de su edad de oro, pero el valor matemático de los números primos en sí mismos nunca se perderá.&lt;/p>
&lt;hr>
&lt;h1 id="9-conclusión-la-intersección-entre-la-abstracción-matemática-y-la-sociedad-real">9. Conclusión: La intersección entre la abstracción matemática y la sociedad real
&lt;/h1>&lt;div class="mermaid">graph TD
A["Búsqueda en las matemáticas puras"] --> B["Resolución de la Hipótesis de Riemann"]
B --> C["Comprensión completa de la distribución de números primos"]
C --> D["Desarrollo espectacular de la teoría de números y geometría algebraica"]
D -.-> E["Posibilidad de nuevos algoritmos de factorización prima"]
E -.-> F["Actualización de la evaluación de seguridad de la criptografía"]
A --> G["Matemáticas aplicadas e informática"]
G --> H["Mejora en la eficiencia del test de primalidad y generación criptográfica"]
H --> F&lt;/div>
&lt;p>La incansable búsqueda sobre los números primos que continúa desde la antigua Grecia fue sublimada en una hermosa sinfonía sobre el plano complejo (los ceros de la función zeta) por un genio llamado Riemann. Y sorprendentemente, ese cristal de matemáticas puras y puristas se está aplicando, siglos después, como el escudo más fuerte para garantizar la seguridad de la sociedad de Internet.&lt;/p>
&lt;p>La Hipótesis de Riemann es una entidad que simboliza simultáneamente la &amp;ldquo;belleza abstracta&amp;rdquo; que poseen las matemáticas y su &amp;ldquo;asombrosa aplicabilidad al mundo físico y a la sociedad real&amp;rdquo;.&lt;/p>
&lt;p>Cuando esta gigantesca montaña matemática, a cuya cima aún nadie ha llegado, sea conquistada algún día, comprenderemos por completo la verdad cósmica de los números primos, y al mismo tiempo obtendremos una nueva perspectiva sobre los cimientos de la sociedad de la información. Aprender sobre criptografía es, de hecho, un viaje que recorre la historia de la sabiduría de la humanidad.&lt;/p></description></item><item><title>Algoritmo rápido de prueba de primalidad implementado en C++ (método de Miller-Rabin, etc.)</title><link>http://kenji.blog/es/p/cpp-fast-prime-testing-miller-rabin/</link><pubDate>Fri, 11 Sep 2026 14:00:00 +0900</pubDate><guid>http://kenji.blog/es/p/cpp-fast-prime-testing-miller-rabin/</guid><description>&lt;img src="http://kenji.blog/p/cpp-fast-prime-testing-miller-rabin/img/eyecatch.jpg" alt="Featured image of post Algoritmo rápido de prueba de primalidad implementado en C++ (método de Miller-Rabin, etc.)" />&lt;h1 id="introducción-por-qué-es-necesaria-una-prueba-rápida-de-primalidad">Introducción: ¿Por qué es necesaria una prueba rápida de primalidad?
&lt;/h1>&lt;p>En el mundo de las ciencias de la computación, la criptografía y la programación competitiva, determinar rápida y precisamente &amp;ldquo;si un número es primo o no&amp;rdquo; es un problema muy importante y fundamental. Por ejemplo, la criptografía de clave pública como el cifrado RSA, que respalda la seguridad de la sociedad de internet moderna, basa su seguridad en la generación de números primos enormes y la dificultad de su multiplicación (la dificultad de la factorización de enteros). Por lo tanto, no es exagerado decir que la tecnología para identificar instantáneamente si un número enorme es primo o no es la tecnología que sostiene los cimientos de la sociedad digital.&lt;/p>
&lt;p>Además, en la programación competitiva (como AtCoder o Codeforces), la prueba de primalidad es un tema frecuente. Para una entrada enorme con restricciones como $N \le 10^{18}$, en situaciones donde se deben realizar decenas de miles de pruebas de primalidad en menos de un segundo, los algoritmos tradicionales e ingenuos simplemente no alcanzarían el tiempo de cálculo (Time Limit Exceeded: TLE).&lt;/p>
&lt;p>En este artículo, comenzaremos con algoritmos ingenuos de prueba de primalidad, pasaremos a la prueba probabilística de primalidad conocida como &amp;ldquo;Prueba de Fermat&amp;rdquo;, y luego explicaremos exhaustivamente el &amp;ldquo;Test de primalidad de Miller-Rabin&amp;rdquo;, un algoritmo de alta velocidad de nivel práctico que supera sus debilidades. Cubriremos todo, desde los antecedentes matemáticos hasta implementaciones altamente optimizadas en C++. En particular, para enteros de 64 bits ($N &lt; 2^{64}$), no solo nos limitaremos a una prueba probabilística, sino que también explicaremos detalladamente un método que &amp;ldquo;puede determinar la primalidad con 100% de certeza (prueba determinista)&amp;rdquo; y proporcionaremos un código fuente en C++ listo para usar en la práctica.&lt;/p>
&lt;hr>
&lt;h1 id="1-fundamentos-de-la-prueba-de-primalidad-y-el-método-de-división-por-tentativa-trial-division">1. Fundamentos de la prueba de primalidad y el método de división por tentativa (Trial Division)
&lt;/h1>&lt;p>Un número primo (Prime number) es un número natural mayor o igual a 2 que no tiene divisores positivos aparte de 1 y él mismo. Siguiendo estrictamente la definición de número primo, para determinar si un entero $N$ es primo, se puede intentar dividir $N$ por todos los enteros desde $2$ hasta $N-1$. Si no se puede dividir de forma exacta por ninguno, se considera primo; si se divide de forma exacta al menos una vez, es un número compuesto (no primo).&lt;/p>
&lt;p>Sin embargo, este método tiene una complejidad computacional de $O(N)$, y cuando $N$ es un número enorme como $10^{18}$, incluso las computadoras modernas tardarían una cantidad enorme de tiempo en calcularlo.&lt;/p>
&lt;h2 id="optimización-de-la-división-por-tentativa-búsqueda-hasta-sqrtn">Optimización de la división por tentativa: Búsqueda hasta $\sqrt{N}$
&lt;/h2>&lt;p>Cuando un número compuesto $N$ se expresa como $a \times b = N$ (donde $a \le b$), siempre se cumple que $a \le \sqrt{N}$. Por lo tanto, el bucle para la prueba de primalidad no necesita repetirse hasta $N-1$; es suficiente verificar hasta $\sqrt{N}$.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Prueba de primalidad usando división por tentativa (O(sqrt(N)))
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">bool&lt;/span> &lt;span class="nf">is_prime_trial_division&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Solo verificar números impares mayores o iguales a 3
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>La complejidad computacional de este algoritmo es $O(\sqrt{N})$. Si $N \le 10^{12}$, se puede calcular instantáneamente, pero si $N \approx 10^{18}$, el número de iteraciones del bucle será de aproximadamente $10^9$. Incluso en C++, esto puede tomar desde cientos de milisegundos hasta varios segundos, lo que lo hace inadecuado para múltiples pruebas.&lt;/p>
&lt;hr>
&lt;h1 id="2-prueba-de-fermat-el-comienzo-de-la-prueba-probabilística-de-primalidad">2. Prueba de Fermat: El comienzo de la prueba probabilística de primalidad
&lt;/h1>&lt;p>Para superar los límites del método de división por tentativa, se idearon &amp;ldquo;Algoritmos probabilísticos (Probabilistic Algorithm)&amp;rdquo; utilizando teoremas de la teoría de números. Un ejemplo representativo es la &amp;ldquo;Prueba de Fermat (Fermat Primality Test)&amp;rdquo;, que utiliza el pequeño teorema de Fermat.&lt;/p>
&lt;h2 id="pequeño-teorema-de-fermat-fermats-little-theorem">Pequeño teorema de Fermat (Fermat&amp;rsquo;s Little Theorem)
&lt;/h2>&lt;p>Este teorema, descubierto por Pierre de Fermat, afirma lo siguiente:&lt;/p>
&lt;blockquote>
&lt;p>Para cualquier número primo $p$ y cualquier entero $a$ que sea coprimo con $p$ (que no sea un múltiplo de $p$), se cumple la siguiente congruencia:
&lt;/p>
$$ a^{p-1} \equiv 1 \pmod p $$
&lt;/blockquote>
&lt;p>Tomando la contrapositiva de este teorema, se puede decir que &amp;ldquo;si para algún entero $N$ y un entero $a$ coprimo con $N$, $a^{N-1} \not\equiv 1 \pmod N$, entonces $N$ es definitivamente un número compuesto&amp;rdquo;. Utilizando esta propiedad, la prueba de Fermat elige una base aleatoria (base) $a$ para el número a probar $N$, y verifica si $a^{N-1} \pmod N$ es igual a $1$.&lt;/p>
&lt;h2 id="exponenciación-modular-rápida-exponenciación-binaria">Exponenciación modular rápida (Exponenciación binaria)
&lt;/h2>&lt;p>Para llevar a cabo la prueba de Fermat, es necesario calcular rápidamente potencias enormes como $a^{N-1} \pmod N$. Para esto, se utiliza el método de &amp;ldquo;exponenciación binaria (Modular Exponentiation / Binary Exponentiation)&amp;rdquo;. La complejidad computacional se convierte en $O(\log N)$, lo cual es muy rápido.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Cálculo de a^b mod m usando exponenciación binaria
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="nf">mod_pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span> &lt;span class="o">%=&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">__int128_t&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">res&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">__int128_t&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">b&lt;/span> &lt;span class="o">&amp;gt;&amp;gt;=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">res&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>※ Para evitar el desbordamiento, aquí se usa la extensión GCC/Clang &lt;code>__int128_t&lt;/code> (entero de 128 bits) para mantener el producto intermedio.&lt;/p>
&lt;h2 id="pseudoprimos-y-números-de-carmichael-carmichael-numbers">Pseudoprimos y números de Carmichael (Carmichael Numbers)
&lt;/h2>&lt;p>La prueba de Fermat es muy poderosa, pero tiene una debilidad fatal. Resulta que existen números $N$ que son compuestos, pero para los cuales $a^{N-1} \equiv 1 \pmod N$ se cumple para todos los $a$ ($a$ coprimos con $N$).&lt;/p>
&lt;p>A estos números se les llama &amp;ldquo;pseudoprimos absolutos&amp;rdquo; o &amp;ldquo;números de Carmichael&amp;rdquo;. El número de Carmichael más pequeño es $561 = 3 \times 11 \times 17$.
Debido a la existencia de los números de Carmichael, no se puede realizar una prueba determinista &amp;ldquo;100% segura&amp;rdquo; utilizando solo la prueba de Fermat. No importa cuántas $a$ diferentes se prueben, números como el $561$ siempre simularán ser primos (engañarán a la prueba).&lt;/p>
&lt;hr>
&lt;h1 id="3-test-de-primalidad-de-miller-rabin-miller-rabin-primality-test">3. Test de primalidad de Miller-Rabin (Miller-Rabin Primality Test)
&lt;/h1>&lt;p>La debilidad de la prueba de Fermat (la existencia de los números de Carmichael) fue superada brillantemente por el &amp;ldquo;Test de primalidad de Miller-Rabin&amp;rdquo;, ideado por Gary L. Miller y Michael O. Rabin.
En la actualidad, como un algoritmo rápido y práctico de prueba de primalidad, es el más ampliamente utilizado en las bibliotecas internas de varios lenguajes de programación y en la generación de claves para sistemas criptográficos.&lt;/p>
&lt;h2 id="principio-matemático">Principio matemático
&lt;/h2>&lt;p>El algoritmo de Miller-Rabin, además del pequeño teorema de Fermat, utiliza la propiedad de que &amp;ldquo;en el cuerpo residual módulo un primo ($\mathbb{Z}/p\mathbb{Z}$), las soluciones de $x^2 \equiv 1 \pmod p$ están restringidas a $x \equiv 1$ o $x \equiv -1$&amp;rdquo; (si el módulo es un número compuesto, pueden existir otras raíces cuadradas no triviales además de estas).&lt;/p>
&lt;p>Para un número impar $N$ a evaluar, restar $1$ da como resultado $N-1$, que siempre es par. Entonces, dividimos $N-1$ por $2$ tantas veces como sea posible y lo expresamos en la siguiente forma:
&lt;/p>
$$ N-1 = d \cdot 2^s $$
&lt;p>
(donde $d$ es impar, $s \ge 1$)&lt;/p>
&lt;p>Para cualquier base $a$ ($1 &lt; a &lt; N-1$), verificamos si $a^{N-1} \equiv 1 \pmod N$ según el pequeño teorema de Fermat, pero realizamos ese cálculo paso a paso.
Específicamente, repetimos el cuadrado secuencialmente como $a^d, a^{d \cdot 2}, a^{d \cdot 4}, \ldots, a^{d \cdot 2^s}$.&lt;/p>
&lt;p>La condición para que la prueba de Miller-Rabin determine que $N$ es &amp;ldquo;primo (o muy probablemente primo)&amp;rdquo; es que se cumpla &lt;strong>al menos una&lt;/strong> de las siguientes condiciones:&lt;/p>
&lt;ol>
&lt;li>$a^d \equiv 1 \pmod N$&lt;/li>
&lt;li>Existe algún $r$ ($0 \le r &lt; s$) tal que $a^{d \cdot 2^r} \equiv -1 \pmod N$.
※ En la operación módulo de C++, $-1 \pmod N$ se convierte en $N-1$.&lt;/li>
&lt;/ol>
&lt;p>Si $N$ es primo, esta condición siempre se cumplirá para cualquier $a$. Por el contrario, si $N$ es compuesto, al elegir un $a$ aleatorio, se ha demostrado matemáticamente que la probabilidad de que se cumpla esta condición (probabilidad de ser engañado) es menor o igual a $\frac{1}{4}$.
Si se realizan $k$ pruebas independientes, la probabilidad de un falso positivo se vuelve $\left(\frac{1}{4}\right)^k$ o menor, lo que en la práctica puede considerarse cero. No existen números que &amp;ldquo;puedan engañar siempre&amp;rdquo;, como los números de Carmichael.&lt;/p>
&lt;h2 id="flujo-del-algoritmo-del-método-miller-rabin-diagrama-de-flujo-mermaid">Flujo del algoritmo del método Miller-Rabin (Diagrama de flujo Mermaid)
&lt;/h2>&lt;p>El siguiente diagrama muestra el flujo lógico de una sola prueba del test de primalidad de Miller-Rabin (para una base $a$).&lt;/p>
&lt;div class="mermaid">graph TD
Start["Inicio de la prueba (Entrada: N, a)"] --> CalcDS["Calcular d (impar) y s tal que N-1 = d * 2^s"]
CalcDS --> CalcX["Calcular x = a^d mod N"]
CalcX --> CheckX1{"¿x == 1 o x == N-1 ?"}
CheckX1 -- "Sí" --> ReturnTrue["Puede ser primo (Probably Prime)"]
CheckX1 -- "No" --> LoopStart["Iniciar bucle desde r = 1 hasta s-1"]
LoopStart --> LoopCondition{"¿r &lt; s ?"}
LoopCondition -- "No" --> ReturnFalse["Definitivamente compuesto (Composite)"]
LoopCondition -- "Sí" --> SquareX["Calcular x = (x * x) mod N"]
SquareX --> CheckXMinus1{"¿x == N - 1 ?"}
CheckXMinus1 -- "Sí" --> ReturnTrue
CheckXMinus1 -- "No" --> CheckXOne{"¿x == 1 ?"}
CheckXOne -- "Sí" --> ReturnFalse
CheckXOne -- "No" --> LoopNext["Incrementar r en 1 y continuar"]
LoopNext --> LoopCondition&lt;/div>
&lt;hr>
&lt;h1 id="4-prueba-determinista-para-enteros-de-64-bits">4. Prueba determinista para enteros de 64 bits
&lt;/h1>&lt;p>El test de primalidad de Miller-Rabin es inherentemente un algoritmo &amp;ldquo;probabilístico&amp;rdquo;, pero cuando el límite superior de $N$ es fijo, se puede realizar una prueba &amp;ldquo;100% determinista&amp;rdquo; probando un conjunto específico de múltiples bases $a$.
A esto se le llama &lt;strong>Test de Miller-Rabin Determinista (Deterministic Miller-Rabin Test)&lt;/strong>.&lt;/p>
&lt;p>Investigaciones realizadas por Jim Sinclair y otros han demostrado que, para todos los enteros $N &lt; 2^{64}$ (aproximadamente $1.8 \times 10^{19}$), es posible lograr una determinación suficiente y completamente determinista probando los siguientes $7$ primos como la base $a$.&lt;/p>
&lt;p>&lt;strong>Lista de bases $a$ a probar:&lt;/strong>
&lt;code>{2, 325, 9375, 28178, 450775, 9780504, 1795265022}&lt;/code>&lt;/p>
&lt;p>Alternativamente, como otro conjunto bien conocido, usando los siguientes $12$ primos también se puede determinar perfectamente para $N &lt; 2^{64}$.
&lt;code>{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}&lt;/code>&lt;/p>
&lt;p>En este caso, para mejorar la simplicidad y confiabilidad del algoritmo, adoptaremos un enfoque basado en los últimos $12$ primos (o bases optimizadas a $7$). En la implementación en C++, optimizaremos para mantener el número de pruebas al mínimo dividiendo el rango con ramificación condicional.&lt;/p>
&lt;hr>
&lt;h1 id="5-implementación-avanzada-en-c-highly-optimized-c-implementation">5. Implementación avanzada en C++ (Highly Optimized C++ Implementation)
&lt;/h1>&lt;p>Ahora, consolidaremos la teoría matemática y el diseño del algoritmo hasta este punto y presentaremos el código de implementación de una de las funciones de prueba de primalidad de Miller-Rabin más robustas en el C++ moderno.&lt;/p>
&lt;h2 id="puntos-de-la-implementación">Puntos de la implementación
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>&lt;strong>Evitar desbordamientos en la multiplicación de enteros de 64 bits:&lt;/strong>
Cuando $N \approx 10^{18}$, $x \times x$ en la multiplicación modular puede ser hasta $10^{36}$, lo cual desborda fácilmente el valor máximo de $1.8 \times 10^{19}$ de los enteros de 64 bits típicos (&lt;code>uint64_t&lt;/code> o &lt;code>long long&lt;/code>).
Para resolver este problema, utilizamos el tipo extendido &lt;code>__int128_t&lt;/code> (o &lt;code>unsigned __int128&lt;/code>) de GCC o Clang para calcular con precisión de 128 bits antes de aplicar el módulo. Esto permite multiplicaciones modulares rápidas sin recurrir a algoritmos complejos.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Selección de base determinista:&lt;/strong>
Optimizamos para que, si el valor de $N$ es pequeño, solo se prueben unas pocas bases.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="código-fuente-completo-en-c">Código fuente completo en C++
&lt;/h2>&lt;p>A continuación, se muestra la versión completa del código fuente lista para usar en la práctica. Este código se puede copiar y usar tal cual en entornos de programación competitiva.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;span class="lnt">82
&lt;/span>&lt;span class="lnt">83
&lt;/span>&lt;span class="lnt">84
&lt;/span>&lt;span class="lnt">85
&lt;/span>&lt;span class="lnt">86
&lt;/span>&lt;span class="lnt">87
&lt;/span>&lt;span class="lnt">88
&lt;/span>&lt;span class="lnt">89
&lt;/span>&lt;span class="lnt">90
&lt;/span>&lt;span class="lnt">91
&lt;/span>&lt;span class="lnt">92
&lt;/span>&lt;span class="lnt">93
&lt;/span>&lt;span class="lnt">94
&lt;/span>&lt;span class="lnt">95
&lt;/span>&lt;span class="lnt">96
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;vector&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;cstdint&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;initializer_list&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="k">namespace&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// (a * b) mod m rápido usando enteros de 128 bits
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">inline&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="nf">mod_mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span>&lt;span class="p">)((&lt;/span>&lt;span class="kt">unsigned&lt;/span> &lt;span class="n">__int128&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Cálculo de (base^exp) mod m mediante exponenciación binaria
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="nf">mod_pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">exp&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base&lt;/span> &lt;span class="o">%=&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">exp&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">exp&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">exp&lt;/span> &lt;span class="o">&amp;gt;&amp;gt;=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">res&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Prueba determinista de enteros de 64 bits usando el test de primalidad de Miller-Rabin
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">bool&lt;/span> &lt;span class="nf">is_prime_miller_rabin&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Casos base y comprobaciones previas para números primos pequeños
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">3&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">5&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">7&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">3&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">5&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">7&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Descomponer en la forma n-1 = d * 2^s
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">d&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">s&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="n">d&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">d&lt;/span> &lt;span class="o">&amp;gt;&amp;gt;=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Lista de bases a utilizar para la prueba
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// Optimización para minimizar la cantidad de bases probadas según el tamaño de N
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">uint64_t&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">bases&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">4759123141ULL&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bases&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">7&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">61&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">1122004669633ULL&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bases&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">13&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">23&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1662803&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 7 bases que hacen que la prueba sea determinista para todo N &amp;lt; 2^64
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">bases&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">325&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">9375&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">28178&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">450775&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">9780504&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1795265022&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Ejecutar la prueba para cada base
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="nl">a&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">bases&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span> &lt;span class="o">%=&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">continue&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Si a es un múltiplo de n, no se puede determinar, pero no es primo
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">d&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">continue&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Pasa la primera condición, a la siguiente base
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">bool&lt;/span> &lt;span class="n">composite&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Bucle de s-1 iteraciones (x = x^2 mod n)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">r&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">r&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">s&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">r&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">x&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">composite&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Pasa la segunda condición, posible primo
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Si no cumple ninguna condición, es definitivamente un número compuesto
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">composite&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Si pasa las condiciones para todas las bases, es definitivamente primo
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Casos de prueba de muestra
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">uint64_t&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">test_cases&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="mi">1000000007&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// Primo famoso
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="mi">998244353&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// Primo famoso
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="mi">1000000000000000003&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// Primo alrededor de 10^18
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="mi">1000000000000000007&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// Compuesto (10^18 + 7)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="mi">561&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// Número de Carmichael (compuesto)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="mi">18446744073709551557ULL&lt;/span> &lt;span class="c1">// Uno de los primos más grandes cerca de 2^64
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="nl">n&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">test_cases&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34; is &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">is_prime_miller_rabin&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="s">&amp;#34;Prime&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="s">&amp;#34;Composite&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h1 id="6-complejidad-del-algoritmo-y-evaluación-de-rendimiento">6. Complejidad del algoritmo y evaluación de rendimiento
&lt;/h1>&lt;p>Consideraremos el rendimiento del algoritmo implementado.&lt;/p>
&lt;h2 id="complejidad-temporal-time-complexity">Complejidad temporal (Time Complexity)
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>División por tentativa:&lt;/strong> $O(\sqrt{N})$&lt;/li>
&lt;li>&lt;strong>Prueba de Fermat:&lt;/strong> Cálculo de potencia $O(\log N) \times k$ ($k$ es el número de pruebas)&lt;/li>
&lt;li>&lt;strong>Método de Miller-Rabin:&lt;/strong> Cálculo de potencia y bucle $O(\log N) \times k$&lt;/li>
&lt;/ul>
&lt;p>En un entorno de 64 bits ($N \le 2^{64}$), el método determinista de Miller-Rabin anterior verifica un máximo de $7$ bases. Por lo tanto, $k \le 7$ puede considerarse constante, y la complejidad de tiempo total es estrictamente $O(\log N)$.
Incluso en el peor de los casos ($N \approx 10^{19}$), el número de pasos de ejecución se reduce a un máximo de $7 \times 64 = 448$ operaciones básicas, y el tiempo de ejecución es inferior a unos pocos microsegundos ($10^{-6}$ segundos). Comparado con el método de división por tentativa de $O(\sqrt{N})$ (iteraciones del bucle $\approx 4 \times 10^9$), se logra una &lt;strong>aceleración de millones de veces&lt;/strong>.&lt;/p>
&lt;h2 id="mayor-optimización-multiplicación-de-montgomery-montgomery-multiplication">Mayor optimización: Multiplicación de Montgomery (Montgomery Multiplication)
&lt;/h2>&lt;p>En la implementación de este artículo, la división (operación de módulo &lt;code>%&lt;/code>) se realiza utilizando el tipo extendido de enteros de 128 bits &lt;code>__int128_t&lt;/code>. Incluso en las CPU modernas, la división de enteros (instrucción DIV) es una operación costosa que requiere decenas de ciclos en comparación con la suma o la multiplicación.&lt;/p>
&lt;p>Los creadores de bibliotecas y los programadores competitivos que buscan la optimización extrema a veces adoptan una técnica llamada &lt;strong>Multiplicación de Montgomery (Montgomery Multiplication)&lt;/strong>. La multiplicación de Montgomery es un algoritmo asombroso que reemplaza la costosa operación de módulo (división) por &amp;ldquo;solamente desplazamientos de bits y multiplicaciones&amp;rdquo; al mapear números en un &amp;ldquo;espacio de Montgomery&amp;rdquo; especial.
Al integrar esto en la multiplicación modular de la prueba de Miller-Rabin, es posible aumentar aún más la velocidad de ejecución de dos a tres veces. Como este es un tema muy profundo, me gustaría explicarlo con más detalle en otro artículo.&lt;/p>
&lt;hr>
&lt;h1 id="7-resumen">7. Resumen
&lt;/h1>&lt;p>En este artículo, hemos explicado todo sobre las pruebas de primalidad, desde los conceptos básicos hasta el contenido avanzado.
Repasemos los puntos principales.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>El método de división por tentativa&lt;/strong> es seguro, pero debido a su complejidad computacional $O(\sqrt{N})$, carece de viabilidad práctica cuando $N$ supera $10^{12}$.&lt;/li>
&lt;li>&lt;strong>La prueba de Fermat&lt;/strong> es muy rápida con $O(\log N)$, pero tiene una debilidad fatal, ya que puede ser engañada por pseudoprimos absolutos como los números de Carmichael.&lt;/li>
&lt;li>&lt;strong>El test de primalidad de Miller-Rabin&lt;/strong> es el algoritmo práctico más robusto que resuelve las debilidades de la prueba de Fermat.&lt;/li>
&lt;li>En la implementación en C++, utilizar &lt;code>__int128_t&lt;/code> permite manejar de forma segura los desbordamientos de multiplicación de enteros de 64 bits.&lt;/li>
&lt;li>Dentro del rango de enteros de 64 bits ($N &lt; 2^{64}$), al elegir $7$ o $12$ números primos específicos como base, es posible una &lt;strong>prueba de primalidad determinista (100% precisa)&lt;/strong> en lugar de probabilística.&lt;/li>
&lt;/ol>
&lt;p>La prueba de primalidad rápida es una tecnología ineludible en el cálculo con números gigantes. El código fuente de C++ de Miller-Rabin proporcionado en este artículo es lo suficientemente robusto como para ser utilizado en la práctica. Asegúrate de usarlo en tus propios proyectos y en competencias algorítmicas.&lt;/p>
&lt;div class="mermaid">graph LR
TrialDivision["División por tentativa (O(√N))"] --> Fermat["Prueba de Fermat (O(log N), tiene debilidades)"]
Fermat --> MillerRabin["Método Miller-Rabin (O(log N), puede ser determinista)"]
MillerRabin --> Montgomery["+ Multiplicación de Montgomery (aceleración por factor constante)"]
style MillerRabin fill:#f9f,stroke:#333,stroke-width:2px&lt;/div>
&lt;p>El mundo de los algoritmos donde se cruzan la programación y las matemáticas es sumamente hermoso y profundo. Espero que este artículo te haya ayudado en tu aprendizaje continuo.&lt;/p>
&lt;hr>
&lt;p>&lt;em>Reference:&lt;/em>&lt;/p>
&lt;ul>
&lt;li>&lt;em>Pomerance, C., Selfridge, J. L., &amp;amp; Wagstaff, S. S. (1980). The pseudoprimes to 25.10^9. Mathematics of Computation.&lt;/em>&lt;/li>
&lt;li>&lt;em>Sinclair, J. (2011). Deterministic Miller-Rabin primality testing.&lt;/em>&lt;/li>
&lt;/ul></description></item><item><title>¿Qué es el cifrado totalmente homomórfico (FHE)? Explicación de la clave de la seguridad de próxima generación</title><link>http://kenji.blog/es/p/fully-homomorphic-encryption-fhe-explained/</link><pubDate>Fri, 11 Sep 2026 11:00:00 +0900</pubDate><guid>http://kenji.blog/es/p/fully-homomorphic-encryption-fhe-explained/</guid><description>&lt;img src="http://kenji.blog/p/fully-homomorphic-encryption-fhe-explained/img/eyecatch.jpg" alt="Featured image of post ¿Qué es el cifrado totalmente homomórfico (FHE)? Explicación de la clave de la seguridad de próxima generación" />&lt;p>A medida que la computación en la nube y la tecnología de IA se establecen como la base de la sociedad, la compensación entre la &amp;ldquo;privacidad de los datos&amp;rdquo; y la &amp;ldquo;utilización de los datos&amp;rdquo; se ha convertido en uno de los problemas más importantes. Aunque existe una demanda creciente para que la IA en la nube analice datos altamente confidenciales como datos médicos, información financiera e información biométrica personal, muchas empresas dudan en enviar datos externamente debido a preocupaciones de seguridad.&lt;/p>
&lt;p>Las tecnologías de cifrado tradicionales (como AES y RSA) son excelentes para proteger los datos almacenados en disco (Data at Rest) y los datos que fluyen por la red (Data in Transit). Sin embargo, &lt;strong>cuando el servidor realiza un procesamiento (cálculo) en los datos, como una búsqueda o aprendizaje automático (Data in Use), es necesario descifrar el cifrado y devolverlo a texto plano&lt;/strong>. Si el servidor es hackeado en este momento de descifrado, o si un administrador malicioso interno husmea en los datos, esto conduce directamente a una fuga de información.&lt;/p>
&lt;p>La tecnología soñada para superar esta debilidad fundamental del &amp;ldquo;descifrado durante el procesamiento&amp;rdquo; es el &lt;strong>Cifrado Totalmente Homomórfico (Fully Homomorphic Encryption: FHE)&lt;/strong>. Al usar FHE, es posible realizar cálculos en datos mientras permanecen cifrados, sin descifrarlos en absoluto, y devolver solo el texto cifrado resultante al cliente.&lt;/p>
&lt;p>En este artículo, explicaremos en profundidad el FHE, la clave de la seguridad de próxima generación, desde el concepto y la historia del FHE, el avance revolucionario de Craig Gentry, los fundamentos matemáticos (como Ring-LWE), el mayor desafío del &amp;ldquo;ruido&amp;rdquo; y su solución (bootstrapping), hasta las bibliotecas de implementación más recientes.&lt;/p>
&lt;hr>
&lt;h2 id="1-qué-es-el-cifrado-homomórfico-conceptos-básicos">1. ¿Qué es el cifrado homomórfico? Conceptos básicos
&lt;/h2>&lt;p>&amp;ldquo;Homomórfico&amp;rdquo; es un término en álgebra que se refiere a la propiedad de mapear entre conjuntos con una determinada estructura preservando la estructura de las operaciones. En criptografía, la &amp;ldquo;propiedad homomórfica&amp;rdquo; es la propiedad de que &lt;strong>las operaciones en el espacio de texto plano corresponden a operaciones en el espacio de texto cifrado&lt;/strong>.&lt;/p>
&lt;p>Expresado en una fórmula matemática simple, sean $m_1$ y $m_2$ los textos planos, $E(\cdot)$ la función de cifrado y $D(\cdot)$ la función de descifrado. Si la operación (como suma o multiplicación) en el texto plano es $\circ$ y la operación en el texto cifrado es $\diamond$, se cumple la siguiente relación:&lt;/p>
$$ D(E(m_1) \diamond E(m_2)) = m_1 \circ m_2 $$
&lt;p>En otras palabras, el resultado de aplicar alguna operación $\diamond$ a los textos cifrados $E(m_1)$ y $E(m_2)$ y luego descifrarlo coincide con el resultado de operar los textos planos originales con $\circ$.&lt;/p>
&lt;h3 id="flujo-de-datos-en-la-computación-en-la-nube">Flujo de datos en la computación en la nube
&lt;/h3>&lt;p>La arquitectura del procesamiento en la nube usando FHE es completamente diferente a la tradicional. La siguiente figura muestra el flujo de procesamiento de datos seguro utilizando FHE.&lt;/p>
&lt;div class="mermaid">graph TD
A["Cliente (Posee la clave privada)"] -->|1. Cifrar texto plano x: E(x)| B["Servidor en la nube (Solo datos cifrados)"]
B -->|2. Aplicar función f manteniendo el cifrado: E(f(x))| B
B -->|3. Texto cifrado del resultado E(y)| A
A -->|4. Descifrar con clave privada: y = f(x)| A
style A fill:#d4edda,stroke:#28a745
style B fill:#f8d7da,stroke:#dc3545&lt;/div>
&lt;p>El servidor recibe los datos cifrados $E(x)$, pero como no tiene la clave privada, le es absolutamente imposible conocer el contenido de los datos. Sin embargo, al utilizar las propiedades del FHE, es posible aplicar una función $f$ (por ejemplo, un modelo de inferencia de aprendizaje automático) al texto cifrado y generar $E(f(x))$. El cliente recibe esto y lo descifra con su propia clave privada para obtener el resultado deseado $y = f(x)$.&lt;/p>
&lt;hr>
&lt;h2 id="2-historia-de-la-evolución-del-cifrado-homomórfico-phe-she-fhe">2. Historia de la evolución del cifrado homomórfico: PHE, SHE, FHE
&lt;/h2>&lt;p>El cifrado homomórfico no alcanzó su forma &amp;ldquo;completa&amp;rdquo; actual de una sola vez. Se clasifica a grandes rasgos en tres etapas dependiendo de los tipos y el número de operaciones que se pueden realizar.&lt;/p>
&lt;h3 id="cifrado-parcialmente-homomórfico-phe">Cifrado Parcialmente Homomórfico (PHE)
&lt;/h3>&lt;p>El PHE es un esquema de cifrado que permite un número ilimitado de operaciones de suma &lt;strong>o&lt;/strong> multiplicación, pero solo una de las dos. De hecho, los cifrados con esta propiedad han existido durante mucho tiempo.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Cifrado RSA (Propiedad homomórfica para la multiplicación)&lt;/strong>
El cifrado RSA posee de forma no intencionada una propiedad homomórfica para la multiplicación. Dados los textos planos $m_1, m_2$ y la clave pública $(e, N)$:
$$ E(m_1) = m_1^e \pmod N $$
$$ E(m_2) = m_2^e \pmod N $$
Al multiplicarlos juntos:
$$ E(m_1) \times E(m_2) = (m_1 \cdot m_2)^e \pmod N = E(m_1 \times m_2) $$
De esta manera, la multiplicación de textos cifrados corresponde a la multiplicación de textos planos.&lt;/li>
&lt;li>&lt;strong>Cifrado Paillier (Propiedad homomórfica para la suma)&lt;/strong>
El cifrado Paillier, inventado en 1999, posee una propiedad homomórfica para la suma. Se ha puesto en práctica en sistemas como la votación electrónica (donde los votos cifrados se suman y solo se descifra el resultado final).&lt;/li>
&lt;/ul>
&lt;h3 id="cifrado-algo-homomórfico-she">Cifrado Algo Homomórfico (SHE)
&lt;/h3>&lt;p>Es un esquema que puede ejecutar &lt;strong>tanto&lt;/strong> sumas como multiplicaciones, pero tiene un &lt;strong>límite en el número de operaciones (profundidad del circuito)&lt;/strong>. Debido a la acumulación de &amp;ldquo;ruido&amp;rdquo;, que se explicará más adelante, el descifrado se vuelve imposible después de realizar un cierto número de multiplicaciones. El cifrado BGN (Boneh-Goh-Nissim) de 2005 es un ejemplo de esto, pero tenía limitaciones para realizar cálculos complejos prácticos (como el aprendizaje profundo).&lt;/p>
&lt;h3 id="cifrado-totalmente-homomórfico-fhe">Cifrado Totalmente Homomórfico (FHE)
&lt;/h3>&lt;p>Es un esquema de cifrado que permite ejecutar tanto sumas como multiplicaciones un &lt;strong>número ilimitado de veces&lt;/strong>. De manera similar a la integridad de Turing en la teoría de la información, si la suma (equivalente a XOR) y la multiplicación (equivalente a AND) se pueden combinar infinitamente, significa que, en principio, cualquier función o algoritmo computable puede ejecutarse mientras permanece cifrado.&lt;/p>
&lt;p>El FHE fue llamado durante mucho tiempo el &amp;ldquo;Santo Grial de la criptografía&amp;rdquo;, e incluso se decía que podría ser imposible de lograr. Sin embargo, en 2009, &lt;strong>Craig Gentry&lt;/strong>, que entonces era estudiante de doctorado en la Universidad de Stanford, propuso el primer esquema FHE basado en redes ideales (Ideal Lattices), sorprendiendo al mundo.&lt;/p>
&lt;hr>
&lt;h2 id="3-fundamentos-matemáticos-del-fhe-el-problema-lwe-y-ring-lwe">3. Fundamentos matemáticos del FHE: El problema LWE y Ring-LWE
&lt;/h2>&lt;p>Muchos de los esquemas FHE principales actuales se basan en el &lt;strong>problema LWE (Learning With Errors)&lt;/strong>, que es un problema matemático complejo en la &amp;ldquo;Criptografía basada en retículos (Lattice-based Cryptography)&amp;rdquo;, también conocida como Criptografía Post-Cuántica (Post-Quantum Cryptography).&lt;/p>
&lt;h3 id="comprensión-intuitiva-del-problema-lwe">Comprensión intuitiva del problema LWE
&lt;/h3>&lt;p>Resolver un sistema de ecuaciones lineales es fácil si se utiliza, por ejemplo, la eliminación de Gauss.&lt;/p>
$$ \begin{cases} 3s_1 + 4s_2 + 2s_3 \equiv 12 \pmod{17} \\ 1s_1 + 9s_2 + 5s_3 \equiv 8 \pmod{17} \\ \vdots \end{cases} $$
&lt;p>Pero, ¿qué pasaría si agregamos un pequeñísimo &amp;ldquo;error aleatorio (ruido)&amp;rdquo; $e$ a los resultados de estas ecuaciones?&lt;/p>
$$ \begin{cases} 3s_1 + 4s_2 + 2s_3 + e_1 \equiv 13 \pmod{17} \\ 1s_1 + 9s_2 + 5s_3 + e_2 \equiv 7 \pmod{17} \\ \vdots \end{cases} $$
&lt;p>El solo hecho de agregar este error $e$ transforma el problema de encontrar el vector de variables secretas $\vec{s}$ en un problema NP-difícil que es extremadamente complicado de resolver, incluso utilizando supercomputadoras actuales o computadoras cuánticas. Este es el problema LWE.&lt;/p>
&lt;h3 id="el-problema-ring-lwe-rlwe">El problema Ring-LWE (RLWE)
&lt;/h3>&lt;p>El problema LWE estándar involucra operaciones con matrices, lo que tenía el problema de que el tamaño de la clave era muy grande (a veces en gigabytes) y la eficiencia computacional era pobre. Para solucionar esto, se introdujo el &lt;strong>problema Ring-LWE (RLWE)&lt;/strong>, que utiliza operaciones sobre anillos de polinomios.&lt;/p>
&lt;p>En RLWE, los elementos pertenecen a un anillo de polinomios $R_q = \mathbb{Z}_q[x] / (x^N + 1)$ (donde $N$ es una potencia de 2 y $q$ es el módulo primo).
Si tomamos como clave privada un polinomio $s(x)$, y generamos un polinomio aleatorio $a(x)$ y un pequeño polinomio de ruido $e(x)$, la clave pública será el siguiente par:&lt;/p>
$$ (a(x), b(x)) \quad \text{where} \quad b(x) = -a(x) \cdot s(x) + e(x) \pmod q $$
&lt;p>Durante el cifrado, las propiedades de estos polinomios se utilizan para codificar el texto plano $m(x)$ y generar el texto cifrado.&lt;/p>
&lt;hr>
&lt;h2 id="4-el-mayor-obstáculo-el-ruido-y-el-bootstrapping-de-gentry">4. El mayor obstáculo: El &amp;ldquo;ruido&amp;rdquo; y el bootstrapping de Gentry
&lt;/h2>&lt;p>El concepto más importante para comprender el FHE es la &lt;strong>&amp;ldquo;gestión del ruido&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>En los cifrados basados en LWE/RLWE, se incluye intencionalmente un pequeño &amp;ldquo;ruido (error)&amp;rdquo; para garantizar la seguridad.
El proceso de descifrado del texto cifrado $c$ para obtener el texto plano $m$ puede representarse a grandes rasgos mediante la siguiente fórmula:&lt;/p>
$$ D(c) = (c \cdot s) \pmod q = m + \text{noise} $$
&lt;p>Durante el descifrado, este &lt;code>noise&lt;/code> (ruido) se elimina mediante procesos como el redondeo para obtener el texto plano correcto $m$. Sin embargo, cuando se realizan operaciones homomórficas (especialmente multiplicaciones) entre textos cifrados, este ruido se amplifica drásticamente.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Suma homomórfica&lt;/strong>: El ruido aumenta de forma aditiva ($e_1 + e_2$). Este es un aumento relativamente leve.&lt;/li>
&lt;li>&lt;strong>Expresión matemática de la propiedad homomórfica para la suma&lt;/strong>:
$$ E(m_1) \oplus E(m_2) = E(m_1 + m_2) $$&lt;/li>
&lt;li>&lt;strong>Multiplicación homomórfica&lt;/strong>: El ruido explota de forma multiplicativa (ya que incluye términos como $e_1 \times e_2$). Después de unas pocas multiplicaciones, el ruido excede el umbral $q/2$, lo que impide un redondeo correcto y hace que el descifrado falle.&lt;/li>
&lt;li>&lt;strong>Expresión matemática de la propiedad homomórfica para la multiplicación&lt;/strong>:
$$ E(m_1) \otimes E(m_2) = E(m_1 \times m_2) $$&lt;/li>
&lt;/ul>
&lt;p>Esta es la razón por la que el FHE no se pudo realizar durante mucho tiempo y se mantuvo en el nivel de SHE (limitado en operaciones).&lt;/p>
&lt;h3 id="la-magia-del-bootstrapping">La magia del Bootstrapping
&lt;/h3>&lt;p>La contribución genial de Craig Gentry fue la invención de una técnica de reducción de ruido llamada &lt;strong>&amp;ldquo;bootstrapping&amp;rdquo;&lt;/strong>. Esto fue un cambio de paradigma en la criptografía.&lt;/p>
&lt;p>Intuitivamente, es una operación de &amp;ldquo;limpiar el texto cifrado &amp;lsquo;descifrándolo&amp;rsquo; mientras permanece en estado cifrado, antes de que se corrompa por estar lleno de ruido, y colocarlo en un nuevo texto cifrado&amp;rdquo;.&lt;/p>
&lt;ol>
&lt;li>Supongamos que hay un texto cifrado con mucho ruido $C_{noisy}$.&lt;/li>
&lt;li>El cliente proporciona previamente al servidor su clave privada $sk$ &amp;ldquo;cifrada con su clave pública&amp;rdquo; $E_{pk}(sk)$ (a esto se le llama clave de bootstrapping).&lt;/li>
&lt;li>El servidor ejecuta un &lt;strong>circuito de descifrado (Decryption Circuit)&lt;/strong> de forma homomórfica sobre $C_{noisy}$.&lt;/li>
&lt;li>Específicamente, realiza un &amp;ldquo;descifrado en el espacio cifrado&amp;rdquo; sobre $E_{pk}(C_{noisy})$ utilizando $E_{pk}(sk)$.&lt;/li>
&lt;li>Dado que este circuito de descifrado es en sí mismo una operación homomórfica, genera nuevo ruido, pero el ruido del nuevo texto cifrado resultante $C_{fresh}$ se restablece a un &amp;ldquo;nivel fijo&amp;rdquo; constante.&lt;/li>
&lt;/ol>
&lt;div class="mermaid">graph LR
A["Texto cifrado con alto ruido C_noisy"] --> B["Circuito de descifrado homomórfico (Eval_Dec)"]
C["Clave privada cifrada E(sk)"] --> B
B --> D["Texto cifrado con bajo ruido C_fresh"]
style B fill:#ffeeba,stroke:#ffc107&lt;/div>
&lt;p>Al ejecutar regularmente este bootstrapping durante el cálculo, en teoría se hizo posible calcular circuitos de profundidad infinita (logrando el FHE). Sin embargo, en el esquema original de Gentry, este proceso de bootstrapping tenía un costo computacional desesperadamente alto, tardando de decenas de minutos a horas por cada ejecución.&lt;/p>
&lt;hr>
&lt;h2 id="5-generaciones-de-fhe-y-evolución-de-los-esquemas-principales">5. Generaciones de FHE y evolución de los esquemas principales
&lt;/h2>&lt;p>En busca de la aplicación práctica del FHE, criptógrafos de todo el mundo han estado compitiendo para mejorar los algoritmos. Actualmente, el FHE se clasifica principalmente en cuatro generaciones o familias.&lt;/p>
&lt;h3 id="2ª-generación-operaciones-exactas-con-enteros-bgv-bfv">2ª Generación: Operaciones exactas con enteros (BGV, BFV)
&lt;/h3>&lt;p>Estos son los esquemas &lt;strong>BGV (Brakerski-Gentry-Vaikuntanathan)&lt;/strong> y &lt;strong>BFV (Brakerski/Fan-Vercauteren)&lt;/strong> que aparecieron entre 2011 y 2012. Se basan en RLWE y son adecuados para la aritmética modular de enteros (cálculos exactos).
Soportan técnicas de procesamiento por lotes (Batching) similares a SIMD (Single Instruction, Multiple Data), lo que permite empaquetar miles de ranuras de datos dentro de un solo texto cifrado polinomial gigante y realizar cálculos en paralelo a la vez.&lt;/p>
&lt;h3 id="3ª-generación-aceleración-del-bootstrapping-gsw-fhew-tfhe">3ª Generación: Aceleración del bootstrapping (GSW, FHEW, TFHE)
&lt;/h3>&lt;p>El esquema &lt;strong>GSW (Gentry-Sahai-Waters)&lt;/strong> de 2013 simplificó aún más la estructura del FHE. Un desarrollo basado en esto es el &lt;strong>TFHE (Fast Fully Homomorphic Encryption over the Torus)&lt;/strong>, que es una de las opciones principales en la actualidad.
La característica del TFHE es que su bootstrapping es extremadamente rápido (en el orden de milisegundos). Es fuerte en operaciones a nivel de puertas (circuitos lógicos como AND, XOR) y tiene un tamaño de texto cifrado relativamente pequeño, por lo que es adecuado para evaluar rápidamente circuitos lógicos arbitrarios.&lt;/p>
&lt;h3 id="4ª-generación-especialización-en-cálculos-aproximados-y-aprendizaje-automático-ckks">4ª Generación: Especialización en cálculos aproximados y aprendizaje automático (CKKS)
&lt;/h3>&lt;p>El esquema &lt;strong>CKKS (Cheon-Kim-Kim-Song)&lt;/strong>, propuesto por Cheon et al. en 2017, es la tecnología definitiva para la protección de la privacidad en la IA y el aprendizaje automático actuales.
Mientras que las versiones anteriores de FHE se centraban en &amp;ldquo;cálculos de enteros exactos&amp;rdquo;, el CKKS admite &lt;strong>&amp;ldquo;cálculos aproximados de números de coma flotante&amp;rdquo;&lt;/strong> mientras los datos permanecen cifrados. Muestra un rendimiento abrumador en cálculos de números reales donde se permiten pequeños errores, como el entrenamiento y la inferencia de redes neuronales.&lt;/p>
&lt;p>La siguiente tabla resume cómo elegir un esquema según su propósito.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align:left">Nombre del Esquema&lt;/th>
&lt;th style="text-align:left">Tipo de datos preferido&lt;/th>
&lt;th style="text-align:left">Casos de uso recomendados&lt;/th>
&lt;th style="text-align:left">Características&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>BFV / BGV&lt;/strong>&lt;/td>
&lt;td style="text-align:left">Enteros (Integer)&lt;/td>
&lt;td style="text-align:left">Cálculos estadísticos exactos, agregación de datos financieros, búsquedas en bases de datos&lt;/td>
&lt;td style="text-align:left">Alto rendimiento a través del procesamiento por lotes SIMD&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>CKKS&lt;/strong>&lt;/td>
&lt;td style="text-align:left">Números reales (Real/Complex)&lt;/td>
&lt;td style="text-align:left">Aprendizaje automático (DNN, regresión logística), procesamiento de señales&lt;/td>
&lt;td style="text-align:left">Aceleración mediante cálculos aproximados, reescalado&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>TFHE&lt;/strong>&lt;/td>
&lt;td style="text-align:left">Valores booleanos (Boolean)&lt;/td>
&lt;td style="text-align:left">Circuitos lógicos arbitrarios, búsqueda de cadenas, evaluación de funciones no lineales&lt;/td>
&lt;td style="text-align:left">Bootstrapping ultrarrápido (en milisegundos)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="6-práctica-bibliotecas-fhe-y-código-conceptual">6. Práctica: Bibliotecas FHE y código conceptual
&lt;/h2>&lt;p>Actualmente, existen muchas bibliotecas de código abierto disponibles que permiten usar el FHE sin requerir un conocimiento profundo en criptografía.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Microsoft SEAL (Simple Encrypted Arithmetic Library)&lt;/strong>: Biblioteca en C++ que admite BFV, BGV y CKKS. Es uno de los estándares de la industria. Su enlace en Python, &lt;strong>TenSEAL&lt;/strong>, es popular entre los ingenieros de IA.&lt;/li>
&lt;li>&lt;strong>Zama (Concrete)&lt;/strong>: Un framework basado en TFHE. Puede escribirse en Rust/Python y ofrece la capacidad de compilar modelos PyTorch existentes para ejecutarlos sobre FHE (Concrete ML).&lt;/li>
&lt;li>&lt;strong>OpenFHE&lt;/strong>: Sucesor de PALISADE, es una biblioteca integral en C++ que soporta todos los esquemas principales.&lt;/li>
&lt;/ul>
&lt;h3 id="ejemplo-de-programación-fhe-usando-python-tenseal">Ejemplo de programación FHE usando Python (TenSEAL)
&lt;/h3>&lt;p>Aquí presentamos un ejemplo de código conceptual en Python usando el esquema CKKS para sumar y multiplicar vectores de números reales mientras se mantienen cifrados.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">tenseal&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">ts&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. Configuración del contexto (incluye generación de claves)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Usando el esquema CKKS, establecer el grado del polinomio en 8192&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">context&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ts&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ts&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">SCHEME_TYPE&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">CKKS&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">poly_modulus_degree&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">8192&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">coeff_mod_bit_sizes&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">60&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">40&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">40&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">60&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">context&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">generate_galois_keys&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">context&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">global_scale&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="o">**&lt;/span>&lt;span class="mi">40&lt;/span> &lt;span class="c1"># Factor de escala para números reales&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. Lado del cliente: Cifrado de datos&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">vector1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mf">1.5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">2.5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">3.5&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">vector2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mf">2.0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">3.0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">4.0&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Convertir vectores de texto plano a texto cifrado (normalmente ejecutado en el lado del cliente)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">enc_v1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ts&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">ckks_vector&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vector1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">enc_v2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ts&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">ckks_vector&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vector2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. Lado del servidor: Operaciones con datos cifrados (Protección de Data in Use)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># El servidor no conoce el texto plano, pero puede realizar sumas y multiplicaciones&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">enc_add&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">enc_v1&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">enc_v2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">enc_mul&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">enc_v1&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">enc_v2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 4. Lado del cliente: Descifrado de los resultados&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Solo el cliente que posee la clave privada puede ver los resultados&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">res_add&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">enc_add&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">decrypt&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">res_mul&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">enc_mul&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">decrypt&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Resultado de la suma descifrado: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">res_add&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Ejemplo de salida: [3.5000001, 5.5000001, 7.5000002] (Contiene un ligero error debido al cálculo aproximado)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Resultado de la multiplicación descifrado: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">res_mul&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Ejemplo de salida: [3.0000002, 7.5000005, 14.0000003]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Como se puede ver en el código anterior, los operadores normales de Python como &lt;code>enc_v1 + enc_v2&lt;/code> están sobrecargados, lo que permite escribir cálculos entre textos cifrados de forma muy intuitiva. En el lado del servidor, las operaciones con vectores se completan sin conocer su contenido.&lt;/p>
&lt;hr>
&lt;h2 id="7-desafíos-del-fhe-rendimiento-y-aceleración-de-hardware">7. Desafíos del FHE: Rendimiento y aceleración de hardware
&lt;/h2>&lt;p>Aunque el FHE proporciona una seguridad teóricamente perfecta, el mayor desafío en su implementación práctica es la &lt;strong>&amp;ldquo;sobrecarga de rendimiento (overhead)&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Sobrecarga computacional&lt;/strong>: Comparado con los cálculos en texto plano, los cálculos en texto cifrado son de miles a decenas de miles de veces más lentos en una CPU. La multiplicación de polinomios y el bootstrapping requieren cálculos masivos de FFT (Transformada Rápida de Fourier) o NTT (Transformada Teórica de Números).&lt;/li>
&lt;li>&lt;strong>Expansión del tamaño de los datos (Ciphertext Expansion)&lt;/strong>: Unos pocos bytes de texto plano pueden convertirse en megabytes una vez cifrados. Esto ejerce una fuerte presión sobre el ancho de banda de la memoria y la red.&lt;/li>
&lt;/ol>
&lt;h3 id="enfoques-para-soluciones-a-través-del-hardware">Enfoques para soluciones a través del hardware
&lt;/h3>&lt;p>Para superar esta sobrecarga, se están desarrollando en todo el mundo aceleradores de hardware dedicados al FHE (con soporte para ASIC, FPGA y GPU).&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Aceleración por GPU&lt;/strong>: Se están realizando esfuerzos para paralelizar las operaciones NTT y el bootstrapping utilizando potentes GPUs como las de NVIDIA, habiéndose reportado velocidades decenas de veces mayores en comparación con las implementaciones por software (por ejemplo, 100x.ai, el backend CUDA de TFHE-rs de Zama).&lt;/li>
&lt;li>&lt;strong>Proyecto DARPA DPRIVE&lt;/strong>: La Agencia de Proyectos de Investigación Avanzados de Defensa de EE. UU. (DARPA) está promoviendo el proyecto de desarrollo de hardware dedicado &amp;ldquo;DPRIVE (Data Protection in Virtual Environments)&amp;rdquo; para llevar la velocidad de cálculo del FHE a un nivel equivalente al de procesamiento de texto plano (dentro de un margen de sobrecarga de 10 veces), con la participación de Intel, Microsoft, Intellectual Ventures, entre otros.&lt;/li>
&lt;li>&lt;strong>La llegada de la FPU (FHE Processing Unit)&lt;/strong>: Startups como Cornami y Optalysys se están embarcando en el desarrollo de chips dedicados al FHE utilizando computación óptica o arquitecturas de silicio especiales.&lt;/li>
&lt;/ul>
&lt;p>En un futuro cercano, puede que llegue una era en la que las &amp;ldquo;FPU&amp;rdquo; vengan integradas de serie en servidores e infraestructuras en la nube, al igual que ocurre con las NPU (Neural Processing Unit) en el campo de la IA.&lt;/p>
&lt;hr>
&lt;h2 id="8-casos-de-uso-esperados">8. Casos de uso esperados
&lt;/h2>&lt;p>A medida que el FHE se acerca a velocidades prácticas, se esperan innovaciones disruptivas en los siguientes campos:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Protección de la privacidad en análisis médicos y genómicos&lt;/strong>:
Al permitir que la IA en la nube aprenda de los historiales médicos o los datos de ADN de los pacientes en poder de múltiples hospitales mientras se mantienen cifrados con FHE, es posible desarrollar modelos de diagnóstico de cáncer de alta precisión o nuevos fármacos sin violar las leyes de privacidad (como HIPAA o GDPR).&lt;/li>
&lt;li>&lt;strong>Detección de fraudes y prevención de lavado de dinero (AML) en instituciones financieras&lt;/strong>:
Bancos de la competencia pueden cotejar datos entre sí de forma cifrada, sin revelar la información de las cuentas de los clientes o el historial de transacciones, lo que permite realizar análisis interbancarios para detectar enormes redes de transferencias ilícitas.&lt;/li>
&lt;li>&lt;strong>API de inferencia de IA segura (MaaS: Model as a Service)&lt;/strong>:
Los usuarios envían su propia voz, imágenes faciales o indicaciones (prompts) de forma cifrada a servicios de IA (como un LLM tipo ChatGPT). El proveedor de IA genera la respuesta sin conocer en absoluto los datos introducidos por el usuario y la devuelve como texto cifrado. Esto elimina por completo la preocupación de que &amp;ldquo;la IA aprenda o espíe la información personal&amp;rdquo;.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="9-conclusión-el-futuro-de-la-criptografía-avanza-hacia-el-cálculo-invisible">9. Conclusión: El futuro de la criptografía avanza hacia el &amp;ldquo;cálculo invisible&amp;rdquo;
&lt;/h2>&lt;p>Al igual que la invención de la criptografía de clave pública (RSA) en la década de 1970 hizo posible la comunicación segura en Internet (como HTTPS), la invención del FHE por parte de Craig Gentry es uno de los hitos más importantes en la historia de la criptografía.&lt;/p>
&lt;p>Hoy en día, el cifrado totalmente homomórfico (FHE) ha saltado de la teoría de los laboratorios y ha entrado en una fase en la que Microsoft, IBM, Intel, Google y muchas startups compiten ferozmente por su aplicación práctica. Aunque los desafíos del coste computacional y el tamaño de los datos aún persisten, el rendimiento continúa mejorando a un ritmo que supera la Ley de Moore, gracias al refinamiento de los algoritmos y la evolución de los aceleradores de hardware.&lt;/p>
&lt;p>En unos pocos años, &amp;ldquo;calcular datos mientras están cifrados&amp;rdquo; ya no será algo extraordinario, sino que se convertirá en una de las mejores prácticas estándar de protección de datos en los servicios en la nube. El FHE es verdaderamente la clave de la seguridad de la próxima generación, al materializar &lt;strong>el equilibrio definitivo entre la privacidad y la utilización de los datos&lt;/strong> en nuestra sociedad impulsada por los datos.&lt;/p></description></item><item><title>Fundamentos matemáticos de la criptografía de curva elíptica (ECC) e implementación en C++</title><link>http://kenji.blog/es/p/elliptic-curve-cryptography-math-cpp/</link><pubDate>Fri, 11 Sep 2026 10:00:00 +0900</pubDate><guid>http://kenji.blog/es/p/elliptic-curve-cryptography-math-cpp/</guid><description>&lt;img src="http://kenji.blog/p/elliptic-curve-cryptography-math-cpp/img/eyecatch.jpg" alt="Featured image of post Fundamentos matemáticos de la criptografía de curva elíptica (ECC) e implementación en C++" />&lt;h1 id="fundamentos-matemáticos-de-la-criptografía-de-curva-elíptica-ecc-e-implementación-en-c">Fundamentos matemáticos de la criptografía de curva elíptica (ECC) e implementación en C++
&lt;/h1>&lt;p>En la tecnología criptográfica moderna, la &lt;strong>criptografía de curva elíptica (Elliptic Curve Cryptography: ECC)&lt;/strong> juega un papel extremadamente importante. Desde nuestras comunicaciones diarias en Internet (HTTPS/TLS), los enclaves seguros de los teléfonos inteligentes, la autenticación de servidores mediante SSH, la autenticación sin contraseña como FIDO, e incluso en criptomonedas como Bitcoin y Ethereum, no es una exageración decir que la base de confianza de la sociedad digital moderna está respaldada por ECC.&lt;/p>
&lt;p>En este artículo, explicaremos exhaustivamente con un volumen abrumador cómo funciona esta criptografía de curva elíptica, partiendo de la hermosa pero compleja teoría matemática detrás de ella (geometría algebraica sobre campos finitos), los métodos de implementación prácticos usando C++, y además, técnicas de codificación seguras para prevenir ataques de canal lateral (ataques de tiempo).&lt;/p>
&lt;hr>
&lt;h2 id="1-por-qué-la-criptografía-de-curva-elíptica-comparación-con-rsa">1. ¿Por qué la criptografía de curva elíptica? (Comparación con RSA)
&lt;/h2>&lt;p>Cuando se hablaba de criptografía de clave pública, durante mucho tiempo el sinónimo era &lt;strong>RSA&lt;/strong>. RSA basa su seguridad en la &amp;ldquo;dificultad de factorizar números compuestos enormes&amp;rdquo;. Sin embargo, con el aumento de la capacidad computacional de los ordenadores, para mantener la seguridad ha surgido la necesidad de alargar continuamente el tamaño de la clave RSA (el número de bits del módulo). Hoy en día, se recomienda una longitud de clave de al menos 2048 bits, o 3072 y 4096 bits para mayor seguridad.&lt;/p>
&lt;p>Por otro lado, la criptografía de curva elíptica (ECC) fundamenta su seguridad en una dificultad matemática diferente: el &lt;strong>&amp;ldquo;problema del logaritmo discreto en curvas elípticas (ECDLP)&amp;rdquo;&lt;/strong>. Hasta la fecha, no se ha descubierto ningún algoritmo eficiente (como algoritmos de tiempo subexponencial) para resolver el ECDLP, e incluso los métodos de ataque más eficientes conocidos requieren un tiempo exponencial.&lt;/p>
&lt;p>Debido a esta propiedad, ECC tiene la ventaja decisiva de &lt;strong>poder lograr el mismo nivel de seguridad que RSA con una longitud de clave mucho más corta&lt;/strong>.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align:center">Nivel de seguridad (bits)&lt;/th>
&lt;th style="text-align:center">Longitud de clave RSA (bits)&lt;/th>
&lt;th style="text-align:center">Longitud de clave ECC (bits)&lt;/th>
&lt;th style="text-align:center">Proporción de longitud de clave&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align:center">80&lt;/td>
&lt;td style="text-align:center">1024&lt;/td>
&lt;td style="text-align:center">160&lt;/td>
&lt;td style="text-align:center">1:6&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:center">112&lt;/td>
&lt;td style="text-align:center">2048&lt;/td>
&lt;td style="text-align:center">224&lt;/td>
&lt;td style="text-align:center">1:9&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:center">128&lt;/td>
&lt;td style="text-align:center">3072&lt;/td>
&lt;td style="text-align:center">256&lt;/td>
&lt;td style="text-align:center">1:12&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:center">192&lt;/td>
&lt;td style="text-align:center">7680&lt;/td>
&lt;td style="text-align:center">384&lt;/td>
&lt;td style="text-align:center">1:20&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:center">256&lt;/td>
&lt;td style="text-align:center">15360&lt;/td>
&lt;td style="text-align:center">512&lt;/td>
&lt;td style="text-align:center">1:30&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Como muestra la tabla anterior, para obtener una seguridad de 128 bits (el estándar actual), RSA requiere una clave de 3072 bits, pero con ECC solo se necesitan 256 bits. Esto permite una reducción en la cantidad de cálculos, un menor uso de memoria y un ahorro de ancho de banda en la red, presumiendo de una ventaja abrumadora, especialmente en entornos con recursos limitados como dispositivos IoT y tarjetas inteligentes.&lt;/p>
&lt;hr>
&lt;h2 id="2-preparación-matemática-teoría-de-grupos-y-el-mundo-de-los-campos-finitos">2. Preparación matemática: Teoría de grupos y el mundo de los campos finitos
&lt;/h2>&lt;p>Para comprender verdaderamente la criptografía de curva elíptica, es necesario dominar los conceptos básicos del álgebra abstracta (teoría de grupos y teoría de campos). Aquí resumiremos de manera concisa los conocimientos previos para construir ECC.&lt;/p>
&lt;h3 id="21-grupo-group-y-grupo-abeliano">2.1. Grupo (Group) y grupo abeliano
&lt;/h3>&lt;p>Un &lt;strong>grupo (Group)&lt;/strong> es un conjunto $G$ junto con una operación binaria (aquí la asumiremos como adición $+$) en ese conjunto, denotado como $(G, +)$, que satisface los siguientes 4 axiomas:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Clausura (Closure)&lt;/strong>: Para cualesquiera $a, b \in G$, se cumple que $a + b \in G$.&lt;/li>
&lt;li>&lt;strong>Asociatividad (Associativity)&lt;/strong>: Para cualesquiera $a, b, c \in G$, se cumple que $(a + b) + c = a + (b + c)$.&lt;/li>
&lt;li>&lt;strong>Existencia del elemento neutro (Identity element)&lt;/strong>: Existe un elemento $e \in G$ tal que para cualquier $a \in G$, $a + e = e + a = a$. En el caso de un grupo aditivo, este elemento neutro generalmente se denota como $0$ o $\mathcal{O}$.&lt;/li>
&lt;li>&lt;strong>Existencia del elemento inverso (Inverse element)&lt;/strong>: Para cualquier $a \in G$, existe un elemento $b \in G$ tal que $a + b = b + a = e$. Este $b$ se denota como $-a$.&lt;/li>
&lt;/ol>
&lt;p>Además, si el resultado no cambia al invertir el orden de la operación, es decir, si satisface la siguiente condición, el grupo se denomina &lt;strong>grupo abeliano (grupo conmutativo)&lt;/strong>.&lt;/p>
&lt;ol start="5">
&lt;li>&lt;strong>Conmutatividad (Commutativity)&lt;/strong>: Para cualesquiera $a, b \in G$, se cumple que $a + b = b + a$.&lt;/li>
&lt;/ol>
&lt;p>El conjunto de puntos en una curva elíptica, al definir una regla de adición específica, forma este &lt;strong>grupo abeliano&lt;/strong>.&lt;/p>
&lt;h3 id="22-campo-finito-finite-field">2.2. Campo finito (Finite Field)
&lt;/h3>&lt;p>En la teoría criptográfica, no se utilizan campos con un número infinito de elementos continuos como los números reales o complejos, sino un &lt;strong>campo finito (Finite Field)&lt;/strong> o campo de Galois (Galois Field), donde el número de elementos es finito.&lt;/p>
&lt;p>El campo finito más básico es el &lt;strong>campo primo $\mathbb{F}_p$&lt;/strong> utilizando un número primo $p$. Esto es el conjunto de enteros $\{0, 1, 2, \dots, p-1\}$ en el que se definen las cuatro operaciones aritméticas (suma, resta, multiplicación, división) módulo $p$ (el resto de dividir por $p$).&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Suma&lt;/strong>: $(a + b) \pmod p$&lt;/li>
&lt;li>&lt;strong>Resta&lt;/strong>: $(a - b) \pmod p$&lt;/li>
&lt;li>&lt;strong>Multiplicación&lt;/strong>: $(a \times b) \pmod p$&lt;/li>
&lt;li>&lt;strong>División&lt;/strong>: $a \times b^{-1} \pmod p$ (donde $b^{-1}$ es el inverso multiplicativo de $b$ módulo $p$)&lt;/li>
&lt;/ul>
&lt;p>El cálculo del &lt;strong>inverso multiplicativo modular (Modular Multiplicative Inverse)&lt;/strong> es extremadamente importante en las implementaciones criptográficas. Para encontrar $b^{-1}$ que satisfaga $b \times b^{-1} \equiv 1 \pmod p$, se utilizan principalmente los dos siguientes algoritmos:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Algoritmo de Euclides extendido (Extended Euclidean Algorithm)&lt;/strong>: Es rápido, pero dependiendo de la implementación, el tiempo de procesamiento puede depender de los valores de entrada, por lo que existe el riesgo de ataques de tiempo.&lt;/li>
&lt;li>&lt;strong>Pequeño teorema de Fermat (Fermat&amp;rsquo;s Little Theorem)&lt;/strong>: Cuando $p$ es primo y $b \neq 0$, se cumple que $b^{p-1} \equiv 1 \pmod p$. Dividiendo ambos lados por $b$, obtenemos $b^{p-2} \equiv b^{-1} \pmod p$. En otras palabras, calculando la potencia $p-2$ de $b$, se obtiene el inverso. Dado que las operaciones de exponenciación son fáciles de implementar en tiempo constante, esta es la preferida en implementaciones criptográficas.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="3-ecuación-de-la-curva-elíptica-y-geometría">3. Ecuación de la curva elíptica y geometría
&lt;/h2>&lt;h3 id="31-forma-normal-de-weierstrass">3.1. Forma normal de Weierstrass
&lt;/h3>&lt;p>Una &lt;strong>curva elíptica (Elliptic Curve)&lt;/strong> es una curva plana generalmente definida por la siguiente ecuación, llamada &lt;strong>forma normal de Weierstrass (Weierstrass normal form)&lt;/strong>.&lt;/p>
$$ y^2 = x^3 + ax + b $$
&lt;p>Aquí, $a$ y $b$ son constantes, y como condición para que la curva no tenga puntos singulares (auto-intersecciones o cúspides), es decir, para que sea una curva suave, se requiere que el siguiente &lt;strong>discriminante (Discriminant) $\Delta$&lt;/strong> no sea cero.&lt;/p>
$$ \Delta = -16(4a^3 + 27b^2) \neq 0 $$
&lt;p>Dado que una curva con puntos singulares compromete la seguridad criptográfica, siempre se eligen coeficientes $a, b$ que satisfagan esta condición.&lt;/p>
&lt;h3 id="32-punto-en-el-infinito-point-at-infinity">3.2. Punto en el infinito (Point at Infinity)
&lt;/h3>&lt;p>Para convertir matemáticamente la curva elíptica en un grupo completo, además de los puntos en el plano, introducimos un punto virtual llamado &lt;strong>&amp;ldquo;punto en el infinito (Point at Infinity)&amp;rdquo;&lt;/strong>. Lo denotamos como $\mathcal{O}$.&lt;/p>
&lt;p>El punto en el infinito $\mathcal{O}$ se define como el punto donde todas las líneas verticales se cruzan en el infinito infinito. En la teoría de grupos, este punto en el infinito $\mathcal{O}$ funciona como el &lt;strong>elemento neutro en la adición&lt;/strong> (cero).&lt;/p>
&lt;p>Es decir, para cualquier punto $P$ en la curva, se cumple lo siguiente:
&lt;/p>
$$ P + \mathcal{O} = \mathcal{O} + P = P $$
&lt;p>Además, el inverso $-P$ del punto $P = (x, y)$ se define como el punto simétrico con respecto al eje x, $(x, -y)$. Por lo tanto:
&lt;/p>
$$ P + (-P) = \mathcal{O} $$
&lt;hr>
&lt;h2 id="4-operaciones-de-grupo-en-la-curva-elíptica-suma-de-puntos-y-duplicación">4. Operaciones de grupo en la curva elíptica (Suma de puntos y duplicación)
&lt;/h2>&lt;p>El núcleo de la criptografía de curva elíptica es la operación de &lt;strong>&amp;ldquo;suma (Addition)&amp;rdquo;&lt;/strong> entre puntos de la curva. Esta difiere de la suma habitual de números enteros y se define basándose en operaciones geométricas.&lt;/p>
&lt;h3 id="41-suma-geométrica-tangent-and-chord-method">4.1. Suma geométrica (Tangent and Chord Method)
&lt;/h3>&lt;p>El procedimiento para sumar dos puntos diferentes $P$ y $Q$ en la curva para obtener un nuevo punto $R$ ($R = P + Q$) es el siguiente:&lt;/p>
&lt;ol>
&lt;li>Se traza una línea recta (cuerda) que pasa por los puntos $P$ y $Q$.&lt;/li>
&lt;li>Esta línea recta siempre interceptará la curva elíptica en un tercer punto (a este lo llamaremos $-R$). (*Según un teorema de la geometría algebraica)&lt;/li>
&lt;li>El punto obtenido al reflejar el punto de intersección $-R$ simétricamente sobre el eje x (invirtiendo el signo de la coordenada y) será el punto $R$ buscado.&lt;/li>
&lt;/ol>
&lt;div class="mermaid">graph TD
Step1["Trazar una línea que conecte P(x1, y1) y Q(x2, y2)"] --> Step2["Encontrar el tercer punto de intersección -R con la curva"]
Step2 --> Step3["Reflejar -R sobre el eje x para obtener R(x3, y3)"]
Step3 -.-> Result["Esto es R = P + Q"]&lt;/div>
&lt;h3 id="42-duplicación-de-puntos-point-doubling">4.2. Duplicación de puntos (Point Doubling)
&lt;/h3>&lt;p>Al sumar el mismo punto $P$ a sí mismo ($P + P = 2P$), no se puede trazar una línea que pase por dos puntos. En este caso, se traza la &lt;strong>línea tangente (Tangent) a la curva en el punto $P$&lt;/strong>.&lt;/p>
&lt;ol>
&lt;li>Se traza la línea tangente a la curva en el punto $P$.&lt;/li>
&lt;li>Esta línea tangente interceptará la curva en un segundo punto $-R$.&lt;/li>
&lt;li>El punto reflejado simétricamente sobre el eje x será el punto buscado $R = 2P$.&lt;/li>
&lt;/ol>
&lt;h3 id="43-fórmulas-de-cálculo-algebraico">4.3. Fórmulas de cálculo algebraico
&lt;/h3>&lt;p>Las operaciones geométricas se traducen a fórmulas algebraicas para que puedan ser calculadas por un ordenador.
Todas las operaciones se realizan &lt;strong>sobre el campo finito $\mathbb{F}_p$ (módulo $p$)&lt;/strong>.&lt;/p>
&lt;p>Sean el punto $P = (x_1, y_1)$ y el punto $Q = (x_2, y_2)$.
Además, el punto resultante del cálculo será $R = P + Q = (x_3, y_3)$.&lt;/p>
&lt;p>Sea $\lambda$ (lambda) la pendiente de la recta.&lt;/p>
&lt;p>&lt;strong>[Caso 1: Cuando $P \neq Q$ (Suma de puntos)]&lt;/strong>
La pendiente $\lambda$ es la tasa de cambio entre los dos puntos.
&lt;/p>
$$ \lambda \equiv \frac{y_2 - y_1}{x_2 - x_1} \pmod p $$
$$ \lambda \equiv (y_2 - y_1) \cdot (x_2 - x_1)^{-1} \pmod p $$
&lt;p>Usando este $\lambda$, $x_3, y_3$ se calculan de la siguiente manera:
&lt;/p>
$$ x_3 \equiv \lambda^2 - x_1 - x_2 \pmod p $$
$$ y_3 \equiv \lambda(x_1 - x_3) - y_1 \pmod p $$
&lt;p>&lt;strong>[Caso 2: Cuando $P = Q$ (Duplicación de puntos)]&lt;/strong>
La pendiente $\lambda$ es la pendiente de la recta tangente obtenida por diferenciación. (Se deriva implícitamente $y^2 = x^3 + ax + b$)
&lt;/p>
$$ 2y \cdot y' = 3x^2 + a \implies y' = \frac{3x^2 + a}{2y} $$
&lt;p>
Por lo tanto,
&lt;/p>
$$ \lambda \equiv (3x_1^2 + a) \cdot (2y_1)^{-1} \pmod p $$
&lt;p>Las fórmulas para $x_3, y_3$ tienen la misma forma que en la suma, pero como $x_2 = x_1$, se convierten en:
&lt;/p>
$$ x_3 \equiv \lambda^2 - 2x_1 \pmod p $$
$$ y_3 \equiv \lambda(x_1 - x_3) - y_1 \pmod p $$
&lt;blockquote>
&lt;p>[!IMPORTANT]
Estas fórmulas incluyen &lt;strong>divisiones (cálculo del inverso modular)&lt;/strong>, como $(x_2 - x_1)^{-1}$ y $(2y_1)^{-1}$. Dado que el cálculo del inverso modular tiene un costo computacional muy alto, en las implementaciones reales generalmente se utilizan sistemas de coordenadas proyectivas que retrasan la división, como las &lt;strong>&amp;ldquo;Coordenadas Jacobianas (Jacobian Coordinates)&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h2 id="5-multiplicación-escalar-y-el-problema-del-logaritmo-discreto-en-curvas-elípticas-ecdlp">5. Multiplicación escalar y el problema del logaritmo discreto en curvas elípticas (ECDLP)
&lt;/h2>&lt;p>En la criptografía de curva elíptica, la operación con mayor costo computacional y que forma el núcleo de la seguridad es la &lt;strong>multiplicación escalar (Scalar Multiplication)&lt;/strong>.&lt;/p>
&lt;h3 id="51-qué-es-la-multiplicación-escalar">5.1. ¿Qué es la multiplicación escalar?
&lt;/h3>&lt;p>La operación de sumar un punto $P$ un número $k$ de veces se llama multiplicación escalar y se denota como $kP$.
&lt;/p>
$$ kP = \underbrace{P + P + \dots + P}_{k\text{ veces}} $$
&lt;p>Aquí, $k$ es un número entero muy grande (por ejemplo, un entero de 256 bits).&lt;/p>
&lt;h3 id="52-problema-del-logaritmo-discreto-en-curvas-elípticas-ecdlp">5.2. Problema del logaritmo discreto en curvas elípticas (ECDLP)
&lt;/h3>&lt;p>La seguridad de la criptografía de curva elíptica depende de la dificultad del siguiente problema.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Problema del logaritmo discreto en curvas elípticas (Elliptic Curve Discrete Logarithm Problem: ECDLP)&lt;/strong>
Dado un punto conocido $P$ (punto base) y el punto resultante $Q$, encontrar el escalar $k$ que satisfaga $Q = kP$.&lt;/p>
&lt;/blockquote>
&lt;p>Calcular $Q$ a partir de $k$ y $P$ (dirección directa) es fácil (tiempo polinomial) utilizando el algoritmo descrito a continuación, pero calcular $k$ inversamente a partir de $P$ y $Q$ (dirección inversa) no tiene una solución eficiente aparte de una búsqueda exhaustiva (fuerza bruta), lo que lo hace virtualmente imposible (función unidireccional).
En los protocolos criptográficos, &lt;strong>$k$ corresponde a la &amp;ldquo;clave privada&amp;rdquo; y $Q$ a la &amp;ldquo;clave pública&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;h3 id="53-algoritmo-double-and-add-doble-y-suma">5.3. Algoritmo Double-and-Add (Doble y Suma)
&lt;/h3>&lt;p>Cuando $k$ es un número enorme (por ejemplo: $2^{256}$), sumar ingenuamente $P$ un total de $k$ veces no terminaría ni siquiera si se agotara la vida del universo. Por lo tanto, para realizar la multiplicación escalar a alta velocidad, se utiliza el &lt;strong>método Double-and-Add (método binario)&lt;/strong>.&lt;/p>
&lt;p>Esta es la versión de curva elíptica del método de &amp;ldquo;cuadratura repetida&amp;rdquo; para calcular potencias de enteros de manera rápida. Expresa el escalar $k$ en formato binario y lo procesa secuencialmente desde el bit más significativo.&lt;/p>
&lt;ol>
&lt;li>Inicializar el punto $R$ que mantiene el resultado con $\mathcal{O}$.&lt;/li>
&lt;li>Repetir lo siguiente desde el bit más significativo de $k$ hasta el menos significativo:
&lt;ul>
&lt;li>Duplicar $R$ (Point Doubling: $R = 2R$)&lt;/li>
&lt;li>Si el bit actual es &lt;code>1&lt;/code>, sumar $P$ a $R$ (Point Addition: $R = R + P$)&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>Gracias a este algoritmo, la complejidad computacional se reduce drásticamente de $O(k)$ a $O(\log_2 k)$, permitiendo cálculos en un tiempo realista (cuestión de milisegundos).&lt;/p>
&lt;hr>
&lt;h2 id="6-intercambio-de-claves-de-curva-elíptica-diffie-hellman-ecdh">6. Intercambio de claves de curva elíptica Diffie-Hellman (ECDH)
&lt;/h2>&lt;p>Aquí explicaremos el funcionamiento del &lt;strong>protocolo de intercambio de claves ECDH (Elliptic Curve Diffie-Hellman)&lt;/strong>, que es el ejemplo de aplicación más representativo de ECC. ECDH es un mecanismo que permite a Alice y Bob generar y compartir de forma segura una clave secreta compartida (clave de sesión) sobre un canal de comunicación que podría ser espiado (es el núcleo del apretón de manos TLS).&lt;/p>
&lt;p>&lt;strong>[Parámetros preestablecidos (Parámetros de dominio)]&lt;/strong>
Ambas partes comparten de antemano la curva elíptica $E$ a utilizar, el número primo $p$ y el punto base $G$. (Por ejemplo, NIST P-256 o secp256k1)&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant Alice as "Alice"
participant Bob as "Bob"
Note over Alice,Bob: "Parámetros públicos: Curva E, Punto base G, Primo p"
Alice->>Alice: "Generar clave privada d_A (entero aleatorio)"
Alice->>Alice: "Calcular clave pública Q_A = d_A * G"
Bob->>Bob: "Generar clave privada d_B (entero aleatorio)"
Bob->>Bob: "Calcular clave pública Q_B = d_B * G"
Alice->>Bob: "Enviar clave pública Q_A (texto plano)"
Bob->>Alice: "Enviar clave pública Q_B (texto plano)"
Alice->>Alice: "Calcular secreto compartido S = d_A * Q_B"
Bob->>Bob: "Calcular secreto compartido S = d_B * Q_A"
Note over Alice,Bob: "S = d_A * (d_B * G) = d_B * (d_A * G) = (d_A * d_B) * G"
Note over Alice,Bob: "¡Los resultados de cálculo S de ambos coinciden perfectamente!"&lt;/div>
&lt;p>Un atacante (Eve) que intercepte la comunicación puede obtener $G$, $Q_A$ y $Q_B$ que fluyen por la red, pero debido a la dificultad del ECDLP, no puede deducir la clave privada $d_A$ de Alice a partir de $Q_A = d_A \cdot G$. Además, multiplicar $Q_A$ y $Q_B$ no produce la clave compartida $S$, por lo que el espía no puede calcular $S$.&lt;/p>
&lt;hr>
&lt;h2 id="7-dificultades-en-la-implementación-ataques-de-canal-lateral-y-contramedidas">7. Dificultades en la implementación: Ataques de canal lateral y contramedidas
&lt;/h2>&lt;p>Incluso si un algoritmo criptográfico es teóricamente perfecto, pueden surgir vulnerabilidades en el proceso de implementarlo como programa. Esto se conoce como &lt;strong>&amp;ldquo;ataque de canal lateral (Side-Channel Attack)&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;h3 id="71-ataque-de-tiempo-timing-attack">7.1. Ataque de tiempo (Timing Attack)
&lt;/h3>&lt;p>Revisemos el algoritmo Double-and-Add mencionado anteriormente.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Pseudocódigo vulnerable de Double-and-Add
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">Point&lt;/span> &lt;span class="n">R&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Infinity&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">255&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">--&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">R&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PointDoubling&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// Siempre se ejecuta
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">bit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">k&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">R&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PointAddition&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// ¡Solo se ejecuta cuando el bit es 1!
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Esta implementación tiene una falla fatal. Dado que la suma de puntos (Point Addition) se ejecuta cuando el bit es &lt;code>1&lt;/code>, el &lt;strong>tiempo de cálculo será ligeramente mayor&lt;/strong> que cuando el bit es &lt;code>0&lt;/code>. Además, el comportamiento de predicción de bifurcaciones del procesador y la memoria caché también cambiará.
Si un atacante observa estadísticamente esta diminuta diferencia en el tiempo de cálculo (o el consumo de energía) miles de veces, &lt;strong>podría recuperar completamente la secuencia de bits de la clave privada $k$ bit por bit&lt;/strong>. Este es el ataque de tiempo.&lt;/p>
&lt;h3 id="72-implementación-de-tiempo-constante-constant-time-escalera-de-montgomery-montgomery-ladder">7.2. Implementación de tiempo constante (Constant-Time): Escalera de Montgomery (Montgomery Ladder)
&lt;/h3>&lt;p>Para prevenir los ataques de tiempo, es necesario adoptar un algoritmo en el que &lt;strong>la secuencia de instrucciones ejecutadas y el tiempo de cálculo sean siempre constantes (Constant-Time), independientemente del valor de los bits de la clave privada&lt;/strong>.&lt;/p>
&lt;p>El ejemplo representativo de esto es la &lt;strong>Escalera de Montgomery (Montgomery Ladder)&lt;/strong>.&lt;/p>
&lt;div class="mermaid">graph TD
Start["Inicialización: R0 = O, R1 = P"] --> LoopStart["Para cada bit i (desde el más significativo)"]
LoopStart --> Cond{"¿Cuál es el valor de k_i?"}
Cond -->|0| Branch0["R1 = R0 + R1&lt;br>R0 = 2 * R0"]
Cond -->|1| Branch1["R0 = R0 + R1&lt;br>R1 = 2 * R1"]
Branch0 --> LoopEnd["Siguiente bit"]
Branch1 --> LoopEnd
LoopEnd --> LoopStart
LoopStart -.->|"Todos los bits completados"| End["Fin: R0 es el resultado (kP)"]&lt;/div>
&lt;p>Lo hermoso de la Escalera de Montgomery es que, sin importar si el bit es &lt;code>0&lt;/code> o &lt;code>1&lt;/code>, &lt;strong>&amp;ldquo;siempre se ejecuta una suma de puntos (Point Addition) y una duplicación de puntos (Point Doubling)&amp;rdquo;&lt;/strong>. Esto elimina por completo la dependencia de los datos en el tiempo de cálculo.&lt;/p>
&lt;p>Sin embargo, si la bifurcación misma (&lt;code>if (k_i == 0)&lt;/code>) sigue existiendo, aún persiste el riesgo de que el tiempo de ejecución varíe debido a las optimizaciones del compilador o la predicción de saltos de la CPU. Por lo tanto, en una implementación real de Constant-Time, las bifurcaciones condicionales (instrucciones &lt;code>if&lt;/code>) se eliminan y se utiliza un &lt;strong>intercambio condicional (Conditional Swap) empleando operaciones a nivel de bits&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h2 id="8-implementación-de-criptografía-de-curva-elíptica-en-c">8. Implementación de criptografía de curva elíptica en C++
&lt;/h2>&lt;p>A partir de aquí, plasmaremos la teoría en código C++. Las bibliotecas criptográficas prácticas (como OpenSSL o libsodium) utilizan optimizaciones avanzadas en ensamblador o el sistema de coordenadas jacobianas, pero aquí, para profundizar en la comprensión matemática, mostraremos la estructura de una &lt;strong>implementación Constant-Time fácil de entender usando coordenadas afines&lt;/strong>.&lt;/p>
&lt;p>Asumiremos el uso de &lt;code>boost::multiprecision::cpp_int&lt;/code> para las operaciones con enteros gigantes.&lt;/p>
&lt;h3 id="81-aritmética-modular-y-el-inverso">8.1. Aritmética modular y el inverso
&lt;/h3>&lt;p>Primero, definiremos funciones de ayuda para operaciones en campos finitos. Implementaremos el cálculo del inverso modular utilizando el pequeño teorema de Fermat.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;vector&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;stdexcept&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;boost/multiprecision/cpp_int.hpp&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="k">namespace&lt;/span> &lt;span class="n">boost&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">multiprecision&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Ejemplo usando el número primo p y parámetros de secp256k1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="nf">p&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">const&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">const&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">7&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Operación modular que devuelve un residuo positivo
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">cpp_int&lt;/span> &lt;span class="nf">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cpp_int&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">r&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">r&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="n">r&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nl">m&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">r&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Exponenciación modular (x^y mod m)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">cpp_int&lt;/span> &lt;span class="nf">powerMod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cpp_int&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">exp&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">exp&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">exp&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">res&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">base&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">exp&lt;/span> &lt;span class="o">/=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">res&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Inverso modular usando el pequeño teorema de Fermat
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">cpp_int&lt;/span> &lt;span class="nf">modInverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cpp_int&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Asumiendo que m es primo: n^(m-2) ≡ n^(-1) mod m
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="n">powerMod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="82-representación-de-puntos-y-operaciones-de-grupo-suma-y-duplicación">8.2. Representación de puntos y operaciones de grupo (Suma y Duplicación)
&lt;/h3>&lt;p>Implementamos la estructura &lt;code>Point&lt;/code> que maneja el punto en el infinito con una bandera, y las fórmulas de suma.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">struct&lt;/span> &lt;span class="nc">Point&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">bool&lt;/span> &lt;span class="n">isInfinity&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Crear el punto en el infinito
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">isInfinity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">true&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Crear un punto normal
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cpp_int&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">isInfinity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">false&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Suma de puntos en la curva elíptica (R = P + Q)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">Point&lt;/span> &lt;span class="nf">pointAdd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">Q&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">isInfinity&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="n">Q&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">Q&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">isInfinity&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">Q&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">Q&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="c1">// P + (-P) = punto en el infinito
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">lambda&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">Q&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">Q&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Point Doubling (Caso P = Q)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// lambda = (3x^2 + a) / 2y
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">num&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">den&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">modInverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lambda&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">num&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">den&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Point Addition (Caso P != Q)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// lambda = (y2 - y1) / (x2 - x1)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">num&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Q&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">den&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">modInverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Q&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lambda&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">num&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">den&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">x3&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">lambda&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">lambda&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">Q&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">y3&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">lambda&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">x3&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y3&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="83-implementación-de-intercambio-condicional-en-tiempo-constante-constant-time-conditional-swap">8.3. Implementación de intercambio condicional en tiempo constante (Constant-Time Conditional Swap)
&lt;/h3>&lt;p>Al intercambiar el contenido de variables basado en el valor de un bit de la clave privada, realizamos el intercambio únicamente mediante operaciones a nivel de bits (máscaras) sin utilizar sentencias &lt;code>if&lt;/code>. Esto asegura que la ruta de ejecución sea completamente constante.&lt;/p>
&lt;blockquote>
&lt;p>[!TIP]
En implementaciones reales, las clases de enteros de precisión múltiple con asignación dinámica como &lt;code>cpp_int&lt;/code> no son adecuadas para procesamientos de tiempo constante, ya que se filtra información de temporización mediante la asignación de memoria o variaciones en el tamaño de los arreglos. Las bibliotecas prácticas los representan con una longitud fija (por ejemplo, arreglos de 4 elementos uint64_t) e implementan el enmascaramiento a nivel de bits. El siguiente es un ejemplo conceptual.&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Constant-Time Swap conceptual (asumiendo enteros de longitud fija)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Si el bit es 1, intercambia P1 y P2; si es 0, no los intercambia
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">void&lt;/span> &lt;span class="nf">cswap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Point&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">P1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">P2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">uint8_t&lt;/span> &lt;span class="n">bit&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// bit es 0 o 1. La máscara es todo 1s (0xFF..) si bit=1, y todo 0s si es 0.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// (Aquí asumimos que cada palabra de la clase BigInt de longitud fija es w para la explicación)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="cm">/*
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> uint64_t mask = 0 - (uint64_t)bit;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> for (int i = 0; i &amp;lt; NUM_WORDS; i++) {
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> uint64_t dummy = mask &amp;amp; (P1.x.words[i] ^ P2.x.words[i]);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> P1.x.words[i] ^= dummy;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> P2.x.words[i] ^= dummy;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> // Procesar de igual manera la coordenada y, así como la bandera isInfinity
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> }
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// * Un intercambio perfecto de tiempo constante en boost::multiprecision es difícil,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// pero aquí lo limitaremos a simularlo mediante ramificación para comprender la lógica.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">bit&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">swap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">P1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">P2&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="84-multiplicación-escalar-mediante-la-escalera-de-montgomery">8.4. Multiplicación escalar mediante la Escalera de Montgomery
&lt;/h3>&lt;p>Combinamos &lt;code>pointAdd&lt;/code> y &lt;code>cswap&lt;/code> previamente descritos para implementar una multiplicación escalar segura.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Multiplicación escalar k * P (Método de Escalera de Montgomery)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">Point&lt;/span> &lt;span class="nf">scalarMultiply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Point&lt;/span> &lt;span class="n">R0&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="c1">// Punto en el infinito
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">Point&lt;/span> &lt;span class="n">R1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Obtener la longitud de bits de k (256 bits para secp256k1)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">numBits&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">256&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">numBits&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">--&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Obtener el valor del i-ésimo bit (0 o 1)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">uint8_t&lt;/span> &lt;span class="n">bit&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">static_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">uint8_t&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bit_test&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">k&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Si bit == 1, intercambiar R0 y R1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">cswap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">R1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">bit&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Siempre se ejecuta la misma operación (Point Addition y Point Doubling)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">R1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">pointAdd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">R1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">R0&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">pointAdd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">R0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Si bit == 1, volver a intercambiar para restaurar el estado original
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">cswap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">R1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">bit&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">R0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Con esta lógica de implementación, sin importar si cada bit del escalar $k$ es &lt;code>0&lt;/code> o &lt;code>1&lt;/code>, las operaciones ejecutadas dentro de la iteración de cada bucle (&lt;code>cswap&lt;/code> $\to$ &lt;code>pointAdd&lt;/code> $\to$ &lt;code>pointAdd&lt;/code> $\to$ &lt;code>cswap&lt;/code>) seguirán un flujo exactamente igual, previniendo fuertemente la fuga de información confidencial a través de variaciones en la temporización o los patrones de acceso a la caché.&lt;/p>
&lt;hr>
&lt;h2 id="9-conclusión">9. Conclusión
&lt;/h2>&lt;p>A primera vista, la criptografía de curva elíptica (ECC) puede parecer un misterio y hacernos preguntar, &amp;ldquo;¿Cómo pueden las operaciones geométricas como trazar una línea recta y reflejar intersecciones convertirse en criptografía?&amp;rdquo;. Sin embargo, al mapearlo al mundo discreto de los campos finitos, es el producto de una fusión milagrosa entre las matemáticas y la criptografía que nos permite construir espléndidas funciones unidireccionales (problema del logaritmo discreto).&lt;/p>
&lt;p>En este artículo explicamos los siguientes puntos clave:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Ventaja sobre RSA&lt;/strong>: Ofrece una seguridad robusta con longitudes de clave muy cortas, ideal para la era actual móvil e IoT.&lt;/li>
&lt;li>&lt;strong>Fundamentos de teoría de grupos y campos finitos&lt;/strong>: La estructura matemática que sirve como base para ECC.&lt;/li>
&lt;li>&lt;strong>Fórmulas de suma y duplicación&lt;/strong>: Métodos de implementación de operaciones de grupo algebraicas usando la ecuación de Weierstrass.&lt;/li>
&lt;li>&lt;strong>La amenaza de los ataques de canal lateral&lt;/strong>: Cómo las bifurcaciones condicionales que dependen de los bits de la clave privada crean vulnerabilidades fatales.&lt;/li>
&lt;li>&lt;strong>Implementación de tiempo constante (Constant-Time)&lt;/strong>: Técnicas de codificación en C++ usando la Escalera de Montgomery y el Intercambio Condicional para igualar el comportamiento a nivel de hardware y prevenir ataques.&lt;/li>
&lt;/ol>
&lt;p>Crear por tu cuenta una biblioteca criptográfica para entornos de producción generalmente no se recomienda (&amp;ldquo;Don&amp;rsquo;t roll your own crypto&amp;rdquo;) debido a los riesgos de seguridad extremadamente altos. Sin embargo, comprender profundamente los algoritmos subyacentes y el contexto matemático puede ser una herramienta invaluable y poderosa para los ingenieros que diseñan y operan sistemas más seguros y eficientes.&lt;/p>
&lt;p>En el próximo artículo, nos gustaría profundizar más en el mecanismo de &lt;strong>ECDSA (Elliptic Curve Digital Signature Algorithm)&lt;/strong>, un algoritmo de firma digital que utiliza esta curva elíptica, y en las &lt;strong>firmas de Schnorr&lt;/strong> adoptadas en Bitcoin.&lt;/p></description></item><item><title>Simulé el algoritmo de Shor en Python</title><link>http://kenji.blog/es/p/shors-algorithm-simulation-python/</link><pubDate>Fri, 11 Sep 2026 08:00:00 +0900</pubDate><guid>http://kenji.blog/es/p/shors-algorithm-simulation-python/</guid><description>&lt;img src="http://kenji.blog/p/shors-algorithm-simulation-python/img/eyecatch.jpg" alt="Featured image of post Simulé el algoritmo de Shor en Python" />&lt;h1 id="1-introducción-la-crisis-de-la-criptografía-provocada-por-las-computadoras-cuánticas">1. Introducción: La crisis de la criptografía provocada por las computadoras cuánticas
&lt;/h1>&lt;p>Gran parte de la seguridad en la sociedad de Internet moderna depende de la &lt;strong>criptografía de clave pública&lt;/strong> (especialmente RSA). Cuando enviamos la información de nuestra tarjeta de crédito en compras en línea o intercambiamos datos altamente confidenciales, el contenido de esa comunicación está fuertemente protegido por la criptografía RSA.&lt;/p>
&lt;p>La base de la seguridad de la criptografía RSA radica en el hecho matemático de que &amp;ldquo;&lt;strong>la factorización de números enteros enormes es extremadamente difícil para las computadoras clásicas (las PC y supercomputadoras que usamos normalmente)&lt;/strong>&amp;rdquo;. Sin embargo, el &amp;ldquo;&lt;strong>Algoritmo de Shor (Shor&amp;rsquo;s Algorithm)&lt;/strong>&amp;rdquo;, publicado por Peter Shor en 1994, socavó esta premisa desde sus cimientos. Se demostró matemáticamente que, si se ejecuta el algoritmo de Shor en una computadora cuántica a gran escala, la factorización de números primos, que le tomaría a una computadora clásica más tiempo que la edad del universo, puede resolverse en solo unos minutos a unas horas.&lt;/p>
&lt;p>En este artículo, explicaremos exhaustiva y detalladamente cómo el algoritmo de Shor realiza la factorización de manera rápida, desde su mecanismo matemático hasta la implementación de una simulación concreta utilizando Python y el marco de computación cuántica &lt;strong>Qiskit&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h1 id="2-cambio-dramático-en-la-complejidad-computacional-de-tiempo-exponencial-a-polinómico">2. Cambio dramático en la complejidad computacional: De tiempo exponencial a polinómico
&lt;/h1>&lt;p>¿Por qué es difícil la factorización? Incluso si utilizamos la &amp;ldquo;Criba General del Campo de Números (General Number Field Sieve, GNFS)&amp;rdquo;, conocida como el mejor algoritmo de factorización en computadoras clásicas, su complejidad computacional es subexponencial.&lt;/p>
&lt;p>La complejidad temporal para factorizar un número compuesto de $N$ dígitos con métodos clásicos es la siguiente:&lt;/p>
$$ O\left(\exp\left( c (\log N)^{1/3} (\log \log N)^{2/3} \right)\right) $$
&lt;p>Por lo tanto, con solo aumentar la longitud de la clave (por ejemplo, a 2048 o 4096 bits), a una computadora clásica le tomará miles o decenas de miles de años descifrarla, un tiempo que no es realista.&lt;/p>
&lt;p>Sin embargo, al utilizar el &lt;strong>algoritmo de Shor&lt;/strong> en una computadora cuántica, la complejidad computacional se reduce dramáticamente a tiempo polinómico respecto al número de bits de entrada $\log N$.&lt;/p>
$$ O((\log N)^3) $$
&lt;p>Esto significa que si se duplica el número de bits, el tiempo de cálculo en una computadora clásica aumentaría astronómicamente, mientras que en una computadora cuántica aumentaría como mucho unas 8 veces. Esta &lt;strong>reducción de la clase de complejidad de tiempo exponencial a polinómico (inclusión en la clase BQP)&lt;/strong> es la verdadera grandeza del algoritmo de Shor.&lt;/p>
&lt;div class="mermaid">graph TD
A["Aumento del tamaño de entrada (número de bits) N"] --> B{"Elección del algoritmo"}
B -->|Clásico: Criba General del Campo de Números| C["Aumento subexponencial O(exp(...))"]
B -->|Cuántico: Algoritmo de Shor| D["Tiempo polinómico O((log N)^3)"]
C --> E["Miles a miles de millones de años (Indescifrable)"]
D --> F["Minutos a horas (Descifrable en tiempo realista)"]&lt;/div>
&lt;hr>
&lt;h1 id="3-visión-general-del-algoritmo-y-fundamentos-matemáticos">3. Visión general del algoritmo y fundamentos matemáticos
&lt;/h1>&lt;p>De hecho, el algoritmo de Shor no realiza todo en la computadora cuántica. Está compuesto por una colaboración entre el preprocesamiento y posprocesamiento mediante una computadora clásica y la parte central (algoritmo de búsqueda de período) realizada por la computadora cuántica.&lt;/p>
&lt;p>El flujo general del algoritmo es el siguiente:&lt;/p>
&lt;div class="mermaid">graph TD
A["Entrada: Número compuesto a factorizar N"] --> B["Elegir un número aleatorio a tal que a &lt; N"]
B --> C{"gcd(a, N) > 1 ?"}
C -- "Sí" --> D["Imprimir factor trivial gcd(a, N) y terminar"]
C -- "No" --> E["Encontrar el período r de f(x) = a^x mod N con algoritmo cuántico"]
E --> F{"¿r es par y a^(r/2) ≢ -1 mod N ?"}
F -- "No" --> B
F -- "Sí" --> G["Calcular factores p = gcd(a^(r/2) - 1, N), q = gcd(a^(r/2) + 1, N)"]
G --> H["Salida: p, q"]&lt;/div>
&lt;h2 id="reducción-del-problema-de-factorización-al-problema-de-búsqueda-de-período">Reducción del problema de factorización al problema de búsqueda de período
&lt;/h2>&lt;p>La genialidad de Shor consistió en transformar el &amp;ldquo;&lt;strong>problema de factorización&lt;/strong>&amp;rdquo; en el &amp;ldquo;&lt;strong>problema de búsqueda de período (Order Finding Problem)&lt;/strong>&amp;rdquo;.&lt;/p>
&lt;p>Consideremos un entero $N$ (el número que queremos factorizar) y un entero $a$ coprimo con $N$ ($1 &lt; a &lt; N$). Definimos la siguiente función exponencial modular:&lt;/p>
$$ f(x) = a^x \bmod N $$
&lt;p>Esta función tiene un cierto período $r$. Es decir, para cualquier $x$, se cumple que $f(x+r) = f(x)$. Especialmente cuando $x=0$,&lt;/p>
$$ a^r \equiv 1 \pmod N $$
&lt;p>El número entero positivo más pequeño $r$ que cumple esto se llama el &amp;ldquo;orden (Order) de $a$ módulo $N$&amp;rdquo;. Si podemos encontrar este período $r$, podemos deducir los factores primos de la siguiente manera.&lt;/p>
&lt;p>Transformando la ecuación,
&lt;/p>
$$ a^r - 1 \equiv 0 \pmod N $$
&lt;p>
Si $r$ es par, se puede factorizar usando la fórmula de diferencia de cuadrados.
&lt;/p>
$$ (a^{r/2} - 1)(a^{r/2} + 1) \equiv 0 \pmod N $$
&lt;p>Esto significa que $N$ tiene un factor común con $(a^{r/2} - 1)$ o con $(a^{r/2} + 1)$ (siempre que se cumpla la condición $a^{r/2} \not\equiv -1 \pmod N$). Por lo tanto, utilizando el algoritmo de Euclides, calculando&lt;/p>
$$ p = \gcd(a^{r/2} - 1, N) $$
$$ q = \gcd(a^{r/2} + 1, N) $$
&lt;p>podemos encontrar los factores primos no triviales $p, q$ de $N$. Este cálculo (el cálculo del máximo común divisor y la generación de números aleatorios) se puede realizar de manera muy rápida en una computadora clásica. El problema se reduce a &lt;strong>cómo encontrar rápidamente el período $r$&lt;/strong>. En una computadora clásica, encontrar este período $r$ en sí mismo requiere un tiempo exponencial. Aquí es donde entra en juego la computadora cuántica.&lt;/p>
&lt;hr>
&lt;h1 id="4-parte-del-algoritmo-cuántico-mecanismo-de-búsqueda-de-período">4. Parte del algoritmo cuántico: Mecanismo de búsqueda de período
&lt;/h1>&lt;p>La subrutina para encontrar el período $r$ utilizando una computadora cuántica consta de los siguientes cuatro pasos.&lt;/p>
&lt;div class="mermaid">graph LR
subgraph "Transición del estado cuántico"
S1["|0⟩|0⟩ (Inicialización)"] --> S2["Compuerta H: Superposición Σ|x⟩|0⟩"]
S2 --> S3["Oráculo U: Σ|x⟩|a^x mod N⟩"]
S3 --> S4["QFT: Extracción del período por interferencia"]
S4 --> S5["Medición: Obtención del valor aproximado y"]
end&lt;/div>
&lt;h2 id="paso-1-inicialización-de-los-registros-cuánticos-y-superposición">Paso 1: Inicialización de los registros cuánticos y superposición
&lt;/h2>&lt;p>Primero, preparamos dos registros cuánticos. El primer registro es para la entrada de estado, y el segundo registro es para almacenar el resultado del cálculo de la función.
El estado inicial es todo $|0\rangle$.&lt;/p>
$$ |\psi_0\rangle = |0\rangle_1 |0\rangle_2 $$
&lt;p>Aplicamos la compuerta de Hadamard (Hadamard Gate) a todos los qubits del primer registro, creando un estado de superposición equiprobable de todas las entradas posibles $x$ (desde $0$ hasta $Q-1$, $Q=2^n$).&lt;/p>
$$ |\psi_1\rangle = \frac{1}{\sqrt{Q}} \sum_{x=0}^{Q-1} |x\rangle_1 |0\rangle_2 $$
&lt;p>De esta manera, la computadora cuántica mantendrá los estados de todas las $Q$ entradas simultáneamente en una sola operación. Esta es la poderosa fuente del &lt;strong>paralelismo cuántico&lt;/strong>.&lt;/p>
&lt;h2 id="paso-2-aplicación-de-la-función-oráculo-exponenciación-modular">Paso 2: Aplicación de la función oráculo (exponenciación modular)
&lt;/h2>&lt;p>A continuación, utilizamos el circuito de operación cuántica $U_f$ para calcular la función $f(x) = a^x \bmod N$ y almacenamos el resultado en el segundo registro.&lt;/p>
$$ |\psi_2\rangle = \frac{1}{\sqrt{Q}} \sum_{x=0}^{Q-1} |x\rangle_1 |a^x \bmod N\rangle_2 $$
&lt;p>En este punto, el primer y el segundo registro se encuentran en un estado de &lt;strong>entrelazamiento cuántico (Entanglement)&lt;/strong>. Si (hipotéticamente) observáramos el segundo registro y obtuviéramos un valor específico $k = a^{x_0} \bmod N$, el estado del primer registro colapsaría a una superposición de las $x$ que producen ese valor $k$. Dado que el período de la función es $r$, los estados restantes serán valores separados por $r$: $x_0, x_0+r, x_0+2r, \dots$&lt;/p>
$$ |\psi_3\rangle = \sqrt{\frac{r}{Q}} \sum_{j=0}^{M-1} |x_0 + j r\rangle_1 |k\rangle_2 $$
&lt;p>Sin embargo, no queremos conocer $x_0$, sino el período $r$ en sí. Es imposible observar $r$ directamente desde este estado. Por lo tanto, utilizamos la transformada de Fourier cuántica.&lt;/p>
&lt;h2 id="paso-3-interferencia-de-fase-mediante-la-transformada-de-fourier-cuántica-qft">Paso 3: Interferencia de fase mediante la Transformada de Fourier Cuántica (QFT)
&lt;/h2>&lt;p>Aplicamos la &lt;strong>Transformada de Fourier Cuántica (Quantum Fourier Transform, QFT)&lt;/strong> al primer registro. La QFT es la versión cuántica de la transformada de Fourier discreta clásica y transforma la amplitud del vector de estado. La acción de la QFT sobre el estado base $|x\rangle$ se define de la siguiente manera:&lt;/p>
$$ QFT |x\rangle = \frac{1}{\sqrt{Q}} \sum_{y=0}^{Q-1} \omega^{xy} |y\rangle $$
&lt;p>Aquí, $\omega = e^{2\pi i / Q}$.&lt;/p>
&lt;p>Al aplicar la QFT, las amplitudes de estado interfieren. Omitiendo los detalles matemáticos, cuando se aplica la QFT a un estado con período $r$, las ondas causan una &lt;strong>interferencia constructiva (Constructive Interference)&lt;/strong> solo cuando $y$ es un valor extremadamente cercano a un múltiplo entero de $Q/r$. Para los demás estados, las amplitudes de probabilidad se anulan debido a la &lt;strong>interferencia destructiva (Destructive Interference)&lt;/strong> y se acercan a cero.&lt;/p>
&lt;h2 id="paso-4-medición-y-expansión-en-fracciones-continuas">Paso 4: Medición y expansión en fracciones continuas
&lt;/h2>&lt;p>Finalmente, medimos el primer registro. El valor $y$ obtenido de la medición satisfará la siguiente condición con alta probabilidad:&lt;/p>
$$ y \approx c \frac{Q}{r} \implies \frac{y}{Q} \approx \frac{c}{r} $$
&lt;p>($c$ es un entero desconocido tal que $0 \le c &lt; r$)&lt;/p>
&lt;p>Al aplicar el algoritmo clásico de &lt;strong>expansión en fracciones continuas (Continued Fraction Expansion)&lt;/strong> al número racional obtenido $y/Q$, calculamos la fracción aproximada $c/r$ y extraemos el período $r$ del denominador.&lt;/p>
&lt;hr>
&lt;h1 id="5-implementación-de-simulación-utilizando-python-y-qiskit">5. Implementación de simulación utilizando Python y Qiskit
&lt;/h1>&lt;p>Dado que la teoría por sí sola puede ser difícil de visualizar, simulemos realmente el algoritmo de Shor utilizando Python y el marco de cálculo cuántico de IBM, &lt;strong>Qiskit&lt;/strong>.&lt;/p>
&lt;p>Aquí implementaremos el escenario más clásico y famoso: &lt;strong>&amp;ldquo;factorizar $N=15$ utilizando $a=7$&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;h2 id="preparación-del-entorno-de-ejecución">Preparación del entorno de ejecución
&lt;/h2>&lt;p>Instala Qiskit de antemano.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">pip install qiskit qiskit-aer numpy
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="resumen-del-código-de-implementación-en-python">Resumen del código de implementación en Python
&lt;/h2>&lt;p>El siguiente código es un ejemplo de implementación del algoritmo de Shor especializado para $N=15, a=7$. Construir un circuito de exponenciación modular de propósito general tiene un costo computacional demasiado alto para los simuladores actuales, por lo que las operaciones de las compuertas para el caso específico de $a=7$ están programadas en duro (hardcoded).&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt"> 10
&lt;/span>&lt;span class="lnt"> 11
&lt;/span>&lt;span class="lnt"> 12
&lt;/span>&lt;span class="lnt"> 13
&lt;/span>&lt;span class="lnt"> 14
&lt;/span>&lt;span class="lnt"> 15
&lt;/span>&lt;span class="lnt"> 16
&lt;/span>&lt;span class="lnt"> 17
&lt;/span>&lt;span class="lnt"> 18
&lt;/span>&lt;span class="lnt"> 19
&lt;/span>&lt;span class="lnt"> 20
&lt;/span>&lt;span class="lnt"> 21
&lt;/span>&lt;span class="lnt"> 22
&lt;/span>&lt;span class="lnt"> 23
&lt;/span>&lt;span class="lnt"> 24
&lt;/span>&lt;span class="lnt"> 25
&lt;/span>&lt;span class="lnt"> 26
&lt;/span>&lt;span class="lnt"> 27
&lt;/span>&lt;span class="lnt"> 28
&lt;/span>&lt;span class="lnt"> 29
&lt;/span>&lt;span class="lnt"> 30
&lt;/span>&lt;span class="lnt"> 31
&lt;/span>&lt;span class="lnt"> 32
&lt;/span>&lt;span class="lnt"> 33
&lt;/span>&lt;span class="lnt"> 34
&lt;/span>&lt;span class="lnt"> 35
&lt;/span>&lt;span class="lnt"> 36
&lt;/span>&lt;span class="lnt"> 37
&lt;/span>&lt;span class="lnt"> 38
&lt;/span>&lt;span class="lnt"> 39
&lt;/span>&lt;span class="lnt"> 40
&lt;/span>&lt;span class="lnt"> 41
&lt;/span>&lt;span class="lnt"> 42
&lt;/span>&lt;span class="lnt"> 43
&lt;/span>&lt;span class="lnt"> 44
&lt;/span>&lt;span class="lnt"> 45
&lt;/span>&lt;span class="lnt"> 46
&lt;/span>&lt;span class="lnt"> 47
&lt;/span>&lt;span class="lnt"> 48
&lt;/span>&lt;span class="lnt"> 49
&lt;/span>&lt;span class="lnt"> 50
&lt;/span>&lt;span class="lnt"> 51
&lt;/span>&lt;span class="lnt"> 52
&lt;/span>&lt;span class="lnt"> 53
&lt;/span>&lt;span class="lnt"> 54
&lt;/span>&lt;span class="lnt"> 55
&lt;/span>&lt;span class="lnt"> 56
&lt;/span>&lt;span class="lnt"> 57
&lt;/span>&lt;span class="lnt"> 58
&lt;/span>&lt;span class="lnt"> 59
&lt;/span>&lt;span class="lnt"> 60
&lt;/span>&lt;span class="lnt"> 61
&lt;/span>&lt;span class="lnt"> 62
&lt;/span>&lt;span class="lnt"> 63
&lt;/span>&lt;span class="lnt"> 64
&lt;/span>&lt;span class="lnt"> 65
&lt;/span>&lt;span class="lnt"> 66
&lt;/span>&lt;span class="lnt"> 67
&lt;/span>&lt;span class="lnt"> 68
&lt;/span>&lt;span class="lnt"> 69
&lt;/span>&lt;span class="lnt"> 70
&lt;/span>&lt;span class="lnt"> 71
&lt;/span>&lt;span class="lnt"> 72
&lt;/span>&lt;span class="lnt"> 73
&lt;/span>&lt;span class="lnt"> 74
&lt;/span>&lt;span class="lnt"> 75
&lt;/span>&lt;span class="lnt"> 76
&lt;/span>&lt;span class="lnt"> 77
&lt;/span>&lt;span class="lnt"> 78
&lt;/span>&lt;span class="lnt"> 79
&lt;/span>&lt;span class="lnt"> 80
&lt;/span>&lt;span class="lnt"> 81
&lt;/span>&lt;span class="lnt"> 82
&lt;/span>&lt;span class="lnt"> 83
&lt;/span>&lt;span class="lnt"> 84
&lt;/span>&lt;span class="lnt"> 85
&lt;/span>&lt;span class="lnt"> 86
&lt;/span>&lt;span class="lnt"> 87
&lt;/span>&lt;span class="lnt"> 88
&lt;/span>&lt;span class="lnt"> 89
&lt;/span>&lt;span class="lnt"> 90
&lt;/span>&lt;span class="lnt"> 91
&lt;/span>&lt;span class="lnt"> 92
&lt;/span>&lt;span class="lnt"> 93
&lt;/span>&lt;span class="lnt"> 94
&lt;/span>&lt;span class="lnt"> 95
&lt;/span>&lt;span class="lnt"> 96
&lt;/span>&lt;span class="lnt"> 97
&lt;/span>&lt;span class="lnt"> 98
&lt;/span>&lt;span class="lnt"> 99
&lt;/span>&lt;span class="lnt">100
&lt;/span>&lt;span class="lnt">101
&lt;/span>&lt;span class="lnt">102
&lt;/span>&lt;span class="lnt">103
&lt;/span>&lt;span class="lnt">104
&lt;/span>&lt;span class="lnt">105
&lt;/span>&lt;span class="lnt">106
&lt;/span>&lt;span class="lnt">107
&lt;/span>&lt;span class="lnt">108
&lt;/span>&lt;span class="lnt">109
&lt;/span>&lt;span class="lnt">110
&lt;/span>&lt;span class="lnt">111
&lt;/span>&lt;span class="lnt">112
&lt;/span>&lt;span class="lnt">113
&lt;/span>&lt;span class="lnt">114
&lt;/span>&lt;span class="lnt">115
&lt;/span>&lt;span class="lnt">116
&lt;/span>&lt;span class="lnt">117
&lt;/span>&lt;span class="lnt">118
&lt;/span>&lt;span class="lnt">119
&lt;/span>&lt;span class="lnt">120
&lt;/span>&lt;span class="lnt">121
&lt;/span>&lt;span class="lnt">122
&lt;/span>&lt;span class="lnt">123
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">numpy&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">np&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">qiskit&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">QuantumCircuit&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">qiskit_aer&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AerSimulator&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">qiskit.visualization&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">plot_histogram&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">fractions&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Fraction&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">math&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. Función para construir la Transformada de Fourier Cuántica Inversa (QFT†)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">qft_dagger&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Genera un circuito de transformada de Fourier cuántica inversa de n qubits&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">QuantumCircuit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Compuertas SWAP para invertir el orden&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">qubit&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="o">//&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">swap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">qubit&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">qubit&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Aplicación de compuertas de fase controladas y compuertas H&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">j&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">np&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">pi&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nb">float&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="o">**&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">m&lt;/span>&lt;span class="p">)),&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">j&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">h&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;QFT_dagger&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">qc&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. Función para construir la operación de exponenciación modular controlada 7^x mod 15&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">c_amod15&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">power&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Genera una compuerta U controlada para un a específico y su potencia (exclusivo para N=15)&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">QuantumCircuit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">_&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">power&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Lógica de codificación dura (hardcoding) de 7^x mod 15 para el caso a=7&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">swap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">swap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">swap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">7&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">8&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">swap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">swap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">swap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">swap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">swap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">7&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">11&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">q&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">q&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">to_gate&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">^&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">power&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> mod 15&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">c_U&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">control&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">c_U&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. Construcción del circuito cuántico principal&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">shor_circuit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n_count&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># n_count: Número de bits del registro de control&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># El registro objetivo requiere 4 bits para representar de 0 a 15&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">QuantumCircuit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n_count&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n_count&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Inicialización del primer registro (registro de control) (generación de superposición)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">q&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n_count&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">h&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">q&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Inicialización del segundo registro (registro objetivo) a |1&amp;gt; (0001)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">n_count&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Aplicación de la operación de exponenciación modular controlada (Oráculo)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">q&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n_count&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Aplicación de la operación elevada a la potencia 2^q&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c_amod15&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="o">**&lt;/span>&lt;span class="n">q&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="n">q&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="n">n_count&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">)])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Aplicación de la transformada de Fourier cuántica inversa al primer registro&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">qft_dagger&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n_count&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n_count&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Medición del primer registro&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">measure&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n_count&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n_count&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">qc&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># --- Sección de ejecución ---&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;__main__&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">N&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">15&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">7&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">n_count&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">8&lt;/span> &lt;span class="c1"># Uso de 8 qubits para el registro de control (Q=256)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Configuración de búsqueda: N=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">, a=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">, qubits de control=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">n_count&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Generación del circuito&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">shor_circuit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n_count&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Ejecución en el simulador&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sim&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AerSimulator&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Se recomienda usar transpile en las versiones recientes de Qiskit&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">from&lt;/span> &lt;span class="nn">qiskit&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">transpile&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">compiled_circuit&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">transpile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">qc&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sim&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">job&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sim&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">compiled_circuit&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">shots&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1024&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">job&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">counts&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_counts&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">Resultados de medición (cadena de bits: número de observaciones):&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">bitstring&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">count&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">counts&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">items&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34; &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">bitstring&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">count&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> veces&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Posprocesamiento clásico: Identificación del período r mediante expansión de fracciones continuas&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">--- Cálculo del período y factorización ---&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">phases&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">output&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">counts&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Convertir cadena de bits a número decimal&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">decimal&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">output&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Fase = valor medido / 2^n_count&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">phase&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">decimal&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="o">**&lt;/span>&lt;span class="n">n_count&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">phases&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">phase&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Obtener la fracción aproximada por expansión de fracciones continuas. El límite del denominador es N=15&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">frac&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Fraction&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">phase&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">limit_denominator&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">15&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">r&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">frac&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">denominator&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Valor observado: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">decimal&lt;/span>&lt;span class="si">:&lt;/span>&lt;span class="s2">3d&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> | Fase: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">phase&lt;/span>&lt;span class="si">:&lt;/span>&lt;span class="s2">.4f&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> | Fracción continua: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">frac&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> | Período estimado r = &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">r&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Comprobar si el período r es par y produce resultados válidos&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">r&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">guess1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">math&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="o">**&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">r&lt;/span>&lt;span class="o">//&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">N&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">guess2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">math&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="o">**&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">r&lt;/span>&lt;span class="o">//&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">N&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">guess1&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">N&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="ow">or&lt;/span> &lt;span class="n">guess2&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">N&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34; =&amp;gt; ¡Éxito! Los factores primos de &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> son &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">guess1&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> y &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">guess2&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34; =&amp;gt; Solo factores triviales. Intentando de nuevo.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34; =&amp;gt; Fallo porque el período es impar.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="explicación-del-código-y-análisis-de-los-resultados-de-ejecución">Explicación del código y análisis de los resultados de ejecución
&lt;/h2>&lt;p>Al ejecutar el código anterior, como resultado de la medición del registro de control, se obtienen picos específicos (valores observados) con alta probabilidad. En el caso de &lt;code>n_count=8&lt;/code> ($Q=256$), si se trata de una computadora cuántica ideal (o un simulador), los valores observados como &lt;code>0&lt;/code>, &lt;code>64&lt;/code>, &lt;code>128&lt;/code> y &lt;code>192&lt;/code> aparecerán con una probabilidad abrumadora.&lt;/p>
&lt;p>Si dividimos estos valores por $Q=256$, las fases $y/Q$ serán $0.0$, $0.25$, $0.5$ y $0.75$, respectivamente.
Al expandir estas fases en fracciones continuas, obtenemos:&lt;/p>
&lt;ul>
&lt;li>$0.25 \to 1/4$ (Período estimado $r=4$)&lt;/li>
&lt;li>$0.50 \to 1/2$ (Período estimado $r=2$)&lt;/li>
&lt;li>$0.75 \to 3/4$ (Período estimado $r=4$)&lt;/li>
&lt;/ul>
&lt;p>Utilizando el período obtenido $r=4$, calculamos los factores primos.
Dado que $a=7, r=4$,
$p = \gcd(7^2 - 1, 15) = \gcd(48, 15) = 3$
$q = \gcd(7^2 + 1, 15) = \gcd(50, 15) = 5$&lt;/p>
&lt;p>Impresionantemente, hemos tenido éxito en la factorización de $15 = 3 \times 5$.&lt;/p>
&lt;blockquote>
&lt;p>[!TIP]
Si se obtiene $y=128$ (fase $0.5$) como valor de medición, el denominador será $2$, y obtendremos un divisor del verdadero período $r=4$ en lugar del período real. En tales casos, se puede llegar al verdadero período ejecutando el algoritmo varias veces o examinando los múltiplos del $r$ obtenido.&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h1 id="6-desafíos-para-la-implementación-práctica-y-los-límites-de-la-era-nisq">6. Desafíos para la implementación práctica y los límites de la era NISQ
&lt;/h1>&lt;p>Si bien fue fácil factorizar $N=15$ en un simulador, factorizar RSA-2048 (un número decimal de 617 dígitos) utilizado en el mundo real presenta numerosos obstáculos para las computadoras cuánticas actuales.&lt;/p>
&lt;p>La época en la que vivimos actualmente se llama la &lt;strong>era NISQ (Noisy Intermediate-Scale Quantum: Cuántica de Escala Intermedia Ruidosa)&lt;/strong>. Los qubits son extremadamente vulnerables al ruido ambiental y sufren &amp;ldquo;decoherencia&amp;rdquo; durante los cálculos, lo que destruye su estado.&lt;/p>
&lt;p>Para ejecutar con precisión circuitos profundos (con muchas compuertas) como el algoritmo de Shor, la &lt;strong>corrección de errores cuánticos (Quantum Error Correction)&lt;/strong>, que corrige el ruido, es indispensable. Para crear un solo &amp;ldquo;qubit lógico&amp;rdquo; sin ruido, es necesario codificar miles de &amp;ldquo;qubits físicos&amp;rdquo; utilizando un código de superficie (Surface Code) u otros métodos.&lt;/p>
&lt;p>Se estima que para romper la criptografía RSA de 2048 bits se requieren miles de qubits lógicos perfectos, y lograr esto requeriría una computadora cuántica tolerante a fallos (fault-tolerant) equipada con &lt;strong>millones a decenas de millones de qubits físicos&lt;/strong>. Dado que los procesadores cuánticos más avanzados de la actualidad tienen solo unos cientos a unos pocos miles de qubits físicos, la criptografía mundial no se romperá de manera inminente.&lt;/p>
&lt;blockquote>
&lt;p>[!WARNING]
Sin embargo, existe un modelo de amenaza llamado &amp;ldquo;Store Now, Decrypt Later (Almacenar ahora, descifrar después)&amp;rdquo;. Los atacantes podrían adoptar la estrategia de almacenar masivamente comunicaciones confidenciales actualmente encriptadas en forma de datos cifrados, con el objetivo de descifrarlo todo en el momento en que se complete una computadora cuántica poderosa dentro de 10 a 20 años.&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h1 id="7-transición-hacia-la-criptografía-post-cuántica-pqc">7. Transición hacia la Criptografía Post-Cuántica (PQC)
&lt;/h1>&lt;p>En preparación para la llegada de ese &amp;ldquo;Día Q (Q-Day)&amp;rdquo; (el día en que las computadoras cuánticas rompan la criptografía), los criptógrafos de todo el mundo, liderados por el Instituto Nacional de Estándares y Tecnología de EE. UU. (NIST), están avanzando en el desarrollo de la &lt;strong>Criptografía Post-Cuántica (Post-Quantum Cryptography, PQC)&lt;/strong>.&lt;/p>
&lt;p>La PQC se basa en nuevos problemas matemáticos (problemas basados en retículos, polinomios multivariantes, funciones hash, etc.) que se consideran matemáticamente difíciles de resolver de manera eficiente incluso utilizando el algoritmo de Shor (o el algoritmo de Grover). Algoritmos como &amp;ldquo;CRYSTALS-Kyber&amp;rdquo; y &amp;ldquo;CRYSTALS-Dilithium&amp;rdquo; ya han sido seleccionados como estándares y su implementación en protocolos de comunicación para navegadores web e iMessage de Apple está comenzando gradualmente.&lt;/p>
&lt;p>Para los ingenieros que gestionan la infraestructura de TI, incorporar &amp;ldquo;agilidad criptográfica (crypto-agility: la capacidad del diseño para cambiar rápidamente los esquemas criptográficos)&amp;rdquo; en sus sistemas para hacer la transición desde la actual RSA y la criptografía de curva elíptica a PQC será una misión importante en el futuro.&lt;/p>
&lt;hr>
&lt;h1 id="8-conclusión">8. Conclusión
&lt;/h1>&lt;p>En este artículo, hemos proporcionado una explicación exhaustiva de aproximadamente 10,000 caracteres, que abarca desde los fundamentos teóricos y matemáticos del algoritmo de Shor hasta el mecanismo de extracción de períodos mediante la transformada de Fourier cuántica y, finalmente, un código de simulación concreto utilizando Python y Qiskit.&lt;/p>
&lt;p>El hecho de que las leyes físicas del mundo microscópico de la mecánica cuántica estén subvirtiendo fundamentalmente la teoría de la complejidad computacional y la teoría de la criptografía, que son la base de la ciencia de la información macroscópica, es uno de los cambios de paradigma más emocionantes en la historia de la ciencia. No podemos quitarle los ojos de encima a las continuas batallas ofensivas y defensivas entre la tecnología de la computación cuántica, que continúa desarrollándose en tiempo real, y las nuevas tecnologías criptográficas que la enfrentan.&lt;/p>
&lt;p>Te invitamos a ejecutar el código de Python presentado aquí en tu propio entorno para experimentar la &amp;ldquo;magia computacional&amp;rdquo; producida por la superposición de estados cuánticos y la interferencia.&lt;/p>
&lt;hr>
&lt;p>&lt;strong>Referencias&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Shor, P. W. (1994). &amp;ldquo;Algorithms for quantum computation: discrete logarithms and factoring&amp;rdquo;. Proceedings 35th Annual Symposium on Foundations of Computer Science.&lt;/li>
&lt;li>Nielsen, M. A., &amp;amp; Chuang, I. L. (2010). &amp;ldquo;Quantum Computation and Quantum Information&amp;rdquo;. Cambridge University Press.&lt;/li>
&lt;li>Qiskit Documentation: &lt;a class="link" href="https://qiskit.org/documentation/" target="_blank" rel="noopener"
>https://qiskit.org/documentation/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>【PQC Ilustrado】Comparación de los principales algoritmos de criptografía poscuántica</title><link>http://kenji.blog/es/p/post-quantum-cryptography-algorithms-comparison/</link><pubDate>Fri, 11 Sep 2026 07:00:00 +0900</pubDate><guid>http://kenji.blog/es/p/post-quantum-cryptography-algorithms-comparison/</guid><description>&lt;img src="http://kenji.blog/p/post-quantum-cryptography-algorithms-comparison/img/eyecatch.jpg" alt="Featured image of post 【PQC Ilustrado】Comparación de los principales algoritmos de criptografía poscuántica" />&lt;h2 id="1-introducción-la-crisis-criptográfica-provocada-por-las-computadoras-cuánticas">1. Introducción: La &amp;ldquo;crisis criptográfica&amp;rdquo; provocada por las computadoras cuánticas
&lt;/h2>&lt;p>En la sociedad de Internet moderna, la tecnología de criptografía de clave pública es una infraestructura indispensable para proteger la confidencialidad de las comunicaciones y la integridad de los datos. Los criptosistemas ampliamente utilizados en la actualidad, como RSA y la criptografía de curva elíptica (ECC), garantizan la seguridad dependiendo de barreras matemáticas como la &amp;ldquo;dificultad de factorizar números compuestos enormes&amp;rdquo; o la &amp;ldquo;dificultad del problema del logaritmo discreto sobre curvas elípticas&amp;rdquo;, respectivamente. En las computadoras clásicas (las computadoras que usamos hoy en día, incluidas las supercomputadoras), se ha demostrado que resolver estos problemas matemáticos tomaría más tiempo que la edad del universo, lo cual ha sido la base de su seguridad.&lt;/p>
&lt;p>Sin embargo, esta sólida premisa está a punto de ser anulada por los avances en la teoría y aplicación práctica de la &lt;strong>computadora cuántica&lt;/strong>. El &amp;ldquo;&lt;strong>Algoritmo de Shor&lt;/strong>&amp;rdquo;, publicado por el criptógrafo Peter Shor en 1994, demostró teóricamente que el problema de la factorización de enteros y el problema del logaritmo discreto se pueden resolver en &amp;ldquo;tiempo polinómico&amp;rdquo; si se ejecutan en una computadora cuántica de propósito general tolerante a fallas (CRQC: Cryptographically Relevant Quantum Computer) con suficiente rendimiento. Esto significa que toda la criptografía de clave pública que se usa actualmente será inutilizada.&lt;/p>
&lt;div class="mermaid">graph TD
A["Computadora cuántica a gran escala (CRQC)"] -->|Ejecución| B["Algoritmo de Shor"]
B -->|Descifrado en tiempo polinómico| C["Problema de factorización de enteros (RSA)"]
B -->|Descifrado en tiempo polinómico| D["Problema del logaritmo discreto (ECC / ECDSA)"]
C --> E["Intercepción de comunicaciones cifradas, manipulación de datos y suplantación de identidad"]
D --> E
F["Store Now, Decrypt Later (SNDL)"] --> E&lt;/div>
&lt;p>Es muy peligroso pensar que &amp;ldquo;la finalización a gran escala de las computadoras cuánticas está a décadas de distancia, así que no hay problema&amp;rdquo;. Esto se debe a que un método de ataque llamado &lt;strong>Store Now, Decrypt Later (SNDL: Almacenar ahora, descifrar después)&lt;/strong> ya es una amenaza real. Se trata de un ataque en el que naciones hostiles o grupos de hackers almacenan cantidades masivas de datos de comunicación cifrados actuales (como el tráfico TLS) en sistemas de almacenamiento y los descifran todos en el instante en que una potente computadora cuántica esté disponible en el futuro. Los secretos de estado, la información de infraestructuras y los datos médicos que deben protegerse a largo plazo ya están expuestos a esta amenaza.&lt;/p>
&lt;p>Además, existe el &lt;strong>Algoritmo de Grover&lt;/strong>, descubierto en 1996, contra la criptografía de clave simétrica (como AES) y las funciones hash (como SHA-256). Esto reduce a la raíz cuadrada la cantidad de cálculo para los ataques de fuerza bruta. Es decir, dado que el nivel de seguridad de AES-128 se reduce a la mitad (a 2 a la potencia de 64), se recomienda utilizar claves o longitudes de hash más largas en la era cuántica, como AES-256 o SHA-384.&lt;/p>
&lt;p>Para contrarrestar esta crisis criptográfica sin precedentes, nació la &lt;strong>Criptografía Poscuántica (Post-Quantum Cryptography: PQC)&lt;/strong>, basada en nuevos problemas matemáticos que son difíciles de descifrar incluso utilizando una computadora cuántica. Basándose en los resultados del proceso de estandarización de PQC liderado por el Instituto Nacional de Estándares y Tecnología de los EE. UU. (NIST), este artículo explica detalladamente los principales algoritmos de PQC, desde sus fundamentos matemáticos hasta su funcionamiento y comparación de arquitectura.&lt;/p>
&lt;hr>
&lt;h2 id="2-visión-general-e-historia-del-proyecto-de-estandarización-pqc-por-el-nist">2. Visión general e historia del proyecto de estandarización PQC por el NIST
&lt;/h2>&lt;p>La transición de las tecnologías criptográficas, incluyendo el rediseño de protocolos, actualizaciones de sistemas y reemplazo de hardware, toma de varios años a décadas. Por lo tanto, criptógrafos de todo el mundo han avanzado tempranamente en la investigación de PQC. El papel central lo ha desempeñado el NIST de EE. UU. En 2016, el NIST convocó un proceso de estandarización de PQC, recibiendo propuestas para algoritmos criptográficos completamente nuevos de la comunidad criptográfica mundial.&lt;/p>
&lt;p>Los dos principales objetivos de estandarización fueron:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Criptografía de clave pública / Mecanismo de encapsulación de claves (KEM: Key Encapsulation Mechanism)&lt;/strong>: Un mecanismo para compartir (distribuir) de forma segura claves simétricas para cifrar rutas de comunicación, como en conexiones TLS.&lt;/li>
&lt;li>&lt;strong>Firmas digitales (Digital Signatures)&lt;/strong>: Un mecanismo para probar que los datos no han sido manipulados y que no hay suplantación del remitente (autenticidad) en las actualizaciones de software y certificados digitales.&lt;/li>
&lt;/ol>
&lt;p>Después de unos 6 años de competencia extremadamente intensa de evaluación, análisis y criptoanálisis (Ronda 1 a Ronda 3), se realizó una evaluación adicional en la Ronda 4 para algunos algoritmos. Como resultado, en 2024 los siguientes algoritmos fueron publicados formalmente como Estándares Federales de Procesamiento de Información (FIPS), estableciéndose como los futuros estándares mundiales:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>FIPS 203 (ML-KEM)&lt;/strong>: KEM basado en CRYSTALS-Kyber&lt;/li>
&lt;li>&lt;strong>FIPS 204 (ML-DSA)&lt;/strong>: Firma digital basada en CRYSTALS-Dilithium&lt;/li>
&lt;li>&lt;strong>FIPS 205 (SLH-DSA)&lt;/strong>: Firma basada en hash sin estado basada en SPHINCS+&lt;/li>
&lt;li>&lt;strong>(Programado para el futuro) FN-DSA&lt;/strong>: Firma digital basada en FALCON&lt;/li>
&lt;/ul>
&lt;p>Estos algoritmos seleccionados dependen de &amp;ldquo;problemas de dificultad&amp;rdquo; matemáticos diferentes. Esto asegura la diversidad (Crypto Agility) para que, incluso si se descubre una vulnerabilidad fatal en un algoritmo en el futuro, todo el sistema no colapse. En el proceso de estandarización, la criptografía basada en retículos (Lattice-based cryptography) tomó el papel principal en términos de rendimiento, pero la criptografía basada en hash y la criptografía basada en códigos fueron adoptadas como poderosos respaldos.&lt;/p>
&lt;hr>
&lt;h2 id="3-clasificación-de-los-principales-enfoques-matemáticos-de-pqc">3. Clasificación de los principales enfoques matemáticos de PQC
&lt;/h2>&lt;p>Los algoritmos PQC se dividen principalmente en las siguientes cinco categorías según los problemas matemáticos que son la base de su seguridad. Este artículo profundizará especialmente en los tres primeros.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Criptografía basada en retículos (Lattice-based Cryptography)&lt;/strong>:
Se basa en el Problema del Vector Más Corto (SVP) y el Problema del Vector Más Cercano (CVP) en un espacio de retículo multidimensional, y el problema LWE derivado de ellos. Es el centro de la estandarización del NIST e incluye Kyber, Dilithium y FALCON. Tiene el mejor equilibrio entre velocidad de procesamiento, tamaño de clave pública y tamaño de texto cifrado, lo que lo hace adecuado para un uso general.&lt;/li>
&lt;li>&lt;strong>Criptografía basada en hash (Hash-based Cryptography)&lt;/strong>:
Su seguridad se basa únicamente en la &amp;ldquo;resistencia a colisiones&amp;rdquo; y la &amp;ldquo;unidireccionalidad&amp;rdquo; de funciones hash criptográficas (como SHA-2 o SHAKE). Solo es aplicable a firmas digitales (como SPHINCS+), pero su prueba de seguridad es la más robusta, caracterizándose por una resistencia extremadamente alta contra ataques matemáticos desconocidos.&lt;/li>
&lt;li>&lt;strong>Criptografía basada en códigos (Code-based Cryptography)&lt;/strong>:
Se basa en la teoría de códigos de corrección de errores y depende de la dificultad del Problema de Decodificación de Síndrome (Syndrome Decoding Problem). Classic McEliece, propuesto en la década de 1970, es representativo, poseyendo una historia muy larga y seguridad probada, pero el tamaño de la clave pública es extremadamente grande (en megabytes).&lt;/li>
&lt;li>&lt;strong>Criptografía de polinomios multivariables (Multivariate Polynomial Cryptography)&lt;/strong>:
Se basa en la dificultad de resolver sistemas de ecuaciones cuadráticas multivariables sobre un campo finito (problema MQ). Propuesto principalmente como firmas digitales (como Rainbow), pero durante la ronda final del NIST se descubrió un poderoso método de ataque que podía romperlo en unos pocos días en una sola PC, causando que muchos algoritmos quedaran fuera de la estandarización.&lt;/li>
&lt;li>&lt;strong>Criptografía basada en isogenias (Isogeny-based Cryptography)&lt;/strong>:
Se basa en el problema de búsqueda de caminos en grafos de isogenias de curvas elípticas. Tenía claves de tamaño muy pequeño y se esperaba que fuera el verdadero sucesor de ECC, pero su candidato final, &amp;ldquo;SIKE&amp;rdquo;, fue completamente descifrado en 2022 utilizando matemáticas clásicas (como el ataque de Castryck-Decru) en una PC ordinaria en solo unas pocas horas, un dramático final que simboliza la dificultad y el terror de diseñar PQC.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="4-el-abismo-de-la-criptografía-basada-en-retículos-problema-lwe-y-fundamentos-matemáticos-de-module-lwe">4. El abismo de la criptografía basada en retículos: Problema LWE y fundamentos matemáticos de Module-LWE
&lt;/h2>&lt;p>Actualmente vista como la más prometedora y el centro de la estandarización está la &lt;strong>criptografía basada en retículos&lt;/strong>. La base de su seguridad es el &lt;strong>problema LWE (Learning with Errors: Aprendizaje con errores)&lt;/strong>. Propuesto por Oded Regev en 2005, recibió el Premio Gödel por este logro histórico. No podemos hablar del PQC moderno sin entender el problema LWE.&lt;/p>
&lt;h3 id="41-qué-es-el-problema-lwe-learning-with-errors">4.1. ¿Qué es el problema LWE (Learning with Errors)?
&lt;/h3>&lt;p>Primero, consideremos un sistema simple de ecuaciones lineales. Supongamos que bajo un módulo $q$ (módulo $q$), hay una matriz aleatoria conocida $A$ y un vector secreto desconocido $\vec{s}$, y se da su producto $\vec{b}$.&lt;/p>
$$ \vec{b} = A\vec{s} \pmod q $$
&lt;p>En este caso, es fácil encontrar el desconocido $\vec{s}$ a partir de la información pública $A$ y $\vec{b}$. Usando el algoritmo clásico de la &amp;ldquo;eliminación de Gauss&amp;rdquo;, $\vec{s}$ puede calcularse fácilmente en tiempo polinómico.&lt;/p>
&lt;p>Sin embargo, si añadimos un &amp;ldquo;pequeño error (ruido) intencional&amp;rdquo; a esta ecuación, el nivel de dificultad del problema se dispara drásticamente. Este es el &lt;strong>problema LWE&lt;/strong>.&lt;/p>
&lt;p>Preparamos un vector secreto desconocido $\vec{s} \in \mathbb{Z}_q^n$ y una matriz elegida al azar $A \in \mathbb{Z}_q^{m \times n}$. Además, preparamos un vector de error $\vec{e} \in \mathbb{Z}_q^m$ &amp;ldquo;cuyos valores de elementos son suficientemente pequeños&amp;rdquo; elegidos de acuerdo con una distribución normal o binomial, y calculamos $\vec{b}$ de la siguiente manera:&lt;/p>
$$ \vec{b} = A\vec{s} + \vec{e} \pmod q $$
&lt;p>El &lt;strong>problema Search LWE&lt;/strong> es el problema de &amp;ldquo;encontrar la información secreta $\vec{s}$ a partir de la información pública $(A, \vec{b})$&amp;rdquo;. Debido a la existencia de este error $\vec{e}$, si se intenta usar métodos de solución algebraica como la eliminación de Gauss, el error $\vec{e}$ se amplifica exponencialmente en el proceso de sumar y restar ecuaciones, llegando a ser indistinguible de valores aleatorios y fallando por completo.&lt;/p>
&lt;p>La maravilla del problema LWE es que existe una poderosa prueba teórica (reducción) de que a menos que exista un algoritmo cuántico capaz de resolver GapSVP (Problema de decisión del vector más corto) o SIVP (Problema del vector independiente más corto), que son &amp;ldquo;problemas de complejidad en el peor de los casos (Worst-case hardness)&amp;rdquo; en retículos, el problema LWE tampoco puede resolverse en el caso promedio (Average-case). Es decir, incluso para claves criptográficas generadas al azar, se garantiza una fuerte seguridad respaldada por límites teóricos.&lt;/p>
&lt;h3 id="42-mejoras-dramáticas-en-eficiencia-mediante-ring-lwe-y-module-lwe">4.2. Mejoras dramáticas en eficiencia mediante Ring-LWE y Module-LWE
&lt;/h3>&lt;p>El problema LWE ordinario (Standard LWE) tiene una base de seguridad muy clara, pero el tamaño de la matriz $A$ se vuelve muy grande y el tamaño de la clave se vuelve de la clase de megabytes, lo que lo hace poco práctico. Por lo tanto, se propuso el enfoque de usar Anillos Polinómicos (Polynomial Rings) para tener una estructura algebraica.&lt;/p>
&lt;p>En el &lt;strong>problema Ring-LWE&lt;/strong>, en lugar de simples vectores o matrices, se utilizan elementos (polinomios) de un cierto anillo polinómico $R_q$. En los estándares del NIST, se usa generalmente el siguiente anillo polinómico ciclotómico.&lt;/p>
$$ R_q = \mathbb{Z}_q[X]/(X^n + 1) $$
&lt;p>Aquí, $n$ es una potencia de 2 (ej. 256) y $q$ es un número primo adecuado. Sobre este anillo, usando los elementos $a, s, e \in R_q$, calculamos $b = a \cdot s + e \pmod q$. Dado que un polinomio $a$ tiene $n$ coeficientes, los datos pueden comprimirse significativamente, y además, al usar la versión de campo finito de la Transformada Rápida de Fourier (FFT) llamada &lt;strong>NTT (Number Theoretic Transform: Transformada de Teoría de Números)&lt;/strong>, es posible la multiplicación ultrarrápida de polinomios con una complejidad computacional de $O(n \log n)$.&lt;/p>
&lt;p>Sin embargo, había preocupaciones con Ring-LWE de que &amp;ldquo;podrían existir vulnerabilidades desconocidas debido a la estructura algebraica especial del anillo&amp;rdquo;. Además, existía un desafío de ingeniería en el que al cambiar el nivel de seguridad (equivalente a AES-128, 192, 256, etc.), el grado del polinomio $n$ en sí tenía que cambiarse, lo que requería reescribir toda la implementación como el algoritmo NTT.&lt;/p>
&lt;p>Por lo tanto, los algoritmos estandarizados Kyber y Dilithium adoptaron el &lt;strong>problema Module-LWE (M-LWE)&lt;/strong>. Module-LWE es un compromiso situado justo entre el Standard LWE sin estructura y el Ring-LWE que tiene demasiada estructura, utilizando una matriz (módulo) de $k \times k$ cuyos componentes son elementos del anillo polinómico $R_q$.&lt;/p>
$$ \vec{b} = A\vec{s} + \vec{e} \pmod{R_q} \quad (A \in R_q^{k \times k}, \vec{s}, \vec{e} \in R_q^k) $$
&lt;p>La mayor ventaja de Module-LWE es que el nivel de seguridad puede escalarse fácilmente cambiando solo la dimensión $k$ de la matriz mientras se mantiene fijo el grado del polinomio $n$ ($n=256$ en el estándar NIST).
Por ejemplo, en el caso de Kyber, la dimensión $k$ se ajusta de la siguiente manera:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Kyber512 (Nivel 1)&lt;/strong>: $k = 2$ (equivalente a AES-128)&lt;/li>
&lt;li>&lt;strong>Kyber768 (Nivel 3)&lt;/strong>: $k = 3$ (equivalente a AES-192)&lt;/li>
&lt;li>&lt;strong>Kyber1024 (Nivel 5)&lt;/strong>: $k = 4$ (equivalente a AES-256)&lt;/li>
&lt;/ul>
&lt;p>Esto permite que el código NTT subyacente y los circuitos de hardware para las operaciones polinómicas se reutilicen al 100% en todos los niveles de seguridad, mejorando drásticamente la seguridad y eficiencia de la implementación.&lt;/p>
&lt;hr>
&lt;h2 id="5-crystals-kyber-ml-kem-el-mecanismo-de-encapsulación-de-claves-de-próxima-generación">5. CRYSTALS-Kyber (ML-KEM): El mecanismo de encapsulación de claves de próxima generación
&lt;/h2>&lt;p>Formalmente estandarizado como &lt;strong>FIPS 203 (ML-KEM)&lt;/strong>, CRYSTALS-Kyber es un mecanismo de encapsulación de claves (KEM) basado en el problema Module-LWE antes mencionado. En el futuro, será el estándar mundial de facto para compartir claves de sesión de forma segura en TLS 1.3, SSH y otros.&lt;/p>
&lt;h3 id="51-arquitectura-de-kem-key-encapsulation-mechanism">5.1. Arquitectura de KEM (Key Encapsulation Mechanism)
&lt;/h3>&lt;p>En la era PQC, el estándar será el marco de encapsulación KEM en lugar del enfoque directo como RSA donde &amp;ldquo;el cliente crea una clave simétrica, la cifra con la clave pública del servidor y la envía&amp;rdquo;.&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant Client as "Cliente (Alice)"
participant Server as "Servidor (Bob)"
Note over Client: "ML-KEM KeyGen()"
Client->>Client: "Generar clave privada (sk) y clave pública (pk)"
Client->>Server: "Enviar clave pública (pk)"
Note over Server: "ML-KEM Encaps()"
Server->>Server: "Generar una clave simétrica aleatoria (K)"
Server->>Server: "Encapsular K con pk y crear el texto cifrado (c)"
Server->>Client: "Enviar el texto cifrado (c)"
Note over Client: "ML-KEM Decaps()"
Client->>Client: "Descifrar el texto cifrado (c) usando la clave privada (sk)"
Client->>Client: "Desencapsular y extraer la clave simétrica (K)"
Note over Client, Server: "Iniciar la comunicación cifrada con AES, etc. usando la clave simétrica compartida (K)"&lt;/div>
&lt;h3 id="52-mecanismo-del-algoritmo-interno-de-kyber-y-la-transformada-de-fujisaki-okamoto">5.2. Mecanismo del algoritmo interno de Kyber y la Transformada de Fujisaki-Okamoto
&lt;/h3>&lt;p>El diseño de Kyber es muy refinado. Primero, construye un esquema de criptografía de clave pública (Kyber.CPAPKE) que solo es seguro contra ataques de texto plano elegido (CPA), y luego aplica un método criptográficamente muy poderoso llamado &lt;strong>Transformada de Fujisaki-Okamoto (Fujisaki-Okamoto Transform)&lt;/strong> para actualizarlo a un KEM completo que también es seguro contra ataques de texto cifrado adaptativamente elegido (CCA).&lt;/p>
&lt;p>El núcleo del mecanismo de cifrado y descifrado de CPAPKE es el siguiente:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Generación de claves (Key Generation)&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>A partir de un valor semilla aleatorio, se genera una matriz $A \in R_q^{k \times k}$ en el dominio NTT. Se utiliza el módulo $q = 3329$.&lt;/li>
&lt;li>A partir de una distribución binomial centrada (CBD), se muestrean un vector secreto $\vec{s}$ y un vector de error $\vec{e}$ con coeficientes pequeños.&lt;/li>
&lt;li>Se calcula $\vec{t} = A\vec{s} + \vec{e}$. La clave pública es $(A, \vec{t})$ y la clave privada es $\vec{s}$. (En la práctica, $A$ se publica como un valor semilla para ahorrar ancho de banda).&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Cifrado (Encryption)&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>Se codifica el mensaje de 32 bytes (material de la clave simétrica) $m$ que se desea compartir en un polinomio.&lt;/li>
&lt;li>Se genera un nuevo vector aleatorio $\vec{r}$ y errores pequeños $\vec{e_1}, e_2$.&lt;/li>
&lt;li>$\vec{u} = A^T\vec{r} + \vec{e_1}$&lt;/li>
&lt;li>$v = \vec{t}^T\vec{r} + e_2 + \lfloor q/2 \rceil \cdot m$&lt;/li>
&lt;li>El texto cifrado es $(\vec{u}, v)$.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Descifrado (Decryption)&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>El receptor calcula $v - \vec{s}^T\vec{u}$.&lt;/li>
&lt;li>Expandiendo esta fórmula matemáticamente, obtenemos:
$v - \vec{s}^T\vec{u} = (\vec{t}^T\vec{r} + e_2 + \lfloor q/2 \rceil \cdot m) - \vec{s}^T(A^T\vec{r} + \vec{e_1})$&lt;/li>
&lt;li>Sustituyendo $\vec{t} = A\vec{s} + \vec{e}$, el término principal $\vec{s}^TA^T\vec{r}$ se cancela.&lt;/li>
&lt;li>Lo que queda es $\lfloor q/2 \rceil \cdot m + (\vec{e}^T\vec{r} + e_2 - \vec{s}^T\vec{e_1})$.&lt;/li>
&lt;li>Dado que los términos entre paréntesis son &amp;ldquo;productos o sumas de errores pequeños&amp;rdquo;, en conjunto siguen siendo un valor lo suficientemente pequeño (ruido). Por lo tanto, al aplicar un umbral para determinar si cada coeficiente está más cerca de $0$ o $q/2$, es posible recuperar los bits (0 o 1) del mensaje original $m$ sin ningún error.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>La mayor fortaleza de Kyber es su abrumadora &lt;strong>velocidad de procesamiento&lt;/strong> y &lt;strong>tamaño de clave moderado&lt;/strong>. Para Kyber768, el tamaño de la clave pública es de 1,184 bytes y el tamaño del texto cifrado es de 1,088 bytes. Aunque es mayor en comparación con RSA-3072 (tamaño de clave de unos 384 bytes), puede caber dentro de la Unidad Máxima de Transmisión (MTU) de las comunicaciones modernas de Internet sin fragmentación de paquetes y casi no tiene un efecto adverso en la latencia de la red.&lt;/p>
&lt;hr>
&lt;h2 id="6-crystals-dilithium-ml-dsa-firma-digital-de-propósito-general-basada-en-retículos">6. CRYSTALS-Dilithium (ML-DSA): Firma digital de propósito general basada en retículos
&lt;/h2>&lt;p>En la estandarización de las firmas digitales, los algoritmos con diferentes filosofías de diseño dentro del mismo enfoque de criptografía de retículos compitieron entre sí. Entre ellos, &lt;strong>CRYSTALS-Dilithium&lt;/strong> fue seleccionado como &lt;strong>FIPS 204 (ML-DSA)&lt;/strong> como una firma digital de propósito general.&lt;/p>
&lt;h3 id="61-el-paradigma-de-fiat-shamir-con-abortos">6.1. El paradigma de Fiat-Shamir con Abortos
&lt;/h3>&lt;p>Dilithium, al igual que Kyber, es un esquema de firma digital basado en el problema Module-LWE (y el problema Module-SIS). Un paradigma extremadamente importante llamado &amp;ldquo;&lt;strong>Fiat-Shamir with Aborts (Transformada de Fiat-Shamir con abortos)&lt;/strong>&amp;rdquo; se utiliza como base para su diseño.&lt;/p>
&lt;p>La propia transformación de Fiat-Shamir es un método estándar para convertir un protocolo de prueba de conocimiento cero interactivo en una firma digital no interactiva. El probador (firmante) genera un compromiso $y$, calcula $w = Ay$ y lo pasa a través de una función hash para obtener un desafío aleatorio $c$, luego calcula la respuesta $z = y + cs$.&lt;/p>
&lt;p>Sin embargo, si esto se aplica simplemente en la criptografía de retículos, la distribución de la respuesta $z$ se distorsiona dependiendo del valor de la clave privada $s$, lo que causaba un problema fatal donde la información sobre la clave privada $s$ se filtraría gradualmente a un atacante que observara un gran número de firmas (una fuga matemática similar a un ataque de canal lateral).&lt;/p>
&lt;p>El equipo de diseño de Dilithium (Lyubashevsky et al.) introdujo una técnica llamada &amp;ldquo;&lt;strong>Muestreo de Rechazo (Rejection Sampling)&lt;/strong>&amp;rdquo;, donde si los coeficientes del resultado de la firma $z$ no caen dentro de un rango de umbral seguro predeterminado, todo el proceso de firma es descartado (Abort) y se recalcula desde el principio usando un nuevo número aleatorio $y$.&lt;/p>
&lt;p>Como resultado, la firma final $z$ que se emite se convierte en una distribución completamente uniforme independiente de la clave privada en absoluto, logrando prevenir completamente la fuga de información matemáticamente.&lt;/p>
&lt;h3 id="62-ventajas-y-facilidad-de-implementación-de-dilithium">6.2. Ventajas y facilidad de implementación de Dilithium
&lt;/h3>&lt;p>La gran ventaja en el diseño de Dilithium es que &lt;strong>no utiliza en absoluto&lt;/strong> el complejo &amp;ldquo;muestreo de la distribución gaussiana&amp;rdquo; o &amp;ldquo;cálculos de punto flotante&amp;rdquo; en el proceso de generación de firmas. Puede implementarse de forma segura y en tiempo constante (Constant-time) en una amplia gama de entornos, desde microcontroladores integrados hasta servidores en la nube, ya que solo requiere muestreo de una distribución uniforme, simples operaciones modulares de enteros, NTT y funciones hash (SHAKE). Esto lo hace muy resistente contra ataques físicos de canal lateral como los ataques de temporización.&lt;/p>
&lt;hr>
&lt;h2 id="7-falcon-fn-dsa-firmas-de-retículo-ultracompactas">7. FALCON (FN-DSA): Firmas de retículo ultracompactas
&lt;/h2>&lt;p>El NIST seleccionó &lt;strong>FALCON (Fast-Fourier Lattice-based Compact Signatures over NTRU)&lt;/strong> como otro candidato estándar de firma basada en retículos con características diferentes a Dilithium (actualmente en redacción como FN-DSA).&lt;/p>
&lt;h3 id="71-retículos-ntru-y-muestreo-gaussiano">7.1. Retículos NTRU y muestreo gaussiano
&lt;/h3>&lt;p>La principal característica de FALCON es que utiliza &lt;strong>retículos NTRU (N-th degree Truncated polynomial Ring Units)&lt;/strong>, que tienen una larga historia desde 1996, en lugar del problema LWE. Además, adopta el paradigma &amp;ldquo;&lt;strong>Hash-and-Sign (Hash y Firmar)&lt;/strong>&amp;rdquo; basado en el marco GPV (Gentry-Peikert-Vaikuntanathan).&lt;/p>
&lt;p>En Hash-and-Sign, el valor hash del mensaje se utiliza como un punto objetivo en el espacio, y el punto en el retículo más cercano a ese punto (una solución aproximada del problema del vector más cercano) se encuentra para que sirva como la firma. Esto requiere muestrear puntos según una distribución gaussiana discreta, utilizando la clave privada que es una &amp;ldquo;base corta de buena calidad&amp;rdquo;.&lt;/p>
&lt;p>FALCON aceleró dramáticamente este cálculo pesado usando una técnica llamada &amp;ldquo;&lt;strong>Ortogonalización rápida de Fourier (Fast Fourier Orthogonalization: FFO)&lt;/strong>&amp;rdquo;.&lt;/p>
&lt;h3 id="72-ventajas-y-desventajas-de-falcon">7.2. Ventajas y desventajas de FALCON
&lt;/h3>&lt;p>La abrumadora ventaja de FALCON es que &lt;strong>el tamaño de la firma y el tamaño de la clave pública son extremadamente pequeños (compactos)&lt;/strong>. Mientras que el tamaño de la firma de Dilithium3 es de aproximadamente 3,309 bytes, el tamaño de la firma de FALCON-512 es de solo unos 666 bytes. La clave pública también es muy pequeña, 897 bytes, lo que lo convierte en un salvador en entornos donde el ancho de banda de comunicación está extremadamente restringido, dispositivos IoT o protocolos de red específicos.&lt;/p>
&lt;p>Sin embargo, hay una gran desventaja. Dado que el muestreo gaussiano discreto que involucra operaciones complejas de &lt;strong>punto flotante (64-bit IEEE 754)&lt;/strong> es obligatorio durante la generación de la firma, es extremadamente difícil lograr una implementación en tiempo constante (Constant-time implementation) para evitar fugas de temporización, y el código también se vuelve enorme. Por esta razón, FALCON se posiciona como un algoritmo especializado poderoso para propósitos específicos, en contraste con el uso general (Dilithium).&lt;/p>
&lt;div class="mermaid">graph LR
A["Requisitos de la firma digital"] --> B{"¿Cuál es la restricción principal?"}
B -->|"Simplicidad de implementación, generalidad, facilidad de tiempo constante"| C["Dilithium (ML-DSA)"]
B -->|"Minimización del ancho de banda y tamaño compacto de los datos"| D["FALCON (FN-DSA)"]
C --> E["Certificados TLS de uso general, firmas digitales de software"]
D --> F["Protocolos con estrictos límites de tamaño de paquetes, entornos especiales"]&lt;/div>
&lt;hr>
&lt;h2 id="8-sphincs-slh-dsa-firma-basada-en-hash-con-la-máxima-seguridad">8. SPHINCS+ (SLH-DSA): Firma basada en hash con la máxima seguridad
&lt;/h2>&lt;p>En preparación para el peor de los casos en el que la seguridad de la criptografía de retículos se vea comprometida por un avance matemático de genios en el futuro, el NIST formuló &lt;strong>FIPS 205 (SLH-DSA)&lt;/strong>, es decir, &lt;strong>SPHINCS+&lt;/strong>, como un estándar con un enfoque completamente diferente a la criptografía de retículos.&lt;/p>
&lt;p>SPHINCS+ está clasificado como una &lt;strong>firma basada en hash&lt;/strong>. La base de su seguridad se basa únicamente en un punto: &amp;ldquo;que la función hash criptográfica utilizada (como SHA-2 o SHAKE256) posea resistencia a colisiones y unidireccionalidad&amp;rdquo;. Como no depende de problemas matemáticos con una estructura algebraica específica como LWE o la factorización prima, cuenta con una seguridad extremadamente fuerte (la seguridad más conservadora), en el sentido de que no importa cuán poderosos sean los algoritmos cuánticos que aparezcan en el futuro, puede contrarrestarlos simplemente extendiendo la longitud de salida de la función hash.&lt;/p>
&lt;h3 id="81-arquitectura-sin-estado-mediante-wots-y-fors">8.1. Arquitectura sin estado mediante WOTS+ y FORS
&lt;/h3>&lt;p>La historia de las firmas basadas en hash es antigua, remontándose a la firma de Lamport y la firma de un solo uso de Winternitz (WOTS) en la década de 1970. Estas eran claves desechables que &amp;ldquo;solo podían firmar de forma segura una vez&amp;rdquo;. Para hacerlas reutilizables múltiples veces, se desarrollaron algoritmos como XMSS (eXtended Merkle Signature Scheme) y LMS que combinan árboles de Merkle para gestionar innumerables claves de un solo uso con un único hash raíz.&lt;/p>
&lt;p>Sin embargo, XMSS y LMS tenían el defecto grave de ser &amp;ldquo;&lt;strong>con estado (Stateful)&lt;/strong>&amp;rdquo;. Cada vez que se realizaba una firma, el estado del índice de &amp;ldquo;qué clave de un solo uso se usó&amp;rdquo; tenía que registrarse estrictamente en la memoria no volátil, y si el estado se revertía debido a la restauración de una instantánea de máquina virtual, etc., y se usaba la misma clave de un solo uso dos veces, la clave privada se filtraría inmediatamente y el sistema colapsaría.&lt;/p>
&lt;p>SPHINCS+ es una firma basada en hash &amp;ldquo;&lt;strong>sin estado (Stateless)&lt;/strong>&amp;rdquo; que resolvió esta molestia en la gestión de estados.
Su tecnología central es una combinación de lo siguiente:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>WOTS+ (Winternitz One-Time Signature Plus)&lt;/strong>: Una firma básica de un solo uso.&lt;/li>
&lt;li>&lt;strong>FORS (Forest of Random Subsets)&lt;/strong>: Una tecnología de firma de pocas veces (Few-Time Signature). Mantiene la seguridad incluso si la misma clave se reutiliza unas cuantas veces.&lt;/li>
&lt;li>&lt;strong>Hyper-Tree (Estructura de árbol gigante)&lt;/strong>: Una estructura gigantesca que superpone árboles de Merkle en múltiples capas.&lt;/li>
&lt;/ol>
&lt;p>En SPHINCS+, al realizar una firma, en lugar de mantener el estado, selecciona aleatoriamente una usando números pseudoaleatorios de un gran número de claves FORS en la base del Hyper-Tree para firmar. Como el número de hojas en el árbol es astronómicamente alto, la probabilidad de elegir la misma clave dos veces por accidente (colisión) es insignificantemente pequeña, lo que resulta en la realización de la falta de estado.&lt;/p>
&lt;p>La única y mayor debilidad de SPHINCS+ es que su &lt;strong>tamaño de firma es extremadamente grande&lt;/strong>. Dependiendo de los parámetros, el tamaño de la firma puede alcanzar de 17 kilobytes a 49 kilobytes, y la velocidad de generación de la firma es abrumadoramente más lenta en comparación con la criptografía de retículos. Por lo tanto, se prevé que se utilice para propósitos que no requieren firmas frecuentes y exigen una seguridad absoluta a largo plazo, como firmas de actualización de software y certificados de autoridades de certificación raíz (CA), en lugar del uso diario en la navegación web.&lt;/p>
&lt;hr>
&lt;h2 id="9-criptografía-basada-en-códigos-el-viejo-gigante-classic-mceliece">9. Criptografía basada en códigos: El viejo gigante Classic McEliece
&lt;/h2>&lt;p>En el proceso de estandarización del NIST, un enfoque importante que aún se está evaluando como candidato final de la Ronda 4 es &lt;strong>Classic McEliece&lt;/strong>, de la &lt;strong>criptografía basada en códigos&lt;/strong>.&lt;/p>
&lt;p>Propuesto por Robert McEliece en 1978, este algoritmo es uno de los más antiguos en la historia de la criptografía de clave pública junto a RSA. Utiliza un código de geometría algebraica llamado &amp;ldquo;Código Goppa&amp;rdquo;, cifra agregando intencionalmente un error (vector de ruido) al mensaje, y se basa en el &amp;ldquo;&lt;strong>Problema de Decodificación de Síndrome (Syndrome Decoding Problem)&lt;/strong>&amp;rdquo; donde solo aquellos con una matriz de comprobación de paridad de código Goppa como clave privada pueden eliminar el error usando una fuerte capacidad de corrección de errores y descifrar el mensaje original.&lt;/p>
$$ \vec{c} = \vec{m} G + \vec{e} $$
&lt;p>
($G$ es la matriz generadora codificada que es la clave pública, $\vec{e}$ es un vector de error de peso $t$)&lt;/p>
&lt;p>Lo asombroso de Classic McEliece es &lt;strong>su abrumador historial, en el que no se ha descubierto ni una sola vulnerabilidad intrínseca, a pesar de que han pasado más de 40 años desde su propuesta y de haber estado expuesto a una intensa investigación criptoanalítica por parte de criptógrafos de todo el mundo&lt;/strong>. Tiene la &amp;ldquo;fuerte seguridad probada por el tiempo&amp;rdquo; más alta entre los PQC.&lt;/p>
&lt;p>Además, tiene la ventaja de que el tamaño del texto cifrado es muy pequeño (solo unos 100-200 bytes). Sin embargo, tiene el defecto fatal de que &lt;strong>el tamaño de la clave pública se mide en megabytes (MB)&lt;/strong>. Incluso en el nivel de seguridad más bajo (equivalente a AES-128), la clave pública es de unos 250 KB y supera 1 MB en niveles más altos.&lt;/p>
&lt;p>Debido a esto, no es aplicable en absoluto para usos donde la clave pública se transmite a través de la red en cada comunicación, como en un handshake TLS. Sin embargo, en casos de uso especiales donde las claves públicas pueden distribuirse previamente en el sistema, como intercambios de claves precompartidas de VPN, codificación rígida de claves públicas en firmware o comunicaciones por satélite, sigue siendo evaluado como una opción extremadamente prometedora debido a su robusta seguridad.&lt;/p>
&lt;hr>
&lt;h2 id="10-comparación-de-rendimiento-y-compensaciones-de-cada-algoritmo-pqc">10. Comparación de rendimiento y compensaciones de cada algoritmo PQC
&lt;/h2>&lt;p>Las características de rendimiento de los principales algoritmos discutidos hasta ahora, en niveles de seguridad típicos (equivalentes a los niveles 2-3 de NIST, niveles AES-128-192), se resumen en la siguiente tabla.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align:left">Algoritmo (Nombre estándar)&lt;/th>
&lt;th style="text-align:left">Categoría&lt;/th>
&lt;th style="text-align:left">Base matemática&lt;/th>
&lt;th style="text-align:left">Tamaño de clave pública&lt;/th>
&lt;th style="text-align:left">Tamaño de clave privada&lt;/th>
&lt;th style="text-align:left">Tamaño de texto cifrado/firma&lt;/th>
&lt;th style="text-align:left">Tendencia de velocidad&lt;/th>
&lt;th style="text-align:left">Características principales y usos&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Kyber768&lt;/strong>&lt;br>(ML-KEM)&lt;/td>
&lt;td style="text-align:left">KEM&lt;/td>
&lt;td style="text-align:left">Module-LWE&lt;/td>
&lt;td style="text-align:left">1,184 Bytes&lt;/td>
&lt;td style="text-align:left">2,400 Bytes&lt;/td>
&lt;td style="text-align:left">1,088 Bytes&lt;/td>
&lt;td style="text-align:left">Muy rápido&lt;/td>
&lt;td style="text-align:left">El mejor equilibrio de tamaño de clave y velocidad. Estándar KEM de uso general como TLS 1.3.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Dilithium3&lt;/strong>&lt;br>(ML-DSA)&lt;/td>
&lt;td style="text-align:left">Firma&lt;/td>
&lt;td style="text-align:left">Module-LWE&lt;/td>
&lt;td style="text-align:left">1,952 Bytes&lt;/td>
&lt;td style="text-align:left">4,032 Bytes&lt;/td>
&lt;td style="text-align:left">3,309 Bytes&lt;/td>
&lt;td style="text-align:left">Rápido para generar y verificar&lt;/td>
&lt;td style="text-align:left">Implementación simple. Estándar de firma digital de propósito general.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>FALCON-512&lt;/strong>&lt;br>(FN-DSA)&lt;/td>
&lt;td style="text-align:left">Firma&lt;/td>
&lt;td style="text-align:left">Retículo NTRU&lt;/td>
&lt;td style="text-align:left">897 Bytes&lt;/td>
&lt;td style="text-align:left">1,281 Bytes&lt;/td>
&lt;td style="text-align:left">666 Bytes&lt;/td>
&lt;td style="text-align:left">Generación algo lenta, verificación ultrarrápida&lt;/td>
&lt;td style="text-align:left">Tamaño de firma muy pequeño. Pero requiere operaciones de punto flotante. Para sistemas embebidos/IoT.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>SPHINCS+&lt;/strong>&lt;br>(SLH-DSA)&lt;/td>
&lt;td style="text-align:left">Firma&lt;/td>
&lt;td style="text-align:left">Función hash&lt;/td>
&lt;td style="text-align:left">32 Bytes&lt;/td>
&lt;td style="text-align:left">64 Bytes&lt;/td>
&lt;td style="text-align:left">Aprox. 17,000 Bytes&lt;/td>
&lt;td style="text-align:left">Generación muy lenta&lt;/td>
&lt;td style="text-align:left">Riesgo casi nulo de fallo matemático. Usos de alta seguridad como certificados raíz.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Classic McEliece&lt;/strong>&lt;/td>
&lt;td style="text-align:left">KEM&lt;/td>
&lt;td style="text-align:left">Código Goppa&lt;/td>
&lt;td style="text-align:left">&lt;strong>Aprox. 1.04 MB&lt;/strong>&lt;/td>
&lt;td style="text-align:left">13,568 Bytes&lt;/td>
&lt;td style="text-align:left">&lt;strong>188 Bytes&lt;/strong>&lt;/td>
&lt;td style="text-align:left">Encapsulación rápida&lt;/td>
&lt;td style="text-align:left">40 años de historial de seguridad. Clave pública enorme. Para entornos donde se puede codificar de forma rígida (hardcode).&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="entendiendo-las-compensaciones">Entendiendo las compensaciones
&lt;/h3>&lt;p>En el mundo de PQC, no existe un algoritmo único y mágico que sea &amp;ldquo;pequeño en tamaño, rápido en velocidad y perfecto en garantía matemática&amp;rdquo;.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Estándar de Internet (Kyber / Dilithium)&lt;/strong>: Tienen el mejor equilibrio de rendimiento y son los más adecuados para el reemplazo directo (drop-in replacement) desde los actuales RSA/ECC.&lt;/li>
&lt;li>&lt;strong>Conservadurismo definitivo (SPHINCS+)&lt;/strong>: Se elige cuando se desea un seguro absoluto contra un avance matemático en el futuro, incluso a expensas del tamaño de los datos y la velocidad de procesamiento.&lt;/li>
&lt;li>&lt;strong>Para entornos especiales (FALCON / Classic McEliece)&lt;/strong>: Armas especializadas elegidas según las limitaciones del entorno, como cuando el ancho de banda de comunicación es extremadamente estrecho o cuando es posible la distribución previa.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="11-desafíos-para-el-uso-práctico-y-la-solución-realista-de-la-criptografía-híbrida">11. Desafíos para el uso práctico y la solución realista de la &amp;ldquo;Criptografía híbrida&amp;rdquo;
&lt;/h2>&lt;p>Con la finalización de la estandarización por parte del NIST y la emisión formal de las especificaciones FIPS, la migración a PQC de la infraestructura de TI global (&lt;strong>Migración PQC&lt;/strong>) ha comenzado en serio. El navegador Chrome de Google, iMessage de Apple (protocolo PQ3) y los proveedores de red como Cloudflare ya han implementado el soporte PQC en sus protocolos y han iniciado su funcionamiento real.&lt;/p>
&lt;p>Sin embargo, cambiar por completo y de forma repentina a nuevos algoritmos criptográficos conlleva un riesgo muy alto. Si un matemático genial descubre un método de ataque fatal contra la criptografía de retículos como Kyber (un defecto matemático resoluble incluso por computadoras clásicas) en unos años, todo el sistema que dependa de él quedará expuesto instantáneamente.&lt;/p>
&lt;p>Un enfoque realista y recomendado para mitigar este riesgo de incertidumbre es la &amp;ldquo;&lt;strong>Criptografía híbrida (Hybrid Cryptography)&lt;/strong>&amp;rdquo;.&lt;/p>
&lt;p>En la criptografía híbrida, el intercambio de claves se realiza utilizando tanto la criptografía clásica actual con un largo historial (ej., criptografía de curva elíptica como X25519) como la nueva PQC (ej., Kyber768) simultáneamente. Se genera un componente de clave simétrica de forma independiente con cada algoritmo, y finalmente se combinan los dos componentes utilizando una función de derivación de claves (KDF) segura para generar el secreto maestro final.&lt;/p>
&lt;div class="mermaid">graph TD
A["Cliente"] -->|① Enviar clave pública de X25519 + clave pública de Kyber| B["Servidor"]
B -->|② Responder con clave compartida de X25519 + texto cifrado encapsulado de Kyber| A
A --> C{"Derivación del secreto maestro (KDF)"}
B --> C
C -->|Entrada: (Clave simétrica de X25519) || (Clave simétrica de Kyber)| D["Clave de comunicación segura (AES-256 / ChaCha20)"]
D -->|"Resistente tanto a amenazas cuánticas como a vulnerabilidades clásicas"| E["Comunicación segura cifrada híbrida (TLS 1.3)"]&lt;/div>
&lt;p>Con esto, se logra una robusta seguridad en dos niveles: &amp;ldquo;Incluso si las computadoras cuánticas se vuelven realidad y ECC es descifrado, Kyber protege la comunicación&amp;rdquo; y, por el contrario, &amp;ldquo;Incluso si se encuentra un defecto matemático desconocido en Kyber, ECC protege la comunicación&amp;rdquo;. Un ejemplo típico es el borrador &lt;strong>X25519MLKEM768 (anteriormente X25519Kyber768)&lt;/strong> que está siendo estandarizado por el IETF, y la comunicación entre los navegadores web actuales y los servidores de última generación se realiza utilizando precisamente este método híbrido.&lt;/p>
&lt;p>Además, el concepto de &lt;strong>Crypto Agility (Agilidad criptográfica)&lt;/strong>, que consiste en construir en el diseño del sistema &amp;ldquo;una arquitectura que no dependa excesivamente de un algoritmo criptográfico específico y pueda cambiar rápidamente a otro algoritmo (por ejemplo, de Kyber a McEliece, de Dilithium a SPHINCS+) en caso de que el algoritmo falle&amp;rdquo;, se convierte en un requisito indispensable para el desarrollo de sistemas en el futuro.&lt;/p>
&lt;hr>
&lt;h2 id="12-conclusión-un-nuevo-horizonte-para-la-tecnología-criptográfica">12. Conclusión: Un nuevo horizonte para la tecnología criptográfica
&lt;/h2>&lt;p>Irónicamente, la computadora cuántica, la tecnología soñada de la humanidad, se ha convertido en la mayor amenaza para romper las barreras matemáticas del &amp;ldquo;problema de la factorización de enteros&amp;rdquo; y el &amp;ldquo;problema del logaritmo discreto&amp;rdquo; en los que hemos depositado nuestra confianza durante muchos años. Sin embargo, los criptógrafos de todo el mundo no han cedido ante esto, sino que han explorado dominios matemáticos multidimensionales más complejos y profundos como la teoría de retículos, árboles de funciones hash y códigos de corrección de errores, construyendo un nuevo muro defensivo llamado Criptografía Poscuántica (PQC).&lt;/p>
&lt;p>La finalización de la estandarización de FIPS 203 (ML-KEM), FIPS 204 (ML-DSA) y FIPS 205 (SLH-DSA) por parte del NIST no es la meta. Es solo el primer paso de un viaje épico llamado migración PQC que durará décadas. Para los ingenieros de software y arquitectos de sistemas, cómo adaptar de manera óptima &amp;ldquo;el aumento del tamaño de la clave&amp;rdquo; y el &amp;ldquo;cambio en el costo computacional&amp;rdquo; introducidos por estos nuevos algoritmos a los protocolos de red y sistemas será el gran desafío técnico del futuro.&lt;/p>
&lt;p>La batalla entre las computadoras cuánticas y la criptografía es el campo emocionante donde la exploración matemática de la humanidad y la evolución de la tecnología se cruzan más intensamente. A través de este artículo, esperamos que haya comprendido profundamente la hermosa teoría matemática detrás del PQC y los sorprendentes mecanismos de cada algoritmo que darán forma al futuro de la ciberseguridad.&lt;/p>
&lt;hr>
&lt;p>&lt;em>Referencias:&lt;/em>&lt;/p>
&lt;ul>
&lt;li>&lt;em>NIST Post-Quantum Cryptography Standardization Program&lt;/em>&lt;/li>
&lt;li>&lt;em>FIPS 203: Module-Lattice-Based Key-Encapsulation Mechanism Standard&lt;/em>&lt;/li>
&lt;li>&lt;em>FIPS 204: Module-Lattice-Based Digital Signature Standard&lt;/em>&lt;/li>
&lt;li>&lt;em>FIPS 205: Stateless Hash-Based Digital Signature Standard&lt;/em>&lt;/li>
&lt;/ul></description></item><item><title>El día en que los ordenadores cuánticos se hagan realidad: El estado actual en 2026</title><link>http://kenji.blog/es/p/quantum-computing-2026-current-status/</link><pubDate>Fri, 11 Sep 2026 06:00:00 +0900</pubDate><guid>http://kenji.blog/es/p/quantum-computing-2026-current-status/</guid><description>&lt;img src="http://kenji.blog/p/quantum-computing-2026-current-status/img/eyecatch.jpg" alt="Featured image of post El día en que los ordenadores cuánticos se hagan realidad: El estado actual en 2026" />&lt;h2 id="1-introducción-en-2026-hasta-dónde-han-llegado-los-ordenadores-cuánticos">1. Introducción: En 2026, ¿hasta dónde han llegado los ordenadores cuánticos?
&lt;/h2>&lt;p>En la actualidad, en 2026, la computación cuántica ha experimentado un cambio decisivo de ser un antiguo &amp;ldquo;sueño teórico&amp;rdquo; a una &amp;ldquo;realidad de ingeniería&amp;rdquo;. A medida que los límites de los dispositivos &lt;strong>NISQ (Noisy Intermediate-Scale Quantum)&lt;/strong>, que eran la corriente principal hasta hace unos años, se han vuelto evidentes, las instituciones de investigación y los gigantes tecnológicos de todo el mundo han cambiado de rumbo hacia la realización de la &amp;ldquo;Computación Cuántica Tolerante a Fallos (FTQC: Fault-Tolerant Quantum Computing)&amp;rdquo;.&lt;/p>
&lt;p>En este artículo, profundizaremos en el estado actual de los ordenadores cuánticos, intercalando los últimos avances de 2026. En particular, explicaremos en detalle la corrección de errores cuánticos (código de superficie), la diferencia entre cúbits físicos y lógicos, el progreso de la computación cuántica topológica y la vanguardia de los métodos superconductores y de trampa de iones.&lt;/p>
&lt;hr>
&lt;h2 id="2-conceptos-básicos-del-estado-cuántico-y-la-fidelidad">2. Conceptos básicos del estado cuántico y la fidelidad
&lt;/h2>&lt;p>El cúbit (Qubit), que es la unidad básica de un ordenador cuántico, a diferencia de un bit clásico (0 o 1), puede tomar un estado de superposición (Superposition) de 0 y 1. El estado de un solo cúbit se representa como un vector en el espacio de Hilbert de la siguiente manera:&lt;/p>
$$
|\psi\rangle = \alpha|0\rangle + \beta|1\rangle
$$
&lt;p>Aquí, $\alpha$ y $\beta$ son amplitudes de probabilidad complejas y satisfacen la siguiente condición de normalización:&lt;/p>
$$
|\alpha|^2 + |\beta|^2 = 1
$$
&lt;p>Una métrica extremadamente importante para medir el rendimiento de la computación cuántica es la &lt;strong>fidelidad (Fidelity)&lt;/strong>. La fidelidad $F$ entre el estado cuántico ideal $|\psi\rangle$ y la matriz de densidad real $\rho$ que se ha degradado a un estado mixto debido al ruido se define de la siguiente manera:&lt;/p>
$$
F(\rho, |\psi\rangle) = \langle \psi | \rho | \psi \rangle
$$
&lt;p>En la actualidad, en 2026, la fidelidad de las puertas de 2 cúbits (por ejemplo, las puertas CNOT o CZ) ha superado constantemente la barrera del &lt;strong>99,99%&lt;/strong> (los llamados &amp;ldquo;cuatro nueves&amp;rdquo;) utilizando el método superconductor. Esta es una cifra que supera con creces el umbral de corrección de errores mediante el código de superficie (aproximadamente el 99%), siendo uno de los mayores avances hacia su aplicación práctica.&lt;/p>
&lt;hr>
&lt;h2 id="3-los-límites-de-la-era-nisq-y-el-cambio-de-paradigma-hacia-ftqc">3. Los límites de la era NISQ y el cambio de paradigma hacia FTQC
&lt;/h2>&lt;p>Desde finales de la década de 2010 hasta principios de la de 2020 fue la era de NISQ (Noisy Intermediate-Scale Quantum), dispositivos con decenas a cientos de cúbits que no tenían corrección de errores. Sin embargo, NISQ tenía límites claros.&lt;/p>
&lt;p>A medida que aumenta la profundidad (Depth) del circuito, los errores se acumulan exponencialmente, lo que hace imposible obtener resultados de cálculo significativos. La probabilidad general de éxito $P_{success}$ a una profundidad de circuito $D$ decae en relación con la fidelidad $f$ de una sola puerta y el número de puertas $N$ de la siguiente manera:&lt;/p>
$$
P_{success} \approx f^N
$$
&lt;p>Si se aplican 1000 puertas con $f = 0.99$, resultará en $0.99^{1000} \approx 4.3 \times 10^{-5}$, y el resultado quedará casi sepultado en el ruido aleatorio. Por esta razón, en 2026, los recursos se concentran en la generación de &lt;strong>cúbits lógicos (Logical Qubit)&lt;/strong> en lugar de escalar directamente los algoritmos NISQ (como VQE y QAOA).&lt;/p>
&lt;hr>
&lt;h2 id="4-corrección-de-errores-cuánticos-y-cúbits-lógicos-la-vanguardia-del-código-de-superficie">4. Corrección de errores cuánticos y cúbits lógicos: La vanguardia del código de superficie
&lt;/h2>&lt;p>La corrección de errores cuánticos (QEC: Quantum Error Correction) es una tecnología que codifica múltiples &amp;ldquo;cúbits físicos&amp;rdquo; para crear un único &amp;ldquo;cúbit lógico&amp;rdquo; y detecta y corrige errores. Actualmente, el más prometedor es el &lt;strong>código de superficie (Surface Code)&lt;/strong>.&lt;/p>
&lt;h3 id="41-estructura-del-código-de-superficie-surface-code">4.1 Estructura del código de superficie (Surface Code)
&lt;/h3>&lt;p>En el código de superficie, los cúbits se organizan en una cuadrícula bidimensional. Los cúbits de datos (que contienen la información real) y los cúbits de medida (para medir el síndrome) están dispuestos en un patrón de tablero de ajedrez.&lt;/p>
&lt;div class="mermaid">graph TD
A["Cúbit de datos (D1)"] --- B["Cúbit de medida (M1)"]
B --- C["Cúbit de datos (D2)"]
C --- D["Cúbit de medida (M2)"]
D --- E["Cúbit de datos (D3)"]
B --- F["Cúbit de datos (D4)"]
D --- G["Cúbit de datos (D5)"]
style A fill:#e1f5fe,stroke:#039be5
style C fill:#e1f5fe,stroke:#039be5
style E fill:#e1f5fe,stroke:#039be5
style F fill:#e1f5fe,stroke:#039be5
style G fill:#e1f5fe,stroke:#039be5
style B fill:#fff3e0,stroke:#fb8c00
style D fill:#fff3e0,stroke:#fb8c00&lt;/div>
&lt;p>Utilizando los operadores estabilizadores $S_x$ y $S_z$, se monitorizan constantemente los cambios de bit (errores X) y los cambios de fase (errores Z).&lt;/p>
$$
S_x = \prod_{i \in \text{star}} X_i, \quad S_z = \prod_{j \in \text{plaquette}} Z_j
$$
&lt;p>Un avance significativo en 2026 es que se ha superado por completo el &amp;ldquo;punto de equilibrio (Break-even point)&amp;rdquo;. Es decir, el ruido eliminado por la corrección de errores es ahora mayor que el ruido causado por los circuitos adicionales para realizar la corrección de errores, y la vida útil del cúbit lógico supera a la del cúbit físico en varios órdenes de magnitud.&lt;/p>
&lt;h3 id="42-el-ciclo-de-corrección-de-errores-cuánticos">4.2 El ciclo de corrección de errores cuánticos
&lt;/h3>&lt;p>La corrección de errores funciona como un bucle de retroalimentación continuo.&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant D as "Cúbits de datos"
participant M as "Cúbits de medida/ancila"
participant C as "Controlador clásico"
loop "Ciclo de extracción de síndrome (aprox. 1 microseg)"
D->>M: "Entrelazar (CNOT/CZ)"
M->>C: "Medir estado (Síndrome)"
C->>C: "Decodificar síndrome (ej. Minimum Weight Perfect Matching)"
C-->>D: "Aplicar corrección de Pauli (si es necesario)"
end&lt;/div>
&lt;p>Hoy en día, se ha establecido la tecnología para ejecutar este proceso de decodificación clásica (análisis del síndrome) en nanosegundos utilizando FPGAs y ASICs dedicados, y la corrección de errores en tiempo real ha entrado en la fase práctica.&lt;/p>
&lt;hr>
&lt;h2 id="5-evolución-de-la-arquitectura-de-hardware-edición-2026">5. Evolución de la arquitectura de hardware (Edición 2026)
&lt;/h2>&lt;p>El hardware cuántico en 2026 está evolucionando principalmente a lo largo de tres ejes: &amp;ldquo;método superconductor&amp;rdquo;, &amp;ldquo;método de trampa de iones&amp;rdquo; y &amp;ldquo;método topológico&amp;rdquo;.&lt;/p>
&lt;h3 id="51-integración-de-cúbits-superconductores">5.1 Integración de cúbits superconductores
&lt;/h3>&lt;p>El método superconductor es un campo liderado por IBM y Google, donde predominan los cúbits transmon (Transmon) utilizando uniones Josephson. En 2026, se han hecho realidad megachips que integran entre miles y decenas de miles de cúbits físicos en un solo chip.&lt;/p>
&lt;p>Lo que es especialmente destacable es el establecimiento de &lt;strong>comunicaciones cuánticas entre módulos (Quantum Interconnects)&lt;/strong>. La teleportación cuántica entre chips utilizando fotones de microondas se ha implementado a nivel comercial, permitiendo eludir las restricciones de tamaño de un solo refrigerador de dilución.&lt;/p>
&lt;h3 id="52-escalado-bidimensional-e-interconexiones-ópticas-de-trampas-de-iones">5.2 Escalado bidimensional e interconexiones ópticas de trampas de iones
&lt;/h3>&lt;p>El método de trampa de iones (liderado por empresas como Quantinuum e IonQ) utiliza los estados de energía internos de los iones suspendidos en el vacío como cúbits. En comparación con el método superconductor, tiene la ventaja de tiempos de coherencia T1/T2 extremadamente largos y permite conectividad de todos con todos (All-to-All Connectivity).&lt;/p>
&lt;p>Los avances de 2026 son la bidimensionalización de la arquitectura QCCD (Quantum Charge Coupled Device) y la generación de entrelazamiento de alta velocidad entre múltiples trampas mediante interconexiones fotónicas. Como resultado, la &amp;ldquo;baja velocidad de las puertas&amp;rdquo; y la &amp;ldquo;escalabilidad&amp;rdquo;, que eran las debilidades de la trampa de iones, se han mejorado drásticamente.&lt;/p>
&lt;h3 id="53-computación-cuántica-topológica-control-de-anyones">5.3 Computación cuántica topológica: Control de anyones
&lt;/h3>&lt;p>La &lt;strong>computación cuántica topológica&lt;/strong>, considerada durante mucho tiempo una existencia teórica, finalmente ha entrado en la fase de demostración experimental en 2026. Este método, promovido por empresas como Microsoft, utiliza anyones no abelianos (Non-Abelian Anyons) llamados &amp;ldquo;modos cero de Majorana (Majorana Zero Modes)&amp;rdquo;.&lt;/p>
&lt;p>Las puertas cuánticas se ejecutan mediante una operación llamada &amp;ldquo;trenzado (Braiding)&amp;rdquo;, que intercambia las posiciones de las partículas de los anyones.&lt;/p>
$$
|\psi_{final}\rangle = B_{ij} |\psi_{initial}\rangle
$$
&lt;p>Donde $B_{ij}$ es el operador de trenzado. El método topológico es inherentemente resistente al ruido ambiental (tolerancia a errores a nivel de hardware) porque la preservación de la información no depende de los estados locales de las partículas, sino de la topología global de los &amp;ldquo;nudos&amp;rdquo;. En 2026, se confirmó la generación de un cúbit lógico topológico de alta fidelidad por primera vez en el mundo, atrayendo la atención como un poderoso atajo hacia FTQC.&lt;/p>
&lt;hr>
&lt;h2 id="6-hoja-de-ruta-y-perspectivas-para-la-aplicación-práctica">6. Hoja de ruta y perspectivas para la aplicación práctica
&lt;/h2>&lt;p>Para que los ordenadores cuánticos demuestren verdaderamente la &lt;strong>supremacía cuántica (Quantum Advantage)&lt;/strong> superando a los ordenadores clásicos (superordenadores) en &amp;ldquo;cálculos químicos&amp;rdquo;, &amp;ldquo;ciencia de materiales&amp;rdquo;, &amp;ldquo;modelado financiero&amp;rdquo;, etc., se necesitan miles de cúbits lógicos.&lt;/p>
&lt;div class="mermaid">gantt
title "Hoja de ruta de la computación cuántica (Revisada en 2026)"
dateFormat YYYY
axisFormat %Y
section "Era NISQ"
"Cúbits ruidosos (&lt;1000)" :done, 2018, 2024
section "FTQC temprano"
"Demostración del punto de equilibrio" :done, 2024, 2026
"Cientos de cúbits lógicos" :active, 2026, 2028
section "FTQC a gran escala"
"1000+ Cúbits lógicos (Aplicaciones comerciales)" : 2028, 2030
"Ordenador cuántico tolerante a fallos universal" : 2030, 2035&lt;/div>
&lt;h3 id="61-retos-actuales-y-el-futuro">6.1 Retos actuales y el futuro
&lt;/h3>&lt;p>El mayor reto en 2026 es la capacidad de enfriamiento del enorme criostato (refrigerador de dilución) para mantener temperaturas ultrabajas, y el cableado (cuello de botella de E/S) que conecta el equipo de control a temperatura ambiente con el chip cuántico a temperatura ultrabaja. En respuesta a esto, el desarrollo de chips controladores CMOS (Cryo-CMOS) que funcionan en un entorno de temperatura ultrabaja avanza a un ritmo rápido.&lt;/p>
&lt;h3 id="conclusión">Conclusión
&lt;/h3>&lt;p>2026 será registrado en la historia de la computación cuántica como &amp;ldquo;el año inaugural del escalado de los cúbits lógicos&amp;rdquo;. Con la demostración de algoritmos de corrección de errores, la modularización del hardware y el rápido avance de los enfoques topológicos, &amp;ldquo;el día en que se vuelvan prácticos&amp;rdquo; ya no es un futuro lejano, sino que puede ser visto como un hito concreto dentro de los próximos años. Para los desarrolladores de algoritmos cuánticos y las empresas, ahora es el momento de invertir seriamente en la resolución de problemas nativos cuánticos.&lt;/p>
&lt;hr>
&lt;p>&lt;em>Este artículo se basa en los últimos trabajos de investigación en computación cuántica y tendencias de la industria en 2026.&lt;/em>&lt;/p></description></item></channel></rss>