【數論】乘法逆元
Definition
對於一個數 \(x\) 和一個模數 \(p\),若存在一個數字 \(y\),滿足
\[x \times y \equiv 1 \pmod p\]
則稱 \(y\) 是 \(x\) 在模 \(p\) 意義下的逆元,記做 \(x^{-1}~\equiv y \pmod p\)。
一個數字逆元在模意義下的運算中可以完全取代該數字的倒數。例如 \(\frac{x}{y}~\equiv x \times y^{-1} \pmod p\),其中 \(y^{-1}\) 代表 \(y\) 的逆元。
Algorithm
Lemma
首先需要指出的是,一個數 \(x\) 在模 \(p\) 意義下存在逆元,當且僅當 \(x\) 與 \(p\) 互質。
Proof
這裏只證明當 \(x\) 與 \(p\) 不互質時不存在逆元。對於逆元的存在性,由於下面的部分給出了逆元的構造算法,這就已經證明了在互質時逆元是存在的。
反證法,設對於任意的 \(x \in Z^+\),存在 \(x^{-1} \in Z^+\)
\[x \times x^{-1} \equiv 1 \pmod p~~~~~~(1)\]
且
\[\gcd(x,~p) = d \neq 1~~~~~~(2)\]
根據同餘的定義,\((1)\) 可以寫成:
\[x \times x^{-1} = k \times p + 1~~~~~~(3)\]
其中 \(k\) 是一個非負整數。
將 \((3)\) 的等號兩側同時除以 \((2)\) 中的 \(d\):
\[\frac{x \times x^{-1}}{d}~=~\frac{k \times p + 1}{d}~~~~~~(4)\]
整理得到
\[\frac{x}{d} \times x^{-1}~=~\frac{p}{d} \times k + \frac{1}{d}~~~~~~(5)\]
因爲 \(d = \gcd(x, p)\),所以 \(d\) 一定是 \(x\) 和 \(p\) 的因數。所以
\(\frac{x}{d}\) 和 \(\frac{p}{d}\) 都是整數,進而 \(\frac{p}{d} \times k\) 是整數,\(\frac{x}{d} \times x^{-1}\) 是整數。
而因爲 \(d \neq 1\),所以 \(\frac{1}{d}\) 一定不是整數,因此 \(\frac{p}{d} \times k + \frac{1}{d}\) 不是整數。
於是等號左側是整數,等號右側不是整數,左側一定不等於右側,產生矛盾。這就矛盾證明了 \(x\) 在模 \(p\) 意義下存在逆元僅當 \(x\) 與 \(p\) 互質。
以下介紹求逆元的算法:
求單個數字的逆元
Algorithm 1
\[x \times x^{-1}~\equiv 1 \pmod p\]
顯然可以轉化成方程
\(x \times x^-1 = 1 + kp\)
令 \(y = -k\),移項得到
\[x \times x^{-1} + y \times p = 1\]
注意到這個式子就是擴展歐幾里得算法所求的式子
\[ax + by = 1\]
只不過 \(x\) 作爲一個常數,是歐式式子裏的 \(a\),同理 \(p\) 是歐式式子裏的 \(b\)。使用擴展歐幾里得算法求解上面這個式子即可。時間複雜度 \(O(\log x)\)。
Algorithm 2
根據歐拉定理
\(x^{\phi(p)} \equiv 1 \pmod p\)
其中 \(\phi\) 爲歐拉函數,\(\phi(p)\) 表示小於 \(p\) 的正整數中與 \(p\) 互質的數的個數。
等式兩側同乘 \(x^{-1}\) 可以得到
\[x^{\phi(p) - 1} \equiv x^{-1} \pmod p\]
顯然當 \(p\) 是一個質數時,\(\phi(p) = p - 1\),這時可以 \(O(1)\) 算出 \(\phi(p) - 1\) 的值,即可用快速冪 \(O(\log x)\) 求出 \(x\) 的逆元。這個算法好寫好記,常數也較小。一般當 \(p\) 爲 int
範圍內的質數時選擇此算法。當 \(p\) 不在 int
範圍內時,由於快速冪時需要兩個 long long
相乘,會爆精度。
有關歐拉定理的證明可以看這裏
求 \(n\) 以內所有正整數模 \(p\) 的逆元
顯然,由於 \(n\) 以內所有正整數都有在模 \(p\) 意義下的逆元,所以 \(p\) 和 \(n\) 以內的所有數互質。
結論:設 \(inv_i\) 爲 \(i\) 的逆元,則有遞推式
\[inv_i \equiv \left\lfloor\frac{p}{i}\right\rfloor \times inv_{p \bmod i} \pmod p\]
邊界條件爲
\[inv_1 = 1\]
Proof
首先 \(inv_1 = 1\) 顯然成立。
對於 \(i > 1\),寫出 \(p\) 除以 \(i\) 的帶餘除法表達式:
\[p = ki + r\]
其中 \(r \in [0, i - 1]\)
等式兩側對 \(p\) 取餘數,有
\[0 \equiv ki + r \pmod p\]
移項得到
\[r \equiv -ki \pmod p\]
兩側同乘 \(i^{-1} \times r^{-1}\),整理得到
\[i^{-1} \equiv -kr^{-1} \pmod p\]
由於 \(k = \left\lfloor\frac{p}{i}\right\rfloor\),\(r = p \bmod i\),所以原式得證。
又因爲 \(r < i\),所以在計算 \(inv_i\) 時,\(inv_r\) 已經被計算完成,所以上述遞推可以完成。
證畢。
這樣做的時間複雜度顯然是 \(O(n)\)
求 \(n!\) 的逆元
因爲 \((n!)^{-1} \equiv \frac{1}{n!} \equiv \prod_{i = 1}^n n^{-1}\),所以線性篩出 \(n\) 以內所有數字的逆元時,可以順便求出 \(n!\) 的逆元。時間複雜度 \(O(n)\)
Code
Ex_Gcd
#include <iostream>
typedef long long int ll;
ll x, p;
void Ex_gcd(const ll a, const ll b, ll &X, ll &Y);
int main() {
std::cin >> x >> p;
ll a, b;
Ex_gcd(x, p, a, b);
std::cout << (a % p + p) % p << std::endl;
return 0;
}
void Ex_gcd(const ll a, const ll b, ll &X, ll &Y) {
if (b == 0) {
X = 1; Y = 0;
} else {
Ex_gcd(b, a % b, Y, X);
Y -= a / b * X;
}
}
歐拉定理
#include <iostream>
typedef long long int ll;
ll X, p;
ll mpow(ll x, ll y);
int main() {
std::cin >> X >> p;
std::cout << mpow(X, p - 2) << std::endl;
return 0;
}
ll mpow(ll x, ll y) {
ll _ret = 1;
while (y) {
if (y & 1) (_ret *= x) %= p;
y >>= 1;
(x *= x) %= p;
}
return _ret;
}
線性求逆元
這裏的 factinv
即爲階乘逆元。
#include <cstdio>
const int maxn = 3000005;
int n, p;
int inv[maxn], factinv[maxn];
int main() {
scanf("%d%d", &n, &p);
factinv[1] = inv[1] = 1;
printf("%d\n", 1);
for (int i = 2; i <= n; ++i) {
inv[i] = 1ll * (p - p / i) * inv[p % i] % p;
printf("%d\n", inv[i]);
factinv[i] = 1ll * factinv[i - 1] * inv[i] % p;
}
return 0;
}