Aritmética Modular
intermediario criptografia, algoritmos, estruturas-de-dados teoria-dos-numerosAritmética Modular
Aritmética modular é a aritmética do relógio: depois de 12 vem 1 de novo. Formalmente, é a aritmética das classes de equivalência módulo $n$ — e é o ambiente natural de tudo que envolve ciclos, tabelas hash e criptografia.
Congruência
$$a \equiv b \pmod n \iff n \mid (a-b)$$
Esta é uma relação de equivalência, e portanto particiona $\mathbb{Z}$ em $n$ classes residuais. O conjunto delas é $\mathbb{Z}_n = {0,1,\dots,n-1}$.
Propriedades operatórias
Congruências podem ser somadas e multiplicadas:
$$a\equiv b,; c\equiv d \implies a+c \equiv b+d, \quad ac \equiv bd \pmod n$$
Consequência prática essencial: é possível reduzir a cada passo. Isso é o que impede o estouro numérico em cálculos modulares longos:
# ruim: o produto cresce sem controle
resultado = 1
for x in valores:
resultado *= x
resultado %= n
# bom: reduz a cada passo
resultado = 1
for x in valores:
resultado = (resultado * x) % n
Divisão não funciona diretamente. De $ac \equiv bc \pmod n$ não se conclui $a \equiv b$ — a menos que $\gcd(c,n)=1$. Para "dividir", multiplique pelo inverso modular, que só existe nessa condição.
Grupo multiplicativo
Os elementos invertíveis de $\mathbb{Z}_n$ são exatamente os coprimos com $n$. Eles formam o grupo $\mathbb{Z}_n^*$, cuja ordem é a função totiente de Euler:
$$\varphi(n) = n\prod_{p\mid n}\left(1-\frac1p\right)$$
Para $p$ primo, $\varphi(p) = p-1$ e todo elemento não nulo é invertível — ou seja, $\mathbb{Z}_p$ é um corpo. Esse fato é a razão de módulos primos serem preferidos em tantas aplicações.
Para $n = pq$ com $p,q$ primos distintos, $\varphi(n) = (p-1)(q-1)$ — a fórmula no coração do RSA.
Exponenciação modular rápida
Calcular $a^b \bmod n$ ingenuamente é inviável: $b$ pode ter centenas de bits. A exponenciação binária resolve em $O(\log b)$ multiplicações:
def pot_mod(base, exp, mod):
resultado = 1
base %= mod
while exp > 0:
if exp & 1:
resultado = (resultado * base) % mod
base = (base * base) % mod
exp >>= 1
return resultado
# em Python, embutido e otimizado:
pow(base, exp, mod)
Sem essa técnica não haveria criptografia de chave pública: $a^{65537} \bmod n$ com $n$ de 2048 bits leva microssegundos com exponenciação binária, e tempo geológico sem ela.
Alerta de segurança: a versão acima é vulnerável a ataques de canal lateral por tempo, já que o ramo depende do bit do expoente. Implementações criptográficas usam variantes de tempo constante (escada de Montgomery). Em código real, use uma biblioteca auditada — nunca implemente a primitiva você mesmo.
Teste de divisibilidade
As regras clássicas saem de congruências. Como $10 \equiv 1 \pmod 9$, temos $10^k \equiv 1$, logo um número é congruente à soma de seus dígitos módulo 9 — daí a regra do 9. Do mesmo modo, $10 \equiv -1 \pmod{11}$ dá a regra da soma alternada.
Aplicações em computação
Tabelas hash. h(k) = k % m. Escolher $m$ primo evita padrões quando as chaves têm estrutura regular.
Somas de verificação. ISBN, CPF, IBAN e o algoritmo de Luhn (cartões de crédito) são todos verificações modulares que detectam erros de digitação e transposição.
Buffers circulares. indice = (indice + 1) % tamanho.
Geradores pseudoaleatórios. Geradores congruenciais lineares são $x_{n+1} = (ax_n + c) \bmod m$.
Hashing de Rabin-Karp. Um hash rolante módulo um primo permite comparar substrings em tempo constante amortizado.
Criptografia. RSA, Diffie-Hellman, DSA e curvas elípticas operam em aritmética modular.
Redução de Montgomery. Técnica que substitui a divisão cara por deslocamentos, usada em toda biblioteca criptográfica séria.
Exemplo trabalhado
Calcular $3^{100} \bmod 7$.
Pelo pequeno teorema de Fermat, $3^6 \equiv 1 \pmod 7$. Como $100 = 6\cdot16 + 4$:
$$3^{100} = (3^6)^{16}\cdot3^4 \equiv 1^{16}\cdot81 \equiv 81 \bmod 7 \equiv 4$$
Verificando com exponenciação binária: $3^2=2$, $3^4 = 4$, $3^8 = 2$, ... o resultado bate.
Note como reduzir o expoente módulo $\varphi(n)$ transformou um cálculo enorme em trivial. É exatamente essa ideia que o RSA explora.
Erros comuns
- Resto negativo em linguagens com a convenção do C.
- Estouro ao multiplicar antes de reduzir; em linguagens com inteiros de 64 bits, o produto de dois números de 32 bits já pode estourar.
- Dividir em vez de multiplicar pelo inverso.
- Módulo não primo onde a primalidade é necessária.
- Implementar criptografia à mão, sem proteção contra canal lateral.
Leituras recomendadas
- Shoup, capítulos 2 a 4 — aritmética modular com foco computacional.
- Menezes, van Oorschot e Vanstone, Handbook of Applied Cryptography, capítulo 2 — gratuito online, com todos os algoritmos.
- Knuth, TAOCP volume 2 — aritmética de precisão arbitrária.
- Documentação do OpenSSL e da libsodium — como as primitivas são realmente implementadas.
Victor Shoup (2009). A Computational Introduction to Number Theory and Algebra. Cambridge University Press. DOI: 10.1017/cbo9780511814549. Alfred J. Menezes and Paul C. van Oorschot and Scott A. Vanstone (1996). Handbook of Applied Cryptography. CRC Press. ISBN 9781439821916.
Referências
- Victor Shoup (2009). A Computational Introduction to Number Theory and Algebra. Cambridge University Press.
- Alfred J. Menezes; Paul C. van Oorschot; Scott A. Vanstone (1996). Handbook of Applied Cryptography. CRC Press.