<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Prime Numbers on kenji.blog</title><link>http://kenji.blog/es/tags/prime-numbers/</link><description>Recent content in Prime Numbers on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>es</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 16:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/es/tags/prime-numbers/index.xml" rel="self" type="application/rss+xml"/><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></channel></rss>