Purpose: Solve a system of simultaneous modular congruences x ≡ a_i (mod m_i) for pairwise coprime moduli, finding a unique solution modulo the product of all moduli, in O(k log M) time for k congruences with product M.
Algorithm
- Given congruences
x ≡ a1 (mod m1),x ≡ a2 (mod m2), …, with allmipairwise coprime. - Compute
M = m1 · m2 · ... · mk(the product of all moduli). - For each
i, computeMi = M / miand its modular inverseMi_inv = Mi⁻¹ mod mi(via the Extended Euclidean Algorithm). - The solution is
x = Σ (ai · Mi · Mi_inv) mod M. - This
xis the unique solution moduloM.
Code
long long extendedGCD(long long a, long long b, long long& x, long long& y) {
if (b == 0) { x = 1; y = 0; return a; }
long long x1, y1;
long long g = extendedGCD(b, a % b, x1, y1);
x = y1; y = x1 - (a / b) * y1;
return g;
}
long long modInverse(long long a, long long m) {
long long x, y;
extendedGCD(a, m, x, y);
return ((x % m) + m) % m;
}
long long crt(vector<long long>& a, vector<long long>& m) {
long long M = 1;
for (long long mi : m) M *= mi;
long long result = 0;
for (size_t i = 0; i < a.size(); i++) {
long long Mi = M / m[i];
long long MiInv = modInverse(Mi, m[i]);
result = (result + (__int128)a[i] * Mi % M * MiInv) % M;
}
return (result + M) % M;
}Paradigm
Transform and Conquer. The system of congruences is transformed into a weighted sum of independently solved single-modulus components (each isolated using Mi and its inverse), directly combining them into one solution rather than searching or iterating.
Complexity
- Time: O(k log M) for
kcongruences (dominated by the modular inverse computations) - Space: O(k)
Proof of Correctness
Why the constructed x satisfies every congruence: Fix some index j. For every i ≠ j, the term ai · Mi · Mi_inv is divisible by mj, since Mi = M / mi includes mj as a factor whenever i ≠ j (because all moduli are pairwise coprime, mj divides Mi for every i ≠ j). So modulo mj, only the i = j term survives: x ≡ aj · Mj · Mj_inv (mod mj). Since Mj_inv is defined as the modular inverse of Mj modulo mj, Mj · Mj_inv ≡ 1 (mod mj), giving x ≡ aj · 1 = aj (mod mj) — exactly satisfying the j-th congruence. This holds for every j, so x satisfies all congruences simultaneously.
Why the solution is unique modulo M: Suppose x and x' both satisfy all the congruences. Then x - x' ≡ 0 (mod mi) for every i, meaning every mi divides x - x'. Since the mi are pairwise coprime, their product M must also divide x - x' (a standard consequence of coprimality: pairwise coprime divisors of a number multiply to also divide it). So x ≡ x' (mod M), proving uniqueness modulo M.
Why a solution always exists (existence): The explicit construction above directly produces one, so existence is constructive, not just asserted — combined with uniqueness, this fully characterizes the solution set as exactly one residue class modulo M. ∎
Variants / Use Cases
- Garner’s Algorithm → an alternative, often more numerically stable, incremental method for combining congruences without computing the full product
Mupfront until necessary - RSA-CRT optimization → speeds up RSA decryption by working modulo the two prime factors separately and recombining via CRT
- Combining sieve results across small primes → used in number-theoretic algorithms that decompose problems modulo several small primes then reconstruct
- Polynomial interpolation / multi-modulus computation → used in computer algebra systems to compute exact results using multiple modular computations to avoid overflow
- Solving simultaneous scheduling/cyclic constraints → classic puzzle framing (“this number leaves remainder r1 when divided by m1…”)