Mastering Modular Arithmetic for Algorithms
Chinese Remainder Theorem
Solving Systems of Congruences
So far, we've focused on solving individual modular arithmetic equations. But what happens when a number must satisfy several conditions at once? For example, imagine you're looking for a number that leaves a remainder of 2 when divided by 3, a remainder of 3 when divided by 5, and a remainder of 2 when divided by 7.
This scenario can be written as a system of simultaneous congruences:
Finding an that works for all three equations requires a more powerful tool. The Chinese Remainder Theorem (CRT) provides a systematic way to solve such systems, provided the moduli meet a specific condition.
The Chinese Remainder Theorem
The Chinese Remainder Theorem states that if you have a system of congruences where the moduli are pairwise coprime, there is a unique solution for modulo the product of all the moduli. Pairwise coprime means that for any two moduli in the system, their greatest common divisor is 1.
Given a system of congruences: ... If are pairwise coprime, there is a unique solution for modulo .
The theorem not only guarantees a solution but also gives us a constructive algorithm to find it.
The Constructive Algorithm
The core idea is to build the solution piece by piece. For each congruence , we'll construct a term that satisfies this specific congruence while being equivalent to 0 for all other moduli. Summing these terms gives us the final solution.
Here’s the step-by-step process:
-
Calculate the total product. First, compute . This will be the modulus of our final unique solution.
-
Isolate each congruence. For each congruence from 1 to , we define . This value is the product of all moduli except . By its construction, is a multiple of every (where ), which means .
-
Find the modular inverse. We need to find a number that, when multiplied by , is congruent to 1 modulo . This is the modular multiplicative inverse of modulo . We can find it because we know (since all the moduli are pairwise coprime). Let's call this inverse .
- Construct the solution. The full solution is the sum of terms, where each term corresponds to one of the original congruences. The final sum is taken modulo .
A Worked Example
Let's solve the system from the beginning:
The moduli 3, 5, and 7 are all prime, so they are pairwise coprime.
-
Total product: .
-
Calculate each and its inverse :
- For : . We need . Since , we need , which is 2. So, .
- For : . We need . Since , we need , which is 1. So, .
- For : . We need . Since , we need , which is 1. So, .
-
Construct the solution:
- Find the final result modulo : . , so . The smallest positive integer solution is 23.
Implementation and Application
In competitive programming, the CRT is a powerful technique for problems involving large numbers. If you need to compute a result modulo some large composite number , you can sometimes compute it modulo each of the prime power factors of and then combine the results using the CRT. This breaks a large, difficult problem into several smaller, more manageable ones.
Here is a C++ implementation. It relies on the Extended Euclidean Algorithm function extendedGcd we developed previously to find modular inverses.
#include <vector>
#include <numeric>
// Assumes a, b are not both zero
long long extendedGcd(long long a, long long b, long long &x, long long &y) {
if (a == 0) {
x = 0;
y = 1;
return b;
}
long long x1, y1;
long long gcd = extendedGcd(b % a, a, x1, y1);
x = y1 - (b / a) * x1;
y = x1;
return gcd;
}
long long modInverse(long long a, long long m) {
long long x, y;
long long g = extendedGcd(a, m, x, y);
if (g != 1) return -1; // Inverse doesn't exist
return (x % m + m) % m;
}
// Solves the system x
// congruences[i].first = a_i
// congruences[i].second = n_i
long long chineseRemainderTheorem(const std::vector<std::pair<long long, long long>>& congruences) {
long long N = 1;
for (const auto& cong : congruences) {
N *= cong.second;
}
long long solution = 0;
for (const auto& cong : congruences) {
long long a_i = cong.first;
long long n_i = cong.second;
long long N_i = N / n_i;
long long y_i = modInverse(N_i, n_i);
// Use __int128 to prevent overflow during intermediate multiplication
__int128 term = (__int128)a_i * N_i * y_i;
solution = (solution + term) % N;
}
return (solution + N) % N;
}
Notice the use of __int128. The intermediate products can easily exceed the capacity of a long long, even if the final solution fits. Using a 128-bit integer type is a safe way to handle these large intermediate values before taking the final modulo.
What is the fundamental requirement for the moduli () in a system of congruences for the Chinese Remainder Theorem to guarantee a unique solution?
In the process of solving a system of congruences using the CRT, if , what does represent?
With the Chinese Remainder Theorem, you can now solve systems of congruences, a technique that opens the door to more advanced number theory problems.