From Divisibility to Congruence: Number Theory Basics
Based on original number theory notes, covering gcd, lcm, Bezout, exgcd, fast power, modular inverse, and CRT.
Contents#
gcdlcmBézout's IdentityExtended Euclidean AlgorithmFast ExponentiationModular InverseChinese Remainder Theorem
gcd#
Recursive Method:
// Most commonly used
int gcd(int a, int b) // Quickly compute the greatest common divisor
{
return !b ? a : gcd(b, a % b);
}cppIterative Method:
int gcd(int a, int b) {
while (b != 0) {
int tmp = a;
a = b;
b = tmp % b;
}
return a;
}cppC++14 provides __gcd(a, b).
GCD for large numbers:
Big gcd(Big a, Big b) {
// Record the number of times the common factor 2 appears in a and b
int atimes = 0, btimes = 0;
while (a % 2 == 0) {
a >>= 1;
atimes++;
}
while (b % 2 == 0) {
b >>= 1;
btimes++;
}
for (;;) {
// The common factor 2 in a and b has already been accounted for; a and b cannot both be even hereafter.
while (a % 2 == 0) {
a >>= 1;
}
while (b % 2 == 0) {
b >>= 1;
}
if (a == b) break;
// Ensure a >= b
if (a < b) swap(a, b);
a -= b;
}
return a << min(atimes, btimes);
}cpplcm#
For two numbers:
To find the least common multiple of two numbers, first find their greatest common divisor.
For multiple numbers: Once we find the of two numbers, finding the lcm is . For multiple numbers, we don’t necessarily need to find a common gcd first. The most direct method is: after calculating the gcd of two numbers, perhaps when finding the gcd of multiple numbers, we can put it into the sequence and continue solving for the remaining numbers. So, we can simply put the least common multiple into the sequence directly.
Bézout’s Identity#
1 Statement:#
2 Let be integers not both zero. Then there exist integers such that .#
2.1 Proof:#
Suppose the smallest positive integer value of for some integers is . That is, .
Since and ,
it follows that .
Let where .
Then .
Because is the smallest positive integer value, we must have .
Therefore . Similarly, .
Hence .
From and , we get .
QED.
3 Converse:#
Let be integers not both zero. If is a common divisor of and , and there exist integers such that , then .
A special case: Let be integers not both zero. If there exist integers such that , then and are coprime.
4 Further Conclusions:#
For natural numbers , and an integer , with and coprime, consider the indefinite equation: where and are natural numbers. If the equation has a solution, we say can be represented by and .
Let . Since and are coprime, must be odd. Then we have the conclusion:
For any integer , exactly one of and can be represented.
That is, the representable numbers and the non-representable numbers are symmetric in the interval (about half of ). is representable, is not representable; negative numbers are not representable, numbers greater than are representable.
For example, in luogu P3951 ↗, the problem essentially asks for the value of .
Extended Euclidean Algorithm#
Euclidean Algorithm:
Proof ↗ (in Chinese)
Extended Euclidean Algorithm
Commonly used to find a set of feasible solutions to .
Derivation:
Since , , we get , .
Recursively substitute until the gcd is , then backtrack with to solve.
Proof ↗ (in Chinese)
int Exgcd(int a, int b, int &x, int &y) {
if (!b) {
x = 1;
y = 0;
return a;
}
int d = Exgcd(b, a % b, x, y);
int t = x;
x = y;
y = t - (a / b) * y;
return d;
}cppUsing pair:
pair<int, int> extgcd(int a, int b) {
if (!b) return make_pair(1, 0);
int x, y;
tie(y, x) = extgcd(b, a % b);
y -= a / b * x;
return make_pair(x, y);
}cppNon-recursive:
int gcd(int a, int b, int& x, int& y) {
x = 1, y = 0;
int x1 = 0, y1 = 1, a1 = a, b1 = b;
while (b1) {
int q = a1 / b1;
tie(x, x1) = make_tuple(x1, x - q * x1);
tie(y, y1) = make_tuple(y1, y - q * y1);
tie(a1, b1) = make_tuple(b1, a1 - q * b1);
}
return a1;
}cppMatrix method:
int exgcd(int a, int b, int &x, int &y) {
int x1 = 1, x2 = 0, x3 = 0, x4 = 1;
while (b != 0) {
int c = a / b;
std::tie(x1, x2, x3, x4, a, b) =
std::make_tuple(x3, x4, x1 - x3 * c, x2 - x4 * c, b, a - b * c);
}
x = x1, y = x2;
return a;
}cppFast Exponentiation#
Remember to use long long
int qpow(int x, int y)
{
int ans = 1;
while (y)
{
if (y % 2)
ans = (ans * x) % p;
x = (x * x) % p;
y /= 2;
}
return ans;
}cppModular Inverse#
1 Definition#
If a linear congruence equation holds, then is called the modular inverse of , denoted as .
2 Fermat’s Little Theorem:#
. This is only valid when is prime.
3 Extended Euclidean Algorithm:#
void Exgcd(ll a, ll b, ll &x, ll &y) {
if (!b) x = 1, y = 0;
else Exgcd(b, a % b, y, x), y -= a / b * x;
}
int main() {
ll x, y;
Exgcd (a, p, x, y);
x = (x % p + p) % p;
printf ("%d\n", x); // x is the inverse of a modulo p
}cpp4 Linear Recurrence Method:#
int inv[1000000];
void find_inv(int last, int p)
// Find the inverses of all numbers from 1 to last modulo p
{
inv[1] = 1; // The inverse of 1 is 1 itself
for(int i = 2; i <= last; i++)
inv[i] = (long long)(p - p / i) * (inv[p % i]) % p;
// Be careful with long long to prevent potential overflow
}cpp5 Linear Inverses for Any n Numbers#
The method above can only find inverses for to . If you need the inverses of any given numbers (), you need the following method:
First, compute the prefix products of the numbers, denote them as . Then, calculate the inverse of using fast exponentiation or the extended Euclidean algorithm, denote it as .
Since is the inverse of the product of all numbers, multiplying it by will cancel out the inverse of , yielding the inverse of the product of through , denoted as .
Similarly, we can compute all sequentially. Then, can be obtained by .
Thus, we can compute the inverses of numbers in time.
s[0] = 1;
for (int i = 1; i <= n; ++i) s[i] = s[i - 1] * a[i] % p;
sv[n] = qpow(s[n], p - 2);
// Of course, you can also use exgcd here to find the inverse, depending on personal preference.
for (int i = n; i >= 1; --i) sv[i - 1] = sv[i] * a[i] % p;
for (int i = 1; i <= n; ++i) inv[i] = sv[i] * s[i - 1] % p;cpp6 Some Practice Problems#
- Modular Inverse Practice Problems - OI Wiki ↗ (in Chinese) There are several problems here.
- B3645 Sequence Prefix Sum 2 - Luogu Link ↗ (in Chinese)
- B3646 Sequence Prefix Sum 3 - Luogu ↗ (in Chinese)
Chinese Remainder Theorem#
Used to solve systems of linear congruences.
-
Calculate the product of all moduli:
-
For the -th equation, compute
-
Compute the modular multiplicative inverse of modulo , denoted
-
P1495 【Template】Chinese Remainder Theorem (CRT) / Cao Chong Raises Pigs - Luogu ↗ (in Chinese)