Modular Arithmetic
การคำนวณแบบ Modular สำหรับจำนวนที่มีขนาดใหญ่
Modular Arithmetic
ในการแข่งขัน Competitive Programming มักพบโจทย์ที่คำตอบมีขนาดใหญ่มาก จึงต้องให้คำตอบเป็น “mod ” หรือค่าอื่นๆ
พื้นฐาน Modulo
การ mod คือการหาเศษจากการหาร
หมายความว่า โดย
const int MOD = 1e9 + 7;
int mod(int x) {
return ((x % MOD) + MOD) % MOD;
}
⚠️ สำคัญ: ใน C++ การ mod จำนวนลบอาจได้ค่าลบ เช่น
-7 % 5 = -2ต้องบวก MOD กลับ
คุณสมบัติของ Modular Arithmetic
การบวกและลบ
long long addMod(long long a, long long b) {
return ((a % MOD) + (b % MOD)) % MOD;
}
long long subMod(long long a, long long b) {
return ((a % MOD) - (b % MOD) + MOD) % MOD;
}
การคูณ
long long mulMod(long long a, long long b) {
return ((a % MOD) * (b % MOD)) % MOD;
}
การหาร (ต้องใช้ Modular Inverse)
โดย คือ modular inverse ของ
Modular Exponentiation
การยกกำลังแบบ mod อย่างมีประสิทธิภาพ
Binary Exponentiation
คำนวณ ใน
long long power(long long base, long long exp, long long mod) {
long long result = 1;
base %= mod;
while (exp > 0) {
if (exp & 1) { // exp เป็นเลขคี่
result = (result * base) % mod;
}
base = (base * base) % mod;
exp >>= 1; // exp /= 2
}
return result;
}
หลักการ
- ถ้า : ผลลัพธ์ =
- ถ้า เป็นเลขคู่:
- ถ้า เป็นเลขคี่:
ตัวอย่าง:
Modular Inverse
คือจำนวนที่ทำให้
วิธีที่ 1: Fermat’s Little Theorem
ถ้า เป็นจำนวนเฉพาะ:
long long modInverse(long long b, long long mod = MOD) {
return power(b, mod - 2, mod);
}
// การหาร a / b mod m
long long divMod(long long a, long long b) {
return mulMod(a, modInverse(b));
}
วิธีที่ 2: Extended Euclidean Algorithm
ใช้ได้กับ ทั่วไปที่
long long extGcd(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 = extGcd(b, a % b, x1, y1);
x = y1;
y = x1 - (a / b) * y1;
return g;
}
long long modInverseExt(long long b, long long mod = MOD) {
long long x, y;
extGcd(b, mod, x, y);
return (x % mod + mod) % mod;
}
วิธีที่ 3: Pre-compute Inverse 1 ถึง n
const int MAXN = 1e6 + 5;
long long inv[MAXN];
void precomputeInverse(int n) {
inv[1] = 1;
for (int i = 2; i <= n; i++) {
inv[i] = (MOD - (MOD / i) * inv[MOD % i] % MOD) % MOD;
}
}
Factorial และ Combinations
Pre-compute Factorial
const int MAXN = 1e6 + 5;
long long fact[MAXN], invFact[MAXN];
void precompute() {
fact[0] = 1;
for (int i = 1; i < MAXN; i++) {
fact[i] = (fact[i-1] * i) % MOD;
}
invFact[MAXN-1] = modInverse(fact[MAXN-1]);
for (int i = MAXN-2; i >= 0; i--) {
invFact[i] = (invFact[i+1] * (i+1)) % MOD;
}
}
Binomial Coefficient (nCr)
long long nCr(int n, int r) {
if (r < 0 || r > n) return 0;
return (fact[n] * invFact[r] % MOD) * invFact[n-r] % MOD;
}
Lucas’ Theorem
สำหรับคำนวณ เมื่อ มีขนาดใหญ่มากและ เป็นจำนวนเฉพาะ
โดย และ
long long lucas(long long n, long long r, long long p) {
if (r == 0) return 1;
return (lucas(n / p, r / p, p) * nCr(n % p, r % p)) % p;
}
ตัวอย่างการใช้งาน
โจทย์: หา
int main() {
long long n = 1000000000;
cout << power(2, n, MOD) << endl;
return 0;
}
โจทย์: หาจำนวนวิธีเลือกของ r ชิ้นจาก n ชิ้น
int main() {
precompute();
int n = 100000, r = 50000;
cout << nCr(n, r) << endl;
return 0;
}
โจทย์: หา
int main() {
long long a = 10, b = 3;
long long result = divMod(a, b);
// result คือค่าที่ทำให้ (result * 3) % MOD == 10
cout << result << endl;
return 0;
}
ทำไมต้อง ?
- เป็นจำนวนเฉพาะ - ทำให้หา modular inverse ได้ง่าย
- พอดี 32-bit -
- คูณกันไม่ล้น 64-bit -
สรุป
| Operation | Formula | Code |
|---|---|---|
| บวก | (a + b) % MOD | |
| ลบ | (a - b + MOD) % MOD | |
| คูณ | (a * b) % MOD | |
| ยกกำลัง | power(a, n, MOD) | |
| หาร | a * modInverse(b) % MOD | |
| Inverse | power(a, MOD-2, MOD) | |
| nCr | fact[n] * invFact[r] * invFact[n-r] |
💡 Tip: Pre-compute factorial และ inverse factorial ไว้ล่วงหน้าถ้าต้องคำนวณ nCr หลายครั้ง