Skip to Content

Aritmética modular

Recursos
FuenteRecursoNotas
AryanshSThe Art of Modular Arithmetic

introduce la aritmética modular a través de numerosos ejemplos y problemas de nivel de olimpiada matemática

IUSACO13.3 - Modular Arithmetic

muy breve; este módulo se basa en esto

David AltizioModular Arithmetic

muchos ejemplos de olimpiadas matemáticas

CPH21.2 - Modular Arithmetic
PAPS117.4 - Modular Arithmetic
CFSpheniscine - Modular Arithmetic for Beginners

algunos problemas de práctica

MONT2, 5 - Modular Arithmetic

Introducción

En aritmética modular, en lugar de trabajar con los enteros mismos, trabajamos con sus restos al dividir por mm. A esto lo llamamos tomar módulo mm. Por ejemplo, si tomamos m=23m = 23, entonces en lugar de trabajar con x=247x = 247, usamos xmod23=17x \bmod 23 = 17. Por lo general, mm será un primo grande, dado en el problema; los dos valores más comunes son 109+710^9 + 7 y 998244353=119223+1998\,244\,353=119\cdot 2^{23}+1. La aritmética modular se usa para evitar tratar con números que desbordan los tipos de datos nativos, porque podemos tomar restos, de acuerdo con las siguientes fórmulas:

(a+b)modm=(amodm+bmodm)modm (a+b) \bmod m = (a \bmod m + b \bmod m) \bmod m (ab)modm=(amodmbmodm)modm (a-b) \bmod m = (a \bmod m - b \bmod m) \bmod m (ab)(modm)=((amodm)(bmodm))modm (a \cdot b) \pmod{m} = ((a \bmod m) \cdot (b \bmod m)) \bmod m abmodm=(amodm)bmodm a^b \bmod {m} = (a \bmod m)^b \bmod m

Exponenciación modular

HechoFuenteNombreDificultadTagsSolución
CSESExponentiationFácilModular Arithmeticen el módulo

Recursos

Recursos
FuenteRecursoNotas
cp-algoBinary Exponentiation

La exponenciación binaria se puede usar para calcular de forma eficiente xnmodmx ^ n \mod m. Para ello, descompongamos xnx ^ n en componentes binarios. Por ejemplo, 5105 ^ {10} = 5101025 ^ {1010_2} = 58525 ^ 8 \cdot 5 ^ 2. Entonces, si conocemos xyx ^ y para todo yy que es potencia de dos (x1x ^ 1, x2x ^ 2, x4x ^ 4, \dots , x2log2nx ^ {2^{\lfloor{\log_2n} \rfloor}}, podemos calcular xnx ^ n en O(logn)\mathcal{O}(\log n).

Para tratar con mm, observemos que el módulo no afecta las multiplicaciones, así que podemos implementar directamente el algoritmo de “exponenciación binaria” de arriba añadiendo una línea para tomar los resultados (modm)\pmod m.

Solución - Exponentiation

#include <bits/stdc++.h> using namespace std; using ll = long long; ll exp(ll x, ll n, ll m) { assert(n >= 0); x %= m; // note: m * m must be less than 2^63 to avoid ll overflow ll res = 1; while (n > 0) { if (n % 2 == 1) { res = res * x % m; } x = x * x % m; n /= 2; } return res; } int main() { int t; cin >> t; for (int i = 0; i < t; i++) { int a, b; cin >> a >> b; cout << exp(a, b, 1e9 + 7) << "\n"; } }
import java.io.*; import java.util.*; public class Exponentiation { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); PrintWriter out = new PrintWriter(System.out); int n = Integer.parseInt(br.readLine()); for (int i = 0; i < n; i++) { StringTokenizer st = new StringTokenizer(br.readLine()); long a = Long.parseLong(st.nextToken()); long b = Long.parseLong(st.nextToken()); out.println(exp(a, b, (long)1e9 + 7)); } out.close(); } public static long exp(long x, long n, long m) { assert (n >= 0); x %= m; // note: m * m must be less than 2^63 to avoid ll overflow long res = 1; while (n > 0) { if (n % 2 == 1) { res = res * x % m; } x = x * x % m; n /= 2; } return res; } }

Podemos usar la función nativa pow de Python para calcular potencias modulares de forma eficiente.

MOD = 10**9 + 7 for _ in range(int(input())): first, second = [int(i) for i in input().split()] print(pow(first, second, MOD))

Inverso modular

El inverso modular es el equivalente del recíproco en la aritmética de números reales; para dividir aa por bb, se multiplica aa por el inverso modular de bb. Aquí solo consideraremos módulos primos pp.

Por ejemplo, el inverso de 22 módulo p=109+7p=10^9+7 es i=p+12=5108+4i=\frac{p+1}{2}=5\cdot 10^8+4. Esto significa que para cualquier entero xx,

(2x)ix(2i)x(mod109+7). (2x)\cdot i\equiv x\cdot (2i)\equiv x\pmod{10^9+7}.

Por ejemplo, 10i5(mod109+7)10i\equiv 5\pmod{10^9+7}.

Recursos
FuenteRecursoNotas
cp-algoModular Multiplicative Inverse

Varias formas de tomar el inverso modular; aquí solo discutiremos la segunda

Con exponenciación

El pequeño teorema de Fermat (no confundir con el último teorema de Fermat) afirma que todos los enteros aa no divisibles por pp satisfacen ap11(modp)a^{p - 1} \equiv 1 \pmod{p}. En consecuencia, ap2a1(modp)a^{p-2} \cdot a \equiv 1 \pmod{p}. Por lo tanto, ap2a^{p - 2} es un inverso modular de aa módulo pp.

const int MOD = 1e9 + 7; int main() { ll x = exp(2, MOD - 2, MOD); cout << x << "\n"; // 500000004 assert(2 * x % MOD == 1); }
public class Main { public static final int MOD = (int)Math.pow(10, 9) + 7; public static void main(String[] args) throws IOException { long x = exp(2, MOD - 2, MOD); System.out.println(x); // 500000004 assert (2 * x % MOD == 1); } }
MOD = 10**9 + 7 x = pow(2, MOD - 2, MOD) print(x) # 500000004 assert 2 * x % MOD == 1

Con división euclidiana

También podemos hallar inversos modulares mediante la división euclidiana. Dado el módulo primo m>am > a tenemos:

m=ka+r m = k \cdot a + r

donde k=mak = \lfloor \frac{m}{a} \rfloor y r=mmodar = m \mod a. Entonces:

0=ka+rmodm    r=kamodm    ra1=kmodm    a1=kr1modm \begin{align*} & 0 = k \cdot a + r \mod m \\ \iff{} & r = -k \cdot a \mod m \\ \iff{} & r \cdot a^{-1} = -k \mod m \\ \iff{} & a^{-1} = -k \cdot r^{-1} \mod m \end{align*}

Aquí hay una implementación recursiva breve de la fórmula anterior:

int inv(int x) { return x <= 1 ? x : MOD - MOD / x * inv(MOD % x) % MOD; }
static int inv(int x) { return x <= 1 ? x : MOD - MOD / x * inv(MOD % x) % MOD; }
def inv(x: int) -> int: return x if x <= 1 else MOD - MOD // x * inv(MOD % x) % MOD

La ventaja de este enfoque es que podemos precomputar el inverso modular de los números en el rango [1,MOD)[1, MOD) en O(MOD)\mathcal{O}(MOD).

inv[1] = 1; // assume we already defined this array for (int i = 2; i < MOD; i++) { inv[i] = MOD - MOD / i * inv[MOD % i] % MOD; }
inv[1] = 1; // assume we already defined this array for (int i = 2; i < MOD; i++) { inv[i] = MOD - MOD / i * inv[MOD % i] % MOD; }
inv[1] = 1 # assume we already defined this array for i in range(2, MOD): inv[i] = MOD - MOD / i * inv[MOD % i] % MOD

Como toma O(logp)\mathcal{O}(\log p) tiempo calcular un inverso modular módulo pp, el uso frecuente de la división dentro de un bucle puede aumentar de forma significativa el tiempo de ejecución de un programa. Si el inverso modular del mismo número (o de los mismos números) se usa muchas veces, es buena idea precalcularlo.

Además, siempre hay que asegurarse de no intentar dividir por 0. Tener en cuenta que después de aplicar el módulo, un número distinto de cero puede volverse cero, así que hay que tener mucho cuidado al dividir por valores no constantes.

Otra forma de calcular inversos modulares

También podemos usar el algoritmo de Euclides extendido. Ver el módulo en la sección Avanzado.

Plantillas

Aquí hay algunas plantillas que implementan tipos enteros que se envuelven automáticamente cuando superan un cierto módulo:

Recursos
FuenteRecursoNotas
BenqModInt
BenqModIntShort

factible de tipear durante un contest

AtCoderModInt

contiene un archivo modint.hpp

AtCoderModInt Documentation

Aquí hay un ejemplo usando la plantilla de Benq:

#include <bits/stdc++.h> using namespace std; const int MOD = 1e9 + 7; // BeginCodeSnip{ModInt} struct mi { int v; explicit operator int() const { return v; } mi() { v = 0; } mi(long long _v) : v(_v % MOD) { v += (v < 0) * MOD; } }; mi &operator+=(mi &a, mi b) { if ((a.v += b.v) >= MOD) a.v -= MOD; return a; } mi &operator-=(mi &a, mi b) { if ((a.v -= b.v) < 0) a.v += MOD; return a; } mi operator+(mi a, mi b) { return a += b; } mi operator-(mi a, mi b) { return a -= b; } mi operator*(mi a, mi b) { return mi((long long)a.v * b.v); } mi &operator*=(mi &a, mi b) { return a = a * b; } mi pow(mi a, long long p) { assert(p >= 0); return p == 0 ? 1 : pow(a * a, p / 2) * (p & 1 ? a : 1); } mi inv(mi a) { assert(a.v != 0); return pow(a, MOD - 2); } mi operator/(mi a, mi b) { return a * inv(b); } // EndCodeSnip int main() { { int a = 1e8, b = 1e8, c = 1e8; cout << (long long)a * b % MOD * c % MOD << "\n"; // 49000000 } { mi a = 1e8, b = 1e8, c = 1e8; // cout << a * b * c << "\n"; // Errors- we have to cast this an an int cout << (int)(a * b * c) << "\n"; // 49000000 } }

Y uno usando la de AtCoder:

#include <bits/stdc++.h> using namespace std; // https://atcoder.github.io/ac-library/document_en/modint.html // (included in atcoder grading) #include <atcoder/modint> using mint = atcoder::modint; const int MOD = 1e9 + 7; int main() { // Set a global modulus mint::set_mod(MOD); // Scientific notation doesn't work with AtCoder's class mint a = 100000000; mint b = 100000000; mint c = 100000000; mint res = a * b * c; cout << res.val() << endl; // Same result }

Problemas

HechoFuenteNombreDificultadTagsSolución
CSESExponentiation IIFácilModular ArithmeticSolución
CFSanta's BotFácilModular ArithmeticSolución
KilonovaSumdivNormalPrime Factorization, MathSolución
YSQueue CompositeNormalModular ArithmeticSolución
CSESDivisor AnalysisNormalModular ArithmeticSolución
GoldThe Best SubsequenceDifícilModular Arithmetic, Binary SearchSolución