<?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/pt/tags/prime-numbers/</link><description>Recent content in Prime Numbers on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>pt</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 16:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/pt/tags/prime-numbers/index.xml" rel="self" type="application/rss+xml"/><item><title>A Hipótese de Riemann e a Distribuição dos Números Primos: A Profunda Relação com a Criptografia Moderna</title><link>http://kenji.blog/pt/p/riemann-hypothesis-prime-distribution-cryptography/</link><pubDate>Fri, 11 Sep 2026 16:00:00 +0900</pubDate><guid>http://kenji.blog/pt/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 A Hipótese de Riemann e a Distribuição dos Números Primos: A Profunda Relação com a Criptografia Moderna" />&lt;h1 id="1-introdução-o-mistério-cósmico-dos-números-primos-e-a-hipótese-de-riemann">1. Introdução: O Mistério Cósmico dos Números Primos e a Hipótese de Riemann
&lt;/h1>&lt;p>Os &amp;ldquo;Números Primos&amp;rdquo; (Prime Numbers) são números naturais divisíveis apenas por 1 e por si mesmos, sendo frequentemente chamados de &amp;ldquo;átomos&amp;rdquo; do mundo da matemática. Essa sequência que segue como 2, 3, 5, 7, 11, 13&amp;hellip; parece, à primeira vista, desordenada e aleatória. Desde que o matemático grego antigo Euclides provou que &amp;ldquo;os números primos são infinitos&amp;rdquo;, incontáveis matemáticos têm tentado desvendar os padrões ocultos nessa disposição de primos.&lt;/p>
&lt;p>Aquele que chegou mais perto de desvendar esse mistério dos números primos foi o matemático alemão Bernhard Riemann, que propôs a &lt;strong>&amp;ldquo;Hipótese de Riemann&amp;rdquo; (Riemann Hypothesis)&lt;/strong> em 1859. A Hipótese de Riemann é um dos problemas mais importantes e não resolvidos da matemática moderna, com um prêmio de 1 milhão de dólares oferecido pelo Instituto de Matemática Clay como um dos Problemas do Prêmio Millennium.&lt;/p>
&lt;p>À primeira vista, pode parecer que esse problema formidável da matemática pura sobre a distribuição de números primos não tenha relação com o nosso dia a dia. No entanto, a segurança da internet, que sustenta a infraestrutura da sociedade moderna, especialmente as &lt;strong>tecnologias de criptografia modernas como a criptografia RSA e a criptografia de curva elíptica (ECC)&lt;/strong>, depende profundamente das propriedades de números primos gigantescos.&lt;/p>
&lt;p>Neste artigo, faremos uma jornada matemática partindo da distribuição dos números primos até o Teorema dos Números Primos, a função zeta de Riemann e o núcleo da Hipótese de Riemann, explorando de forma extremamente detalhada e profunda como isso se conecta com a criptografia moderna e o que aconteceria com o mundo se a Hipótese de Riemann fosse provada.&lt;/p>
&lt;hr>
&lt;h1 id="2-o-teorema-dos-números-primos-e-a-distribuição-de-primos-a-descoberta-de-gauss">2. O Teorema dos Números Primos e a Distribuição de Primos: A Descoberta de Gauss
&lt;/h1>&lt;p>Para entender como os números primos estão distribuídos, os matemáticos conceberam a &lt;strong>função de contagem de números primos (Prime-counting function)&lt;/strong> $\pi(x)$, que representa &amp;ldquo;quantos números primos existem menores ou iguais a um dado número $x$&amp;rdquo;.&lt;/p>
&lt;p>Por exemplo:&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>O gênio matemático de 15 anos, Carl Friedrich Gauss, calculou extensas tabelas de números primos e descobriu que a frequência de aparecimento dos números primos diminui de forma inversamente proporcional ao logaritmo natural $\ln x$. Em outras palavras, ele conjecturou que a probabilidade de encontrar um número primo perto de um número $x$ é de aproximadamente $\frac{1}{\ln x}$.&lt;/p>
&lt;p>Expressando isso por meio de uma integral, obtemos o &lt;strong>logaritmo integral (Logarithmic integral)&lt;/strong> $\text{Li}(x)$.&lt;/p>
$$ \text{Li}(x) = \int_{2}^{x} \frac{dt}{\ln t} $$
&lt;p>A conjectura de Gauss foi posteriormente provada de forma independente por Jacques Hadamard e Charles Jean de la Vallée Poussin em 1896, estabelecendo-se como o &lt;strong>Teorema dos Números Primos (Prime Number Theorem, PNT)&lt;/strong>.&lt;/p>
$$ \lim_{x \to \infty} \frac{\pi(x)}{\text{Li}(x)} = 1 $$
&lt;p>Ou, de forma aproximada, pode ser expresso como:&lt;/p>
$$ \pi(x) \sim \frac{x}{\ln x} $$
&lt;p>Este teorema mostrou que, de uma perspectiva macroscópica, os números primos têm uma distribuição muito suave e previsível. No entanto, microscopicamente, sempre existe um &amp;ldquo;erro&amp;rdquo;, ou seja, uma &amp;ldquo;flutuação&amp;rdquo; entre $\pi(x)$ e $\text{Li}(x)$. A verdadeira natureza dessa flutuação é exatamente o maior mistério que a Hipótese de Riemann tenta desvendar.&lt;/p>
&lt;hr>
&lt;h1 id="3-a-função-zeta-de-riemann-e-o-produto-de-euler">3. A Função Zeta de Riemann e o Produto de Euler
&lt;/h1>&lt;p>A arma mais poderosa para analisar a distribuição dos números primos é a &lt;strong>Função Zeta de Riemann (Riemann Zeta Function)&lt;/strong>. Originalmente, era uma série infinita definida por Leonhard Euler para números reais $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>Uma das maiores conquistas de Euler foi provar que esta série infinita pode ser expressa como um produto infinito sobre todos os números primos $p$. Este é o &lt;strong>Produto 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>Uma compreensão intuitiva da prova é que, se expandirmos cada termo do lado direito como uma série geométrica e os multiplicarmos, o Teorema Fundamental da Aritmética (que afirma que todo número natural pode ser expresso de forma única como um produto de primos) reconstrói perfeitamente a soma dos recíprocos dos números naturais no lado esquerdo.&lt;/p>
&lt;p>&lt;strong>Essa única equação se tornou a ponte que liga a análise (séries infinitas, funções contínuas) à teoria dos números (números primos, números discretos).&lt;/strong> Estudar a função zeta é sinônimo de estudar a distribuição dos números primos.&lt;/p>
&lt;hr>
&lt;h1 id="4-continuação-analítica-e-a-extensão-para-o-plano-complexo">4. Continuação Analítica e a Extensão para o Plano Complexo
&lt;/h1>&lt;p>A genialidade de Riemann reside no fato de ele ter estendido a variável $s$ de $\zeta(s)$, que Euler considerava apenas para números reais, para &lt;strong>números complexos $s = \sigma + it$ ($\sigma$ é a parte real e $t$ é a parte imaginária)&lt;/strong>.&lt;/p>
&lt;p>A série infinita original converge apenas para $\sigma > 1$, mas Riemann usou um método chamado &amp;ldquo;Continuação Analítica (Analytic Continuation)&amp;rdquo; para estender a definição de modo que $\zeta(s)$ fizesse sentido em todo o plano complexo, com exceção do polo em $s = 1$.&lt;/p>
&lt;p>Ele também derivou uma bela equação funcional (Functional equation) que a função zeta satisfaz.&lt;/p>
$$ \zeta(s) = 2^s \pi^{s-1} \sin\left(\frac{\pi s}{2}\right) \Gamma(1-s) \zeta(1-s) $$
&lt;p>Aqui, $\Gamma(x)$ é a função gama. Com esta equação, podemos conhecer as propriedades do semiplano esquerdo a partir das propriedades do semiplano direito.&lt;/p>
&lt;h3 id="zeros-zeros-of-the-zeta-function">Zeros (Zeros of the Zeta Function)
&lt;/h3>&lt;p>Os números complexos $s$ para os quais o valor da função zeta se torna 0 são chamados de &amp;ldquo;zeros&amp;rdquo;.
A partir da equação funcional, quando $s$ é um número par negativo ($-2, -4, -6, \dots$), $\sin(\pi s / 2)$ torna-se 0, resultando em $\zeta(s) = 0$. Estes são chamados de &lt;strong>zeros triviais (Trivial zeros)&lt;/strong>.&lt;/p>
&lt;p>No entanto, o que importa para a distribuição dos números primos são os outros zeros, ou seja, os &lt;strong>zeros não triviais (Non-trivial zeros)&lt;/strong>, que existem na &amp;ldquo;faixa crítica (Critical strip)&amp;rdquo; onde $0 \le \sigma \le 1$.&lt;/p>
&lt;hr>
&lt;h1 id="5-o-núcleo-da-hipótese-de-riemann-e-a-fórmula-explícita">5. O Núcleo da Hipótese de Riemann e a Fórmula Explícita
&lt;/h1>&lt;p>Riemann calculou alguns zeros e formulou uma conjectura surpreendente. Esta é a &lt;strong>Hipótese de Riemann&lt;/strong>.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Hipótese de Riemann (Riemann Hypothesis)&lt;/strong>
Todos os zeros não triviais da função zeta de Riemann $\zeta(s)$ encontram-se na linha reta onde a parte real é igual a $1/2$ ($\text{Re}(s) = 1/2$).&lt;/p>
&lt;/blockquote>
&lt;p>Esta reta onde a parte real é 1/2 é chamada de &amp;ldquo;linha crítica (Critical line)&amp;rdquo;.&lt;/p>
&lt;div class="mermaid">graph TD
A["Função Zeta de Riemann ζ(s)"] --> B["Extensão para o plano complexo via continuação analítica"]
B --> C["Zeros triviais (s = -2, -4, -6 ...)"]
B --> D["Zeros não triviais (0 &lt;= Re(s) &lt;= 1)"]
D --> E["Hipótese de Riemann"]
E --> F["Todos os zeros não triviais estão em Re(s) = 1/2"]
F --> G["Para a prova do limite do termo de erro da distribuição de primos"]&lt;/div>
&lt;p>Por que a Hipótese de Riemann é tão importante? Porque os zeros da função zeta determinam &lt;strong>completamente&lt;/strong> a distribuição dos números primos.&lt;/p>
&lt;p>Riemann e o matemático posterior von Mangoldt derivaram a &amp;ldquo;Fórmula Explícita (Explicit formula)&amp;rdquo;, que descreve com precisão a distribuição dos números primos. Usando a função de Chebyshev $\psi(x)$, ela pode ser expressa da seguinte forma:&lt;/p>
$$ \psi(x) = x - \sum_{\rho} \frac{x^\rho}{\rho} - \ln(2\pi) - \frac{1}{2}\ln(1 - x^{-2}) $$
&lt;p>Aqui, a soma é sobre todos os zeros não triviais $\rho$ da função zeta.
O termo principal é $x$ (o que corresponde ao Teorema dos Números Primos), e ao adicionar e subtrair termos ondulatórios que dependem dos zeros $\rho$, a distribuição exata em forma de degraus dos números primos é restaurada. Pode-se dizer que os zeros não triviais representam as &amp;ldquo;frequências (ondas)&amp;rdquo; da distribuição dos números primos.&lt;/p>
&lt;p>Se a Hipótese de Riemann estiver correta e a parte real de todos os zeros não triviais $\rho$ for exatamente $1/2$, o termo de erro do Teorema dos Números Primos ficará dentro da menor margem teoricamente possível.&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>Em outras palavras, &lt;strong>se a Hipótese de Riemann for verdadeira, fica provado que os números primos estão distribuídos da forma mais &amp;ldquo;ordenada e bela&amp;rdquo; que possamos imaginar&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h1 id="6-a-relação-inseparável-entre-a-criptografia-moderna-e-os-números-primos">6. A Relação Inseparável entre a Criptografia Moderna e os Números Primos
&lt;/h1>&lt;p>Até aqui estivemos no mundo profundo da matemática pura, mas essas propriedades dos números primos sustentam fundamentalmente a sociedade digital moderna. O exemplo mais representativo são os sistemas de criptografia de chave pública, como a &lt;strong>criptografia RSA&lt;/strong>.&lt;/p>
&lt;p>A segurança de todas as comunicações, como pagamentos com cartão de crédito na internet, transmissão de senhas e assinaturas digitais em blockchain, depende dos &amp;ldquo;números primos&amp;rdquo;.&lt;/p>
&lt;h3 id="como-funciona-a-criptografia-rsa">Como funciona a Criptografia RSA
&lt;/h3>&lt;p>A segurança da criptografia RSA é baseada no fato matemático (o problema de fatoração de inteiros) de que &amp;ldquo;a fatoração de um número composto com muitos dígitos é extremamente difícil&amp;rdquo;.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Geração de Chaves&lt;/strong>:
Escolhem-se aleatoriamente dois números primos gigantescos $p$ e $q$ (por exemplo, de 2048 bits cada).
Multiplicam-se ambos para calcular $N = p \times q$. Este $N$ se torna parte da chave pública.
Usando a função totiente de Euler $\phi(N) = (p-1)(q-1)$, gera-se a chave privada $d$.&lt;/p>
$$ e \times d \equiv 1 \pmod{\phi(N)} $$
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Criptografia e Descriptografia&lt;/strong>:
O texto plano (plaintext) $M$ é convertido em um texto cifrado (ciphertext) $C$ usando a chave pública $e, N$.
&lt;/p>
$$ C \equiv M^e \pmod{N} $$
&lt;p>
Apenas aquele que possui a chave privada $d$ pode descriptografar.
&lt;/p>
$$ M \equiv C^d \pmod{N} $$
&lt;/li>
&lt;/ol>
&lt;div class="mermaid">graph LR
A["Texto plano (Plaintext)"] --> B["Criptografado com a chave pública (e, N)"]
B --> C["Texto cifrado (Ciphertext)"]
C --> D["Descriptografado com a chave privada (d)"]
D --> E["Texto plano original"]
F["Atacante (Attacker)"] -- "Tenta fatorar N" --> C
F -.-> G["Sem saber p e q, d é incalculável"]&lt;/div>
&lt;p>Para quebrar a criptografia RSA, é necessário encontrar os números primos originais $p$ e $q$ a partir do gigantesco $N$ (fatoração de primos). Mesmo usando os algoritmos predominantes atuais (como o Crivo do Corpo de Números Generalizado: GNFS), diz-se que fatorar um número de centenas de dígitos levaria um tempo que excede muito a idade do universo, mesmo utilizando supercomputadores.&lt;/p>
&lt;hr>
&lt;h1 id="7-o-impacto-da-hipótese-de-riemann-na-criptografia">7. O Impacto da Hipótese de Riemann na Criptografia
&lt;/h1>&lt;p>Então, como a &amp;ldquo;Hipótese de Riemann&amp;rdquo;, que está no ápice da matemática pura, cruza-se com a &amp;ldquo;criptografia&amp;rdquo;?&lt;/p>
&lt;h3 id="71-algoritmos-de-geração-de-números-primos-teste-de-primalidade-e-a-hipótese-generalizada-de-riemann-grh">7.1. Algoritmos de Geração de Números Primos (Teste de Primalidade) e a Hipótese Generalizada de Riemann (GRH)
&lt;/h3>&lt;p>Para operar a criptografia RSA, primeiro é necessário gerar os gigantescos números primos $p$ e $q$. No entanto, determinar de forma confiável e rápida &amp;ldquo;se um determinado número é primo&amp;rdquo; não é algo simples.&lt;/p>
&lt;p>Atualmente, o que é utilizado de forma prática é um algoritmo probabilístico chamado &lt;strong>teste de primalidade de Miller-Rabin (Miller-Rabin primality test)&lt;/strong>. Esse algoritmo é rápido, mas carrega o risco de classificar incorretamente, com uma probabilidade extremamente baixa, um número composto como primo, um chamado &amp;ldquo;pseudoprimo&amp;rdquo;.&lt;/p>
&lt;p>No entanto, assumindo que a &lt;strong>&amp;ldquo;Hipótese Generalizada de Riemann (Generalized Riemann Hypothesis, GRH)&amp;rdquo;&lt;/strong>, que estende a Hipótese de Riemann às funções L de Dirichlet, é verdadeira, a história muda drasticamente.&lt;/p>
&lt;p>Se a GRH for verdadeira, o limite superior do número de testes no teste de Miller-Rabin será matematicamente garantido, elevando-o de um algoritmo probabilístico para um &lt;strong>&amp;ldquo;algoritmo de tempo polinomial determinístico&amp;rdquo;&lt;/strong> (este era um fato crucial já conhecido antes da descoberta do teste de primalidade AKS).&lt;/p>
&lt;p>Em outras palavras, a Hipótese de Riemann (e sua generalização) desempenha o papel de dar uma garantia direta para a base da criptografia: &amp;ldquo;é possível gerar números primos gigantescos de forma rápida e com absoluta confiança?&amp;rdquo;.&lt;/p>
&lt;h3 id="72-relação-com-os-algoritmos-de-fatoração">7.2. Relação com os Algoritmos de Fatoração
&lt;/h3>&lt;p>Ao avaliar a complexidade computacional dos algoritmos usados para decifrar códigos (como o Crivo do Corpo de Números Generalizado), o conhecimento sobre a distribuição dos números primos também é indispensável. A maioria dos algoritmos de fatoração depende da distribuição de &amp;ldquo;números lisos (Smooth numbers: números que possuem apenas pequenos fatores primos)&amp;rdquo;.&lt;/p>
&lt;p>Para avaliar rigorosamente a frequência com que números lisos aparecem, é necessária uma profunda compreensão da distribuição dos números primos, e aqui também são plenamente utilizadas técnicas de teoria analítica dos números que se conectam diretamente à função zeta e à Hipótese de Riemann. Se a Hipótese de Riemann for provada e o erro na distribuição de primos for completamente determinado, será possível discernir os limites de desempenho dos algoritmos de fatoração de forma mais precisa.&lt;/p>
&lt;hr>
&lt;h1 id="8-se-a-hipótese-de-riemann-for-provada-a-criptografia-será-quebrada">8. Se a Hipótese de Riemann for Provada, a Criptografia será Quebrada?
&lt;/h1>&lt;p>Assim como uma lenda urbana, às vezes se diz que &amp;ldquo;se a Hipótese de Riemann for resolvida, a criptografia RSA desmoronará em um instante&amp;rdquo;, mas &lt;strong>isso é matematicamente impreciso&lt;/strong>.&lt;/p>
&lt;p>A prova da Hipótese de Riemann, por si só, não produzirá imediatamente um algoritmo mágico que acelera drasticamente a fatoração. Isso porque a Hipótese de Riemann é, em última análise, um teorema sobre os &amp;ldquo;padrões de distribuição macroscópicos&amp;rdquo; dos números primos, e não nos diz diretamente por quais números primos um número específico $N$ é divisível (uma propriedade local).&lt;/p>
&lt;p>No entanto, o impacto não é zero.
Isso porque é extremamente provável que, no processo de provar a Hipótese de Riemann, &lt;strong>&amp;ldquo;novas ferramentas matemáticas&amp;rdquo; e &amp;ldquo;métodos analíticos desconhecidos&amp;rdquo; sejam descobertos&lt;/strong>. Olhando para a história, quando o Último Teorema de Fermat ou a Conjectura de Poincaré foram provados, as novas teorias desenvolvidas no processo impulsionaram significativamente a matemática como um todo.&lt;/p>
&lt;p>Se for estabelecido um método desconhecido de geometria algébrica ou de geometria não-comutativa capaz de manipular completamente as propriedades dos zeros da função zeta de Riemann, não se pode negar a possibilidade de que isso leve à descoberta de um algoritmo de fatoração revolucionário (por exemplo, um algoritmo clássico que reduz a complexidade para o tempo polinomial). Nesse sentido, os criptógrafos nunca podem tirar os olhos dos desenvolvimentos da Hipótese de Riemann.&lt;/p>
&lt;h3 id="computadores-quânticos-e-o-algoritmo-de-shor">Computadores Quânticos e o Algoritmo de Shor
&lt;/h3>&lt;p>Uma ameaça mais direta e realista à criptografia não é a prova da Hipótese de Riemann, mas os &lt;strong>computadores quânticos&lt;/strong>. O &amp;ldquo;Algoritmo de Shor&amp;rdquo;, anunciado por Peter Shor em 1994, provou que, com um computador quântico de capacidade suficiente, a fatoração pode ser resolvida em tempo polinomial. Isso fundamentalmente quebraria a criptografia RSA e a criptografia de curva elíptica.&lt;/p>
&lt;p>Atualmente, ao redor do mundo, há uma transição em andamento para a &amp;ldquo;Criptografia Pós-Quântica (Post-Quantum Cryptography, PQC)&amp;rdquo; (como a criptografia baseada em reticulados), que não pode ser decifrada nem por computadores quânticos. As tecnologias de criptografia baseadas em números primos podem, de certa forma, estar encerrando sua era de ouro, mas o valor matemático dos números primos em si nunca se perderá.&lt;/p>
&lt;hr>
&lt;h1 id="9-conclusão-o-cruzamento-entre-a-abstração-da-matemática-e-o-mundo-real">9. Conclusão: O Cruzamento entre a Abstração da Matemática e o Mundo Real
&lt;/h1>&lt;div class="mermaid">graph TD
A["Exploração da matemática pura"] --> B["Esclarecimento da Hipótese de Riemann"]
B --> C["Compreensão completa da distribuição de números primos"]
C --> D["Avanço rápido na teoria dos números e geometria algébrica"]
D -.-> E["Possibilidade de novos algoritmos de fatoração"]
E -.-> F["Atualização na avaliação de segurança da criptografia"]
A --> G["Matemática aplicada e ciência da computação"]
G --> H["Eficiência no teste de primalidade e geração de códigos"]
H --> F&lt;/div>
&lt;p>A exploração insaciável dos números primos, que remonta à Grécia Antiga, foi sublimada por um gênio chamado Riemann em uma bela sinfonia no plano complexo (os zeros da função zeta). E, surpreendentemente, esse cristal de matemática pura e inocente foi aplicado séculos depois como o escudo mais forte para garantir a segurança da sociedade da internet.&lt;/p>
&lt;p>A Hipótese de Riemann é uma entidade que simboliza, simultaneamente, a &amp;ldquo;beleza abstrata&amp;rdquo; e a &amp;ldquo;incrível aplicabilidade ao mundo físico e à sociedade real&amp;rdquo; que a matemática possui.&lt;/p>
&lt;p>Quando esta montanha colossal da matemática, cujo cume ninguém ainda alcançou, for um dia conquistada, compreenderemos completamente a verdade cósmica dos números primos, e também ganharemos uma nova perspectiva sobre os alicerces da sociedade da informação. Estudar criptografia é, por si só, uma jornada pela história da sabedoria humana.&lt;/p></description></item><item><title>Implementando um Algoritmo Rápido de Teste de Primalidade em C++ (Miller-Rabin, etc.)</title><link>http://kenji.blog/pt/p/cpp-fast-prime-testing-miller-rabin/</link><pubDate>Fri, 11 Sep 2026 14:00:00 +0900</pubDate><guid>http://kenji.blog/pt/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 Implementando um Algoritmo Rápido de Teste de Primalidade em C++ (Miller-Rabin, etc.)" />&lt;h1 id="introdução-por-que-é-necessário-um-teste-de-primalidade-rápido">Introdução: Por que é necessário um teste de primalidade rápido?
&lt;/h1>&lt;p>No mundo da ciência da computação, teoria da criptografia e programação competitiva, determinar de forma rápida e precisa &amp;ldquo;se um determinado número é primo&amp;rdquo; é um problema fundamental e extremamente importante. Por exemplo, a criptografia de chave pública, como a criptografia RSA, que sustenta a segurança da sociedade da internet moderna, baseia-se na geração de números primos gigantescos e na dificuldade de sua multiplicação (a dificuldade da fatoração em números primos) como base de sua segurança. Portanto, a tecnologia para identificar instantaneamente se um número gigante é primo não é exagero dizer que é a tecnologia que sustenta os fundamentos da sociedade digital.&lt;/p>
&lt;p>Além disso, na programação competitiva (como AtCoder e Codeforces), o teste de primalidade é um tema frequente. Em situações onde as restrições são para entradas gigantescas como $N \le 10^{18}$, e você precisa realizar dezenas de milhares de testes de primalidade em menos de 1 segundo, algoritmos tradicionais e ingênuos certamente não conseguirão ser executados a tempo (Time Limit Exceeded: TLE).&lt;/p>
&lt;p>Neste artigo, começaremos com algoritmos de teste de primalidade ingênuos, passaremos pelo &amp;ldquo;Teste de Fermat&amp;rdquo;, que é um método de teste de primalidade probabilístico, e explicaremos detalhadamente o algoritmo de altíssima velocidade de nível mais forte na prática, o &amp;ldquo;Teste de Primalidade de Miller-Rabin&amp;rdquo;, que superou as fraquezas do anterior, desde a base matemática até uma implementação altamente otimizada em C++. Em particular, para inteiros de 64 bits ($N &lt; 2^{64}$), explicaremos em detalhes o método que vai além do teste probabilístico e pode &amp;ldquo;testar a primalidade com 100% de certeza (teste determinístico)&amp;rdquo;, e forneceremos o código-fonte em C++ que pode ser usado diretamente na prática.&lt;/p>
&lt;hr>
&lt;h1 id="1-fundamentos-do-teste-de-primalidade-e-divisão-por-tentativa-trial-division">1. Fundamentos do Teste de Primalidade e Divisão por Tentativa (Trial Division)
&lt;/h1>&lt;p>Um número primo (Prime number) é um número natural maior ou igual a 2 que não possui divisores positivos além de 1 e ele mesmo. Seguindo estritamente a definição de número primo, para determinar se um inteiro $N$ é primo, podemos tentar dividir $N$ por todos os inteiros de $2$ a $N-1$, e se ele nunca for divisível, podemos julgar que é um número primo; se for divisível mesmo uma vez, é um número composto (não é um número primo).&lt;/p>
&lt;p>No entanto, a complexidade de tempo deste método é $O(N)$, e se $N$ for um número gigante como $10^{18}$, até mesmo computadores modernos levariam uma quantidade enorme de tempo para calcular.&lt;/p>
&lt;h2 id="otimização-da-divisão-por-tentativa-busca-até-sqrtn">Otimização da Divisão por Tentativa: Busca até $\sqrt{N}$
&lt;/h2>&lt;p>Quando um número composto $N$ é expresso como $a \times b = N$ ($a \le b$), é certo que $a \le \sqrt{N}$. Portanto, não é necessário iterar o loop de teste de primalidade até $N-1$; é suficiente verificar até $\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">// Teste de primalidade por divisão 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">// Verificar apenas números ímpares a partir de 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>A complexidade de tempo deste algoritmo é $O(\sqrt{N})$. Se for em torno de $N \le 10^{12}$, pode ser calculado instantaneamente, mas se $N \approx 10^{18}$, o número de loops será de cerca de $10^9$ vezes, e mesmo com o compilador C++, levará de centenas de milissegundos a vários segundos, tornando-o inadequado para múltiplos testes.&lt;/p>
&lt;hr>
&lt;h1 id="2-teste-de-fermat-o-início-do-teste-de-primalidade-probabilístico">2. Teste de Fermat: O Início do Teste de Primalidade Probabilístico
&lt;/h1>&lt;p>O que foi concebido para superar as limitações do método de divisão por tentativa foi o &amp;ldquo;Algoritmo Probabilístico&amp;rdquo; usando teoremas da teoria dos números. Um exemplo representativo é o &amp;ldquo;Teste de Primalidade de Fermat&amp;rdquo; que utiliza o Pequeno Teorema de Fermat.&lt;/p>
&lt;h2 id="o-pequeno-teorema-de-fermat">O Pequeno Teorema de Fermat
&lt;/h2>&lt;p>Este teorema, descoberto por Pierre de Fermat, afirma o seguinte:&lt;/p>
&lt;blockquote>
&lt;p>Para qualquer número primo $p$ e qualquer inteiro $a$ coprimo com $p$ (que não seja um múltiplo de $p$), a seguinte congruência é válida.
&lt;/p>
$$ a^{p-1} \equiv 1 \pmod p $$
&lt;/blockquote>
&lt;p>Tomando a contrapositiva deste teorema, podemos dizer que &amp;ldquo;Se para um inteiro $N$ e um inteiro $a$ coprimo com $N$, $a^{N-1} \not\equiv 1 \pmod N$, então $N$ é definitivamente um número composto&amp;rdquo;. Usando esta propriedade, o Teste de Fermat seleciona uma base aleatória $a$ para o número $N$ a ser testado e verifica se o cálculo de $a^{N-1} \pmod N$ resulta em $1$.&lt;/p>
&lt;h2 id="exponenciação-modular-rápida-exponenciação-binária">Exponenciação Modular Rápida (Exponenciação Binária)
&lt;/h2>&lt;p>Para realizar o teste de Fermat, é necessário calcular rapidamente a potência gigante $a^{N-1} \pmod N$. Para isso, usamos o método de &amp;ldquo;Exponenciação Modular / Binária&amp;rdquo;. A complexidade de tempo é $O(\log N)$, tornando-o extremamente 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 exponenciação binária
&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>※ Aqui, para evitar overflow, estamos usando a extensão do GCC/Clang &lt;code>__int128_t&lt;/code> (inteiro de 128 bits) para manter o produto intermediário.&lt;/p>
&lt;h2 id="pseudoprimos-e-números-de-carmichael">Pseudoprimos e Números de Carmichael
&lt;/h2>&lt;p>O Teste de Fermat é muito poderoso, mas tem uma fraqueza fatal. É a existência de números tais que, mesmo $N$ sendo um número composto, $a^{N-1} \equiv 1 \pmod N$ é válido para todos os $a$ (onde $a$ é coprimo com $N$).&lt;/p>
&lt;p>Tais números são chamados de &amp;ldquo;pseudoprimos absolutos&amp;rdquo; ou &amp;ldquo;números de Carmichael&amp;rdquo;. O menor número de Carmichael é $561 = 3 \times 11 \times 17$.
Como os números de Carmichael existem, não é possível realizar um teste determinístico de &amp;ldquo;100% de probabilidade&amp;rdquo; apenas com o Teste de Fermat. Não importa quantos $a$ diferentes você tente, números como $561$ sempre se farão passar por primos (enganarão o teste).&lt;/p>
&lt;hr>
&lt;h1 id="3-teste-de-primalidade-de-miller-rabin">3. Teste de Primalidade de Miller-Rabin
&lt;/h1>&lt;p>O que superou de forma brilhante a fraqueza do Teste de Fermat (a existência dos números de Carmichael) foi o &amp;ldquo;Teste de Primalidade de Miller-Rabin&amp;rdquo; concebido por Gary L. Miller e Michael O. Rabin.
Atualmente, como um algoritmo prático de teste de primalidade de alta velocidade, é o mais amplamente utilizado em bibliotecas internas de várias linguagens de programação e na geração de chaves para sistemas criptográficos.&lt;/p>
&lt;h2 id="princípio-matemático">Princípio Matemático
&lt;/h2>&lt;p>O algoritmo de Miller-Rabin, além do Pequeno Teorema de Fermat, utiliza a propriedade de que &amp;ldquo;em um anel de resíduos módulo um primo ($\mathbb{Z}/p\mathbb{Z}$), as soluções para $x^2 \equiv 1 \pmod p$ limitam-se a $x \equiv 1$ ou $x \equiv -1$&amp;rdquo; (ao usar um número composto como módulo, outras raízes quadradas não triviais podem existir).&lt;/p>
&lt;p>Subtrair $1$ do número ímpar $N$ a ser testado, $N-1$, resultará sempre em um número par. Portanto, dividimos $N-1$ por $2$ o máximo possível e expressamos no seguinte formato:
&lt;/p>
$$ N-1 = d \cdot 2^s $$
&lt;p>
(Onde $d$ é um número ímpar e $s \ge 1$)&lt;/p>
&lt;p>Para qualquer base $a$ ($1 &lt; a &lt; N-1$), verificamos se $a^{N-1} \equiv 1 \pmod N$ de acordo com o Pequeno Teorema de Fermat, mas realizamos esse cálculo em etapas.
Especificamente, repetimos a elevação ao quadrado em ordem: $a^d, a^{d \cdot 2}, a^{d \cdot 4}, \ldots, a^{d \cdot 2^s}$.&lt;/p>
&lt;p>A condição para o teste de Miller-Rabin julgar $N$ como &amp;ldquo;sendo primo (ou sendo um primo com forte probabilidade)&amp;rdquo; é que &lt;strong>qualquer uma&lt;/strong> das seguintes afirmações seja verdadeira.&lt;/p>
&lt;ol>
&lt;li>$a^d \equiv 1 \pmod N$&lt;/li>
&lt;li>Existe algum $r$ ($0 \le r &lt; s$) tal que $a^{d \cdot 2^r} \equiv -1 \pmod N$ é satisfeito.
※ Na operação de módulo em C++, $-1 \pmod N$ se torna $N-1$.&lt;/li>
&lt;/ol>
&lt;p>Se $N$ for um número primo, esta condição será necessariamente satisfeita para qualquer $a$. Por outro lado, foi matematicamente provado que se $N$ for um número composto, a probabilidade de satisfazer essa condição (a probabilidade de ser enganado) ao escolher um $a$ aleatório é de $\frac{1}{4}$ ou menos.
Se você realizar $k$ testes independentes, a probabilidade de um falso positivo será menor ou igual a $\left(\frac{1}{4}\right)^k$, podendo ser praticamente considerada zero. Não há números que possam &amp;ldquo;enganar absolutamente&amp;rdquo; como os números de Carmichael.&lt;/p>
&lt;h2 id="fluxo-do-algoritmo-de-miller-rabin-fluxograma-mermaid">Fluxo do Algoritmo de Miller-Rabin (Fluxograma Mermaid)
&lt;/h2>&lt;p>A figura a seguir mostra o fluxo lógico de uma única rodada do teste de primalidade de Miller-Rabin (teste para uma única base $a$).&lt;/p>
&lt;div class="mermaid">graph TD
Start["Início do Teste (Entrada: N, a)"] --> CalcDS["Calcular d (ímpar) e s que satisfazem N-1 = d * 2^s"]
CalcDS --> CalcX["Calcular x = a^d mod N"]
CalcX --> CheckX1{"x == 1 ou x == N-1 ?"}
CheckX1 -- "Yes" --> ReturnTrue["Pode ser primo (Probably Prime)"]
CheckX1 -- "No" --> LoopStart["Iniciar loop de r = 1 até s-1"]
LoopStart --> LoopCondition{"r &lt; s ?"}
LoopCondition -- "No" --> ReturnFalse["Certamente composto (Composite)"]
LoopCondition -- "Yes" --> SquareX["Calcular x = (x * x) mod N"]
SquareX --> CheckXMinus1{"x == N - 1 ?"}
CheckXMinus1 -- "Yes" --> ReturnTrue
CheckXMinus1 -- "No" --> CheckXOne{"x == 1 ?"}
CheckXOne -- "Yes" --> ReturnFalse
CheckXOne -- "No" --> LoopNext["Incrementar r em 1 e prosseguir"]
LoopNext --> LoopCondition&lt;/div>
&lt;hr>
&lt;h1 id="4-teste-determinístico-para-inteiros-de-64-bits">4. Teste Determinístico para Inteiros de 64 Bits
&lt;/h1>&lt;p>O teste de primalidade de Miller-Rabin é inerentemente um algoritmo &amp;ldquo;probabilístico&amp;rdquo;, mas se o limite superior de $N$ for fixo, você pode realizar o teste de primalidade com &amp;ldquo;100% de certeza&amp;rdquo; testando todos de um conjunto específico de várias bases $a$.
Isso é chamado de &lt;strong>Teste Determinístico de Miller-Rabin (Deterministic Miller-Rabin Test)&lt;/strong>.&lt;/p>
&lt;p>Pesquisas de Jim Sinclair e outros revelaram que para todos os inteiros de $N &lt; 2^{64}$ (aproximadamente $1.8 \times 10^{19}$), um julgamento determinístico suficiente e completo é possível se você escolher e testar os seguintes $7$ números primos como a base $a$.&lt;/p>
&lt;p>&lt;strong>Lista de bases $a$ a serem testadas:&lt;/strong>
&lt;code>{2, 325, 9375, 28178, 450775, 9780504, 1795265022}&lt;/code>&lt;/p>
&lt;p>Alternativamente, é bem conhecido que ao usar o seguinte conjunto de $12$ números primos, também é possível determinar perfeitamente para $N &lt; 2^{64}$ e abaixo.
&lt;code>{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}&lt;/code>&lt;/p>
&lt;p>Desta vez, a fim de aumentar a simplicidade e confiabilidade do algoritmo, adotaremos um método que usa os últimos $12$ números primos como base (ou as $7$ bases mais otimizadas). Na implementação em C++, otimizamos dividindo os intervalos com desvios condicionais para manter o número de testes no mínimo.&lt;/p>
&lt;hr>
&lt;h1 id="5-implementação-avançada-em-c-highly-optimized-c-implementation">5. Implementação Avançada em C++ (Highly Optimized C++ Implementation)
&lt;/h1>&lt;p>Agora, resumiremos as teorias matemáticas e o design do algoritmo discutidos até agora, e apresentaremos o código de implementação da função de teste de primalidade de Miller-Rabin de nível mais forte em C++ moderno.&lt;/p>
&lt;h2 id="pontos-de-implementação">Pontos de Implementação
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>&lt;strong>Evitar o overflow da multiplicação de inteiros de 64 bits:&lt;/strong>
Quando $N \approx 10^{18}$, $x \times x$ na multiplicação modular atinge o máximo de $10^{36}$, ultrapassando facilmente o valor máximo de $1.8 \times 10^{19}$ de um inteiro normal de 64 bits (&lt;code>uint64_t&lt;/code> ou &lt;code>long long&lt;/code>).
Para resolver este problema, usamos a extensão do tipo &lt;code>__int128_t&lt;/code> (ou &lt;code>unsigned __int128&lt;/code>) do GCC e Clang e pegamos o módulo após calcular com precisão de 128 bits. Isso permite multiplicações modulares rápidas sem usar algoritmos complexos.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Seleção da base determinística:&lt;/strong>
Se o valor de $N$ for pequeno, otimizamos para que precisemos testar apenas algumas bases.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="código-fonte-c-completo">Código-Fonte C++ Completo
&lt;/h2>&lt;p>Abaixo, mostramos o código-fonte finalizado pronto para uso prático. Este código pode ser copiado e usado como está em ambientes como de programação 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">// Fast (a * b) mod m using 128-bit integers
&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 por exponenciação binária
&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">// Teste determinístico de inteiros de 64 bits pelo Teste de Primalidade 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">// Pré-julgamento de valores limite e pequenos números primos
&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">// Decompor n-1 no formato 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 usadas para determinação
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// Otimização para minimizar o número de bases testadas de acordo com o tamanho 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 são determinísticas para todos os números 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">// Executar teste 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">// Se a for múltiplo de n, não pode ser julgado, mas não é 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">// Primeira condição atendida, para a próxima 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">// Loop de s-1 vezes (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">// Segunda condição atendida, possível 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">// Se nenhuma condição for atendida, é definitivamente um número composto
&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">// Se as condições forem atendidas em todas as bases, 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 teste de exemplo
&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">// Número 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">// Número 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">// Número primo próximo 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">// Número composto (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 (Número composto)
&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">// Um dos maiores primos perto 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-avaliação-de-complexidade-e-desempenho-do-algoritmo">6. Avaliação de Complexidade e Desempenho do Algoritmo
&lt;/h1>&lt;p>Consideraremos o desempenho do algoritmo implementado.&lt;/p>
&lt;h2 id="complexidade-de-tempo-time-complexity">Complexidade de Tempo (Time Complexity)
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>Divisão por tentativa:&lt;/strong> $O(\sqrt{N})$&lt;/li>
&lt;li>&lt;strong>Teste de Fermat:&lt;/strong> Cálculo de potência $O(\log N) \times k$ ($k$ é o número de tentativas)&lt;/li>
&lt;li>&lt;strong>Método de Miller-Rabin:&lt;/strong> Cálculo de potência e loop $O(\log N) \times k$&lt;/li>
&lt;/ul>
&lt;p>No ambiente de 64 bits ($N \le 2^{64}$), o método determinístico de Miller-Rabin acima verificará no máximo $7$ bases. Portanto, pode-se considerar uma constante $k \le 7$, e a complexidade de tempo total é estritamente $O(\log N)$.
Mesmo no caso máximo ($N \approx 10^{19}$), o número de etapas de execução caberá em não mais que $7 \times 64 = 448$ etapas de operações básicas, e o tempo de execução é menor que alguns microssegundos (segundos $10^{-6}$). Em comparação com o $O(\sqrt{N})$ (cerca de $4 \times 10^9$ loops) do método de divisão por tentativa, uma &lt;strong>aceleração de milhões de vezes&lt;/strong> foi alcançada.&lt;/p>
&lt;h2 id="otimização-adicional-multiplicação-de-montgomery-montgomery-multiplication">Otimização Adicional: Multiplicação de Montgomery (Montgomery Multiplication)
&lt;/h2>&lt;p>Na implementação deste artigo, a extensão do tipo &lt;code>__int128_t&lt;/code> de 128 bits é usada para a divisão (operação de módulo &lt;code>%&lt;/code>). Mesmo com CPUs modernas, a divisão de inteiros (instrução DIV) é uma instrução de alto custo que requer dezenas de ciclos em comparação com a adição e multiplicação.&lt;/p>
&lt;p>Os criadores de bibliotecas e programadores competitivos em busca de otimização extrema ocasionalmente empregarão um método chamado &lt;strong>Multiplicação de Montgomery (Montgomery Multiplication)&lt;/strong>. A multiplicação de Montgomery é um algoritmo surpreendente que substitui a operação cara de módulo (divisão) apenas por &amp;ldquo;deslocamentos de bits e multiplicações&amp;rdquo;, mapeando os números em um &amp;ldquo;espaço de Montgomery&amp;rdquo; especial.
Ao incorporar isso na multiplicação modular do teste de Miller-Rabin, é possível aumentar ainda mais a velocidade de execução em cerca de duas a três vezes. Sendo este um tema muito profundo, eu gostaria de explicá-lo em detalhes em outro artigo.&lt;/p>
&lt;hr>
&lt;h1 id="7-resumo">7. Resumo
&lt;/h1>&lt;p>Neste artigo, explicamos tudo de uma vez, desde os fundamentos dos testes de primalidade até o conteúdo avançado.
Vamos revisar os pontos principais.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>O Método de Divisão por Tentativa&lt;/strong> é confiável, mas como a complexidade computacional é $O(\sqrt{N})$, falta praticidade quando $N$ excede $10^{12}$.&lt;/li>
&lt;li>&lt;strong>O Teste de Fermat&lt;/strong> é muito rápido com $O(\log N)$, mas tem uma fraqueza fatal de ser enganado por pseudoprimos absolutos, como os números de Carmichael.&lt;/li>
&lt;li>&lt;strong>O Teste de Primalidade de Miller-Rabin&lt;/strong> é o algoritmo mais prático e mais forte que resolve a fraqueza do Teste de Fermat.&lt;/li>
&lt;li>Na implementação em C++, o uso de &lt;code>__int128_t&lt;/code> permite o tratamento seguro do overflow da multiplicação de inteiros de 64 bits.&lt;/li>
&lt;li>Se estiver dentro do intervalo de inteiros de 64 bits ($N &lt; 2^{64}$), selecionando $7$ ou $12$ números primos específicos como base, é possível &lt;strong>realizar o teste de primalidade deterministicamente (100% preciso)&lt;/strong> em vez de probabilisticamente.&lt;/li>
&lt;/ol>
&lt;p>Testes rápidos de primalidade são uma técnica inevitável nos cálculos que lidam com números gigantescos. O código-fonte de Miller-Rabin em C++ fornecido neste artigo é robusto e pode ser utilizado como está na prática. Por favor, tente utilizá-lo em seus próprios projetos ou competições de algoritmos.&lt;/p>
&lt;div class="mermaid">graph LR
TrialDivision["Divisão por Tentativa (O(√N))"] --> Fermat["Teste de Fermat (O(log N), tem fraqueza)"]
Fermat --> MillerRabin["Método Miller-Rabin (O(log N), pode ser determinístico)"]
MillerRabin --> Montgomery["+ Multiplicação Montgomery (Aceleração por fator constante)"]
style MillerRabin fill:#f9f,stroke:#333,stroke-width:2px&lt;/div>
&lt;p>O mundo dos algoritmos onde a programação e a matemática se cruzam é muito bonito e profundo. Esperamos que isso o ajude em seu aprendizado futuro.&lt;/p>
&lt;hr>
&lt;p>&lt;em>Referência:&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>【Explicação de Matemática】Explicando o mecanismo da criptografia RSA para que até alunos do ensino médio entendam</title><link>http://kenji.blog/pt/p/rsa-encryption-math-explained-for-beginners/</link><pubDate>Fri, 11 Sep 2026 13:00:00 +0900</pubDate><guid>http://kenji.blog/pt/p/rsa-encryption-math-explained-for-beginners/</guid><description>&lt;img src="http://kenji.blog/p/rsa-encryption-math-explained-for-beginners/img/eyecatch.jpg" alt="Featured image of post 【Explicação de Matemática】Explicando o mecanismo da criptografia RSA para que até alunos do ensino médio entendam" />&lt;p>Uma das tecnologias que sustenta a segurança da nossa sociedade baseada na internet desde a sua base é a &amp;ldquo;Criptografia RSA&amp;rdquo;. Muitas das comunicações que usamos casualmente todos os dias, como pagamentos com cartão de crédito em compras online, troca de mensagens em redes sociais com amigos, ou o envio e recebimento de informações confidenciais de empresas, são protegidas pela criptografia RSA e suas tecnologias sucessoras.&lt;/p>
&lt;p>No entanto, ao ouvir a palavra &amp;ldquo;criptografia&amp;rdquo;, você pode imaginar máquinas de cifra complexas como as que aparecem em filmes de espionagem, ou uma matemática super avançada que apenas alguns gênios conseguem entender. É verdade que a teoria criptográfica moderna é baseada em matemática avançada, mas &lt;strong>o mecanismo fundamental da criptografia RSA pode ser plenamente compreendido com o conhecimento de matemática ensinado no ensino médio (propriedades de números inteiros, números primos, congruências, etc.)&lt;/strong>.&lt;/p>
&lt;p>Neste artigo, usaremos o conhecimento da matemática do ensino médio como ponto de partida para explicar passo a passo e de forma aprofundada os princípios matemáticos pelos quais a criptografia RSA funciona e por que é tão difícil de quebrar. Explicarei cuidadosamente usando exemplos concretos para que mesmo aqueles que têm um pouco de dificuldade com matemática possam entender.&lt;/p>
&lt;hr>
&lt;h2 id="1-criptografia-de-chave-simétrica-e-criptografia-de-chave-pública">1. Criptografia de Chave Simétrica e Criptografia de Chave Pública
&lt;/h2>&lt;p>Antes de mergulhar no mecanismo matemático da criptografia RSA, vamos primeiro revisar a ideia básica de criptografia. Os métodos criptográficos podem ser amplamente divididos em dois tipos: &amp;ldquo;Criptografia de chave simétrica&amp;rdquo; e &amp;ldquo;Criptografia de chave pública&amp;rdquo;.&lt;/p>
&lt;h3 id="11-limitações-da-criptografia-de-chave-simétrica">1.1 Limitações da Criptografia de Chave Simétrica
&lt;/h3>&lt;p>Muitos dos métodos de criptografia usados há muito tempo são chamados de &amp;ldquo;Criptografia de chave simétrica&amp;rdquo;. Este é um método em que &lt;strong>a mesma chave é usada tanto para &amp;ldquo;criptografia&amp;rdquo; (converter uma mensagem em um texto cifrado secreto) quanto para &amp;ldquo;descriptografia&amp;rdquo; (retornar o texto cifrado à mensagem original)&lt;/strong>.&lt;/p>
&lt;p>Por exemplo, suponha que a Alice vá enviar uma carta secreta ao Bob. A Alice coloca a carta em uma caixa e a tranca usando um cadeado (chave simétrica). Para o Bob abrir essa caixa, ele precisará ter a mesma chave que a Alice usou.&lt;/p>
&lt;p>Este método tem um grande problema: o &amp;ldquo;Problema de distribuição de chaves&amp;rdquo;. Quando Alice e Bob, que estão distantes, se comunicam pela primeira vez, como eles poderiam compartilhar a chave sem serem interceptados? Se a chave for roubada por terceiros enquanto é enviada por correio, toda a comunicação criptografada subsequente ficará totalmente exposta.&lt;/p>
&lt;h3 id="12-uma-invenção-revolucionária-criptografia-de-chave-pública">1.2 Uma Invenção Revolucionária: &amp;ldquo;Criptografia de Chave Pública&amp;rdquo;
&lt;/h3>&lt;p>A &amp;ldquo;Criptografia de chave pública&amp;rdquo; foi idealizada para resolver esse problema de distribuição de chaves. A criptografia RSA é um tipo deste sistema.&lt;/p>
&lt;p>Na criptografia de chave pública, usamos &lt;strong>duas chaves diferentes: uma &amp;ldquo;chave para criptografar (chave pública)&amp;rdquo; e uma &amp;ldquo;chave para descriptografar (chave privada)&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;ol>
&lt;li>O destinatário, Bob, cria um par de &amp;ldquo;chave pública&amp;rdquo; e &amp;ldquo;chave privada&amp;rdquo;.&lt;/li>
&lt;li>Bob publica a &amp;ldquo;chave pública&amp;rdquo; para todo o mundo (não importa quem a obtenha).&lt;/li>
&lt;li>A remetente, Alice, criptografa a mensagem usando a &amp;ldquo;chave pública&amp;rdquo; de Bob e a envia.&lt;/li>
&lt;li>A mensagem criptografada só pode ser descriptografada com a &amp;ldquo;chave privada&amp;rdquo; que apenas Bob possui.&lt;/li>
&lt;/ol>
&lt;p>Comparando isso com o exemplo do cadeado: Bob cria vários &amp;ldquo;cadeados abertos (chaves públicas)&amp;rdquo; e os distribui pelo mundo. Alice coloca sua mensagem destinada a Bob em uma caixa e a tranca fechando um dos cadeados que encontrou pertencente a Bob. Uma vez que o cadeado é fechado, ele só pode ser aberto com a &amp;ldquo;chave mestra (chave privada)&amp;rdquo; que Bob possui. Mesmo que alguém roube a caixa no meio do caminho, essa pessoa não conseguirá abri-la porque não tem a chave mestra.&lt;/p>
&lt;div class="mermaid">graph TD
A["Alice (Remetente)"] --> B["Texto Plano (Mensagem)"]
B --> C["Processo de Criptografia"]
D["Chave Pública do Bob (Disponível para qualquer um)"] --> C
C --> E["Enviado via Internet: Texto Cifrado"]
E --> F["Processo de Descriptografia"]
G["Chave Privada do Bob (Mantida apenas pelo Bob)"] --> F
F --> H["Texto Plano Restaurado (Mensagem)"]
H --> I["Bob (Destinatário)"]&lt;/div>
&lt;p>Para implementar este sistema revolucionário, é necessária algum tipo de &lt;strong>&amp;ldquo;função unidirecional (um quebra-cabeça matemático de mão única)&amp;rdquo;&lt;/strong>, que significa que &amp;ldquo;é fácil criptografar com a chave pública, mas é absolutamente impossível descriptografar sem a chave privada&amp;rdquo;. Como peça para esse quebra-cabeça, a atenção se voltou para os &amp;ldquo;números primos&amp;rdquo;, algo que conhecemos bem.&lt;/p>
&lt;hr>
&lt;h2 id="2-base-matemática-1-sustentando-a-criptografia-rsa-números-primos-e-fatoração-em-números-primos">2. Base Matemática 1 Sustentando a Criptografia RSA: Números Primos e Fatoração em Números Primos
&lt;/h2>&lt;p>A segurança da criptografia RSA é baseada no fato matemático de que &lt;strong>&amp;ldquo;a fatoração de números gigantes em números primos é extremamente difícil&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;h3 id="21-o-que-são-números-primos">2.1 O que são Números Primos?
&lt;/h3>&lt;p>Os números primos são &amp;ldquo;números naturais maiores que 1 que só podem ser divididos por 1 e por si mesmos&amp;rdquo;.
Exemplo: $2, 3, 5, 7, 11, 13, 17, 19, 23...$&lt;/p>
&lt;p>Os números primos são como &amp;ldquo;átomos&amp;rdquo; para todos os números inteiros. Qualquer número natural pode ser decomposto na forma de uma multiplicação de números primos. Isso é chamado de &lt;strong>fatoração em números primos&lt;/strong>. Por exemplo, como em $60 = 2^2 \times 3 \times 5$, ignorando a ordem, o fato de que pode ser fatorado em números primos de apenas uma única forma é conhecido como o &amp;ldquo;Teorema Fundamental da Aritmética&amp;rdquo;.&lt;/p>
&lt;h3 id="22-a-dificuldade-da-fatoração-em-números-primos-função-unidirecional">2.2 A Dificuldade da Fatoração em Números Primos (Função Unidirecional)
&lt;/h3>&lt;p>O importante aqui é a assimetria: &lt;strong>&amp;ldquo;A multiplicação é fácil, mas a fatoração em números primos é difícil&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>Por exemplo, tente calcular de cabeça a multiplicação destes dois números primos:
$11 \times 13 = ?$
Isto é fácil. A resposta é $143$.&lt;/p>
&lt;p>Então, que tal o próximo número?
Por favor, fatore $323$ em números primos.
E então? Deve demorar um pouco. (A resposta é $17 \times 19$).&lt;/p>
&lt;p>Quando os números são pequenos, até humanos conseguem calcular de alguma forma, mas quando os números ficam grandes, o cálculo se torna explosivamente difícil, mesmo usando computadores. Na criptografia RSA moderna, usamos um número $N = p \times q$ obtido pela multiplicação de números primos incrivelmente gigantes $p$ e $q$ de 2048 bits (cerca de 600 dígitos decimais).&lt;/p>
&lt;p>Quando fornecidos dois números primos gigantes $p$ e $q$, calcular $N$ leva apenas um instante (menos de um milissegundo) para um computador. No entanto, reversamente, receber apenas $N$ e encontrar os $p$ e $q$ originais leva tanto tempo que não seria resolvido mesmo rodando os supercomputadores mais rápidos da atualidade por trilhões de anos.&lt;/p>
&lt;p>Essa &lt;strong>&amp;ldquo;assimetria computacional (um caminho é fácil, o caminho inverso é difícil)&amp;rdquo;&lt;/strong> é a base que cria o relacionamento entre a chave pública e a chave privada.&lt;/p>
&lt;hr>
&lt;h2 id="3-base-matemática-2-sustentando-a-criptografia-rsa-congruência-aritmética-modular">3. Base Matemática 2 Sustentando a Criptografia RSA: Congruência (Aritmética Modular)
&lt;/h2>&lt;p>Os cálculos da criptografia RSA não são realizados com adição e multiplicação onde os números ficam infinitamente grandes, como costumamos usar, mas sim no mundo dos &amp;ldquo;restos&amp;rdquo; quando divididos por um certo número. Isso é chamado de &lt;strong>congruência (aritmética modular)&lt;/strong>.&lt;/p>
&lt;h3 id="31-a-matemática-do-relógio">3.1 A Matemática do Relógio
&lt;/h3>&lt;p>A aritmética modular é frequentemente comparada com a &amp;ldquo;matemática do relógio&amp;rdquo;. Supondo que agora são 10 horas, que horas serão daqui a 5 horas? $10 + 5 = 15$ horas, mas em um relógio normal de 12 horas, respondemos &amp;ldquo;3 horas&amp;rdquo;. Isso ocorre porque o resto de 15 dividido por 12 é 3.&lt;/p>
&lt;p>No mundo da matemática, isso é escrito da seguinte forma:
&lt;/p>
$$ 15 \equiv 3 \pmod{12} $$
&lt;p>
Lê-se &amp;ldquo;15 e 3 são congruentes módulo 12 (os restos quando divididos por 12 são iguais)&amp;rdquo;.&lt;/p>
&lt;h3 id="32-propriedades-básicas-da-congruência">3.2 Propriedades Básicas da Congruência
&lt;/h3>&lt;p>A congruência possui propriedades muito úteis e semelhantes a equações (igualdades $=$). Vamos assumir que o módulo (número pelo qual dividimos) é $N$.
Quando $a \equiv b \pmod N$ e $c \equiv d \pmod N$, o seguinte é verdadeiro:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Adição:&lt;/strong> $a + c \equiv b + d \pmod N$&lt;/li>
&lt;li>&lt;strong>Subtração:&lt;/strong> $a - c \equiv b - d \pmod N$&lt;/li>
&lt;li>&lt;strong>Multiplicação:&lt;/strong> $a \times c \equiv b \times d \pmod N$&lt;/li>
&lt;li>&lt;strong>Exponenciação:&lt;/strong> $a^k \equiv b^k \pmod N$ ($k$ é um número natural)&lt;/li>
&lt;/ol>
&lt;p>Particularmente importante é a propriedade da &amp;ldquo;exponenciação&amp;rdquo;. Isso significa que &lt;strong>&amp;ldquo;o resto de uma potência é igual à potência do resto&amp;rdquo;&lt;/strong>.
Por exemplo, imagine que você queira encontrar o resto de $7^{100}$ dividido por $5$. Seria muito difícil multiplicar o $7$ por ele mesmo 100 vezes e depois dividir por $5$, mas se usarmos a propriedade da congruência, como $7 \equiv 2 \pmod 5$, então $7^{100} \equiv 2^{100} \pmod 5$, permitindo simplificar drasticamente o cálculo. No mundo da criptografia lidamos com potências de números extremamente grandes, então essa propriedade é indispensável.&lt;/p>
&lt;hr>
&lt;h2 id="4-base-matemática-3-sustentando-a-criptografia-rsa-função-de-euler-e-teorema-de-euler">4. Base Matemática 3 Sustentando a Criptografia RSA: Função de Euler e Teorema de Euler
&lt;/h2>&lt;p>A partir daqui, entra a matemática mágica que forma o núcleo da criptografia RSA. Apresentamos o &amp;ldquo;Teorema de Euler&amp;rdquo;, que é uma generalização do &amp;ldquo;Pequeno Teorema de Fermat&amp;rdquo;.&lt;/p>
&lt;h3 id="41-a-função-totiente-de-euler-phin">4.1 A Função Totiente de Euler $\phi(N)$
&lt;/h3>&lt;p>A função totiente de Euler (função $\phi$) é uma função que, para um determinado número natural $N$, retorna &lt;strong>&amp;ldquo;a quantidade de números naturais de 1 até $N$ que são coprimos de $N$ (ou seja, seu máximo divisor comum é 1)&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>Vamos olhar alguns exemplos.&lt;/p>
&lt;ul>
&lt;li>$\phi(5)$: Dentre 1, 2, 3, 4, 5, os números coprimos de 5 são 1, 2, 3, 4; ou seja, 4 números. Portanto, $\phi(5) = 4$.&lt;/li>
&lt;li>$\phi(6)$: Dentre 1, 2, 3, 4, 5, 6, os números coprimos de 6 são 1, 5; ou seja, 2 números. Portanto, $\phi(6) = 2$.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>[Propriedade especial no caso de números primos]&lt;/strong>
Quando $p$ é um número primo, todos os números de 1 até $p-1$ são coprimos de $p$. Logo,
&lt;/p>
$$ \phi(p) = p - 1 $$
&lt;p>&lt;strong>[Propriedade especial no caso de um produto de números primos]&lt;/strong>
Para dois números primos distintos $p$ e $q$, quando se estabelece que $N = p \times q$, $\phi(N)$ pode ser facilmente obtido pelo seguinte cálculo:
&lt;/p>
$$ \phi(N) = \phi(p) \times \phi(q) = (p - 1)(q - 1) $$
&lt;p>
Essa propriedade funciona como a &amp;ldquo;porta dos fundos secreta (alçapão)&amp;rdquo; na criptografia RSA. Alguém que conhece $p$ e $q$ (o criador das chaves) pode calcular $\phi(N)$ em um instante, mas um terceiro que conhece apenas $N$ não pode determinar $\phi(N)$ a menos que fatore $N$ em números primos.&lt;/p>
&lt;h3 id="42-teorema-de-euler">4.2 Teorema de Euler
&lt;/h3>&lt;p>Leonhard Euler usou esse $\phi(N)$ para provar o seguinte belo teorema.&lt;/p>
&lt;p>&lt;strong>Teorema de Euler:&lt;/strong>
Quando um inteiro $a$ e $N$ são coprimos, a seguinte congruência é verdadeira:
&lt;/p>
$$ a^{\phi(N)} \equiv 1 \pmod N $$
&lt;p>Isso é uma propriedade surpreendente que diz: &amp;ldquo;quando você multiplica um certo número $a$ por ele mesmo $\phi(N)$ vezes e divide por $N$, o resto será sempre $1$&amp;rdquo;. (Se $N$ for um número primo $p$, torna-se $a^{p-1} \equiv 1 \pmod p$, que é chamado de Pequeno Teorema de Fermat).&lt;/p>
&lt;p>Vamos transformar este Teorema de Euler. Multiplicamos ambos os lados por $a$ mais uma vez.
&lt;/p>
$$ a^{\phi(N) + 1} \equiv a \pmod N $$
&lt;p>Além disso, para qualquer número inteiro $k$, $a^{k \cdot \phi(N)}$ também será $1^k = 1$, de modo que a seguinte fórmula é válida:
&lt;/p>
$$ a^{k \cdot \phi(N) + 1} \equiv a \pmod N $$
&lt;p>Esta exata equação é o princípio fundamental que faz com que a mágica da criptografia RSA de &lt;strong>&amp;ldquo;retornar ao original quando descriptografada após criptografar&amp;rdquo;&lt;/strong> funcione.&lt;/p>
&lt;hr>
&lt;h2 id="5-algoritmo-da-criptografia-rsa-passos-para-geração-de-chaves-criptografia-e-descriptografia">5. Algoritmo da Criptografia RSA: Passos para Geração de Chaves, Criptografia e Descriptografia
&lt;/h2>&lt;p>Agora que temos o conhecimento básico, vamos olhar para os passos concretos da criptografia RSA. A criptografia RSA é dividida principalmente em três fases: &amp;ldquo;1. Geração de Chaves&amp;rdquo;, &amp;ldquo;2. Criptografia&amp;rdquo; e &amp;ldquo;3. Descriptografia&amp;rdquo;.&lt;/p>
&lt;div class="mermaid">flowchart TD
A1["1. Escolher os primos p, q"] --> A2["Calcular N = p × q"]
A1 --> A3["Calcular φ(N) = (p-1)(q-1)"]
A3 --> A4["Escolher e coprimo de φ(N)"]
A3 --> A5["Calcular d tal que e × d ≡ 1 (mod φ(N))"]
A2 --> A6["Chave Pública (N, e)"]
A4 --> A6
A5 --> A7["Chave Privada d"]
B1["2. Mensagem em Texto Plano M"] --> B2["Calcular C ≡ M^e (mod N)"]
A6 -.-> B2
B2 --> B3["Enviar Texto Cifrado C"]
B3 --> C1["3. Receber Texto Cifrado C"]
C1 --> C2["Calcular M ≡ C^d (mod N)"]
A7 -.-> C2
C2 --> C3["Obter a Mensagem em Texto Plano original M"]&lt;/div>
&lt;h3 id="51-geração-de-chaves-key-generation">5.1 Geração de Chaves (Key Generation)
&lt;/h3>&lt;p>Bob, o destinatário, gera sua &amp;ldquo;chave pública&amp;rdquo; e &amp;ldquo;chave privada&amp;rdquo;.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Seleção dos primos:&lt;/strong> Ele escolhe dois grandes números primos $p$ e $q$ aleatoriamente.&lt;/li>
&lt;li>&lt;strong>Cálculo do módulo $N$:&lt;/strong> Ele calcula $N = p \times q$. Este $N$ é tornado público.&lt;/li>
&lt;li>&lt;strong>Cálculo de $\phi(N)$:&lt;/strong> Ele calcula a função de Euler $\phi(N) = (p - 1)(q - 1)$. Este número é um segredo apenas do Bob.&lt;/li>
&lt;li>&lt;strong>Seleção da chave pública $e$:&lt;/strong> Ele escolhe um inteiro $e$ tal que $1 &lt; e &lt; \phi(N)$ e que $e$ seja coprimo de $\phi(N)$.&lt;/li>
&lt;li>&lt;strong>Cálculo da chave privada $d$:&lt;/strong> Ele encontra um inteiro $d$ que satisfaça a seguinte condição:
$$ e \times d \equiv 1 \pmod{\phi(N)} $$
Isso significa &amp;ldquo;um número $d$ tal que, quando $e \times d$ é dividido por $\phi(N)$, o resto é $1$&amp;rdquo;.&lt;/li>
&lt;/ol>
&lt;p>Com isso, as chaves estão prontas.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Chave Pública:&lt;/strong> O par $(N, e)$. Publicado para todo o mundo.&lt;/li>
&lt;li>&lt;strong>Chave Privada:&lt;/strong> $d$. Jamais revelado a ninguém.&lt;/li>
&lt;/ul>
&lt;h3 id="52-criptografia-encryption">5.2 Criptografia (Encryption)
&lt;/h3>&lt;p>Suponha que Alice queira enviar uma mensagem secreta $M$ para Bob. (Assumimos que $M$ é um texto convertido em valor numérico, e que $0 \le M &lt; N$). Alice usa a chave pública do Bob, $(N, e)$, para fazer o seguinte cálculo:&lt;/p>
$$ C \equiv M^e \pmod N $$
&lt;p>Ela calcula &amp;ldquo;o resto $C$ obtido após elevar a mensagem $M$ à potência de $e$, e dividir por $N$&amp;rdquo;. Esse $C$ é o texto cifrado.&lt;/p>
&lt;h3 id="53-descriptografia-decryption">5.3 Descriptografia (Decryption)
&lt;/h3>&lt;p>Bob recebe o texto cifrado $C$. Bob usa sua chave privada $d$ para calcular o seguinte:&lt;/p>
$$ M \equiv C^d \pmod N $$
&lt;p>Ao calcular &amp;ldquo;o resto de elevar o texto cifrado $C$ à potência de $d$, e dividir por $N$&amp;rdquo;, incrivelmente a mensagem original $M$ é restaurada!&lt;/p>
&lt;hr>
&lt;h2 id="6-por-que-retorna-ao-original-na-descriptografia-prova-matemática">6. Por que retorna ao original na descriptografia? (Prova Matemática)
&lt;/h2>&lt;p>Você pode se perguntar: &amp;ldquo;Por que simplesmente elevar $C$ à potência de $d$ retorna ao $M$ original?&amp;rdquo;. Aqui, o &amp;ldquo;Teorema de Euler&amp;rdquo; mencionado anteriormente mostra o seu poder.&lt;/p>
&lt;p>Vamos substituir a equação de criptografia $C = M^e$ na equação de descriptografia $C^d \pmod N$.
&lt;/p>
$$ C^d \equiv (M^e)^d \equiv M^{ed} \pmod N $$
&lt;p>Aqui, lembre-se do Passo 5 da Geração de Chaves. Ao criar $d$, Bob o escolheu de forma que $e \times d \equiv 1 \pmod{\phi(N)}$. Isso significa que &amp;ldquo;$ed$ é um múltiplo de $\phi(N)$ somado a $1$&amp;rdquo;. Podemos escrevê-lo da seguinte maneira, utilizando um inteiro $k$:
&lt;/p>
$$ ed = k \cdot \phi(N) + 1 $$
&lt;p>Substituímos isso no expoente e o decompomos usando as leis dos expoentes:
&lt;/p>
$$ M^{ed} = M^{k \cdot \phi(N) + 1} = M^{k \cdot \phi(N)} \times M^1 = (M^{\phi(N)})^k \times M $$
&lt;p>Aqui, assumindo que a mensagem $M$ e $N$ são coprimos, a partir do &lt;strong>Teorema de Euler&lt;/strong>, temos $M^{\phi(N)} \equiv 1 \pmod N$.
&lt;/p>
$$ (M^{\phi(N)})^k \times M \equiv 1^k \times M \equiv M \pmod N $$
&lt;p>Portanto, a seguinte equação é perfeitamente estabelecida:
&lt;/p>
$$ C^d \equiv M \pmod N $$
&lt;p>Como a Alice não conhece $d$, e o espião também não conhece $d$, apenas Bob, que possui $d$, consegue extrair o $M$ a partir de $C$.&lt;/p>
&lt;hr>
&lt;h2 id="7-exemplo-concreto-vamos-vivenciar-o-rsa-calculando-à-mão-usando-pequenos-números-primos">7. Exemplo Concreto: Vamos vivenciar o RSA calculando à mão usando pequenos números primos
&lt;/h2>&lt;p>Vamos tentar enviar uma comunicação criptografada de Alice para Bob usando números pequenos (números primos) de fato.&lt;/p>
&lt;p>&lt;strong>[Fase de Geração de Chaves do Bob]&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>Escolhe-se dois números primos $p=11$, $q=13$.&lt;/li>
&lt;li>Calcula-se $N = 11 \times 13 = 143$.&lt;/li>
&lt;li>Calcula-se $\phi(N) = (11 - 1) \times (13 - 1) = 10 \times 12 = 120$.&lt;/li>
&lt;li>Escolhe-se uma chave pública $e$ coprima de $\phi(N)=120$. Aqui usaremos $e=7$.&lt;/li>
&lt;li>Calcula-se a chave privada $d$. Buscamos um $d$ que satisfaça $7 \times d \equiv 1 \pmod{120}$.
Na equação $7d = 120k + 1$, quando $k=6$, temos $721$, e $721 \div 7 = 103$.
Portanto, $d = 103$.&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>Chave Pública: $(N=143, e=7)$&lt;/li>
&lt;li>Chave Privada: $d=103$&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>[Fase de Criptografia da Alice]&lt;/strong>
Suponha que ela queira enviar a mensagem $M = 9$.
Fórmula: $C \equiv 9^7 \pmod{143}$
$9^7 = 4.782.969$. Dividindo isso por 143, dá $33447$ com resto $48$.
O texto cifrado se tornou $C = 48$.&lt;/p>
&lt;p>&lt;strong>[Fase de Descriptografia do Bob]&lt;/strong>
Bob recebe o texto cifrado $C = 48$ e o descriptografa usando a chave privada $d = 103$.
Fórmula: $M \equiv 48^{103} \pmod{143}$
Se executarmos &lt;code>(48 ** 103) % 143&lt;/code> na calculadora, brilhantemente o resultado será &amp;ldquo;&lt;strong>9&lt;/strong>&amp;rdquo;! A mensagem original foi recebida com sucesso.&lt;/p>
&lt;hr>
&lt;h2 id="8-como-encontrar-a-chave-privada-d-algoritmo-de-euclides-estendido">8. Como encontrar a Chave Privada $d$: Algoritmo de Euclides Estendido
&lt;/h2>&lt;p>No exemplo calculado à mão, adivinhamos o valor de $k$ para encontrar $d=103$, mas quando o número atinge centenas de dígitos, esse método se torna impossível. Nos programas reais usamos um algoritmo chamado &lt;strong>&amp;ldquo;Algoritmo de Euclides Estendido&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>Resolver $7d \equiv 1 \pmod{120}$ é o mesmo que encontrar inteiros $d, y$ que satisfaçam $7d + 120y = 1$. Ao realizar os passos do Algoritmo de Euclides ao reverso, podemos encontrar isso mecanicamente.&lt;/p>
&lt;ol>
&lt;li>$120 \div 7 = 17$ com resto $1$&lt;/li>
&lt;li>Rearranjando isso, temos $1 = 120 - 17 \times 7$&lt;/li>
&lt;li>Em outras palavras, $-17 \times 7 \equiv 1 \pmod{120}$&lt;/li>
&lt;/ol>
&lt;p>No mundo do módulo $120$, $-17$ tem o mesmo significado que $120 - 17 = 103$. Assim, o $d = 103$ pode ser encontrado em um instante. Este método consegue calcular de forma extremamente rápida por mais gigante que o número seja.&lt;/p>
&lt;hr>
&lt;h2 id="9-outra-face-da-criptografia-rsa-assinatura-digital">9. Outra Face da Criptografia RSA: Assinatura Digital
&lt;/h2>&lt;p>A parte fantástica da criptografia RSA é que, se invertermos as funções da chave pública e chave privada, ela também pode ser usada como uma &lt;strong>&amp;ldquo;Assinatura Digital&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>Na criptografia os passos eram: &amp;ldquo;Criptografar com a chave pública $\Rightarrow$ Descriptografar com a chave privada&amp;rdquo;,
Mas na assinatura digital seguimos os passos: &amp;ldquo;Criptografar com a chave privada $\Rightarrow$ Descriptografar com a chave pública&amp;rdquo;.&lt;/p>
&lt;div class="mermaid">flowchart TD
A1["1. Alice cria a assinatura com a chave privada"] --> A2["S ≡ M^d (mod N)"]
A2 --> A3["Envia a mensagem M e a assinatura S"]
A3 --> B1["2. Bob verifica a assinatura com a chave pública"]
B1 --> B2["Calcula M' ≡ S^e (mod N)"]
B2 --> B3["Verifica se M' e M coincidem"]&lt;/div>
&lt;p>A Alice transforma a mensagem usando sua própria chave privada $d$ (essa será a assinatura $S$) e envia ao Bob. O Bob faz o cálculo de verificação usando a chave pública de Alice, $e$. Se o resultado do cálculo for igual à mensagem original, isso prova simultaneamente que &amp;ldquo;os dados só poderiam ter sido criados pela chave privada da Alice&amp;rdquo; e que &amp;ldquo;a mensagem não foi adulterada no meio do caminho&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h2 id="10-sentindo-a-criptografia-rsa-na-prática-com-código">10. Sentindo a Criptografia RSA na Prática com Código
&lt;/h2>&lt;p>O cálculo de potências que seria difícil de fazer à mão pode ser implementado muito facilmente usando Python. A seguir, um código Python para você experienciar a lógica central da criptografia RSA.&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;/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="k">def&lt;/span> &lt;span class="nf">gcd&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">b&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;Calcular o máximo divisor comum&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="n">b&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">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">b&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>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">a&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">def&lt;/span> &lt;span class="nf">mod_inverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&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;Encontrar a chave privada d (usando a função embutida a partir do Python 3.8)&amp;#34;&amp;#34;&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="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&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">phi&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"># 1. Geração de chaves&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">p&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">q&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">13&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="n">p&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">q&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">phi&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">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="o">*&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">q&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">e&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">d&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_inverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&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;Chave Pública: (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">, e=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">), Chave Privada: d=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">d&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"># 2. Criptografia&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">9&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ciphertext&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">e&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;Texto Cifrado: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">ciphertext&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"># 3. Descriptografia&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">decrypted_message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ciphertext&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="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Mensagem Descriptografada: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">decrypted_message&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;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Como a função &lt;code>pow(base, exp, mod)&lt;/code> do Python usa internamente um algoritmo rápido chamado &amp;ldquo;exponenciação binária (ou método das multiplicações repetidas)&amp;rdquo;, o cálculo termina em um instante mesmo para números com centenas de dígitos.&lt;/p>
&lt;hr>
&lt;h2 id="11-conclusão-e-futuro-das-tecnologias-de-criptografia">11. Conclusão e Futuro das Tecnologias de Criptografia
&lt;/h2>&lt;p>Nós desvendamos o mecanismo da criptografia RSA tendo como base a matemática do ensino médio.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>A dificuldade da fatoração em números primos:&lt;/strong> É fácil calcular $p \times q = N$, mas é extremamente difícil encontrar $p, q$ a partir de $N$.&lt;/li>
&lt;li>&lt;strong>Congruências e Teorema de Euler:&lt;/strong> Devido à regra $a^{\phi(N)} \equiv 1 \pmod N$, completa-se o mágico alçapão onde &amp;ldquo;a exponenciação por um certo número retorna ao estado original&amp;rdquo;.&lt;/li>
&lt;li>&lt;strong>Chave Pública e Chave Privada:&lt;/strong> Qualquer um pode criptografar, mas apenas o destinatário legítimo consegue descriptografar.&lt;/li>
&lt;/ol>
&lt;p>O $N$ da criptografia RSA usada atualmente possui mais de 600 dígitos, e mesmo que todos os supercomputadores do mundo se juntassem para faturar esse número em primos, levaria um tempo maior do que a idade do universo. No entanto, se o &amp;ldquo;computador quântico&amp;rdquo;, que vem sendo pesquisado ativamente nos últimos anos, se tornar prático no futuro, essa fatoração poderá ser resolvida em instantes pelo &amp;ldquo;Algoritmo de Shor&amp;rdquo;. Por esse motivo, criptografias &amp;ldquo;resistentes a computadores quânticos&amp;rdquo;, que não poderiam ser quebradas nem mesmo por eles, estão sendo desenvolvidas a um ritmo acelerado pelo mundo todo.&lt;/p>
&lt;p>A matemática avançada que costumamos considerar &amp;ldquo;inútil&amp;rdquo;, na verdade, protege nosso dia a dia na sua essência mais profunda. A criptografia RSA é um excelente material didático que nos ensina essa profundidade e beleza da matemática. Ficaria muito feliz se através deste artigo você pudesse sentir um pouco do fascínio da criptografia e da matemática.&lt;/p></description></item></channel></rss>