乘法逆元是数论中重要的内容,也是 OI 中常用到的数论算法之一。所以,如何高效的求出乘法逆元是一个值得研究的问题。
这里我们只讨论当模数为素数的情况,因为如果模数不为素数,则不一定每个数都有逆元。
定义
在 的意义下我们把 的乘法逆元写作 。 乘法逆元有如下的性质:
乘法逆元的一大应用是模意义下的除法,除法在模意义下并不是封闭的,但我们可以根据上述公式,将其转化为乘法。
费马小定理
要求 为素数。
上述公式可变形为
由乘法逆元的定义, 即为 的乘法逆元。
使用快速幂计算 ,总时间复杂度为 。
代码
inline int pow(const int n, const int k) {
long long ans = 1;
for (long long num = n, t = k; t; num = num * num % MOD, t >>= 1) if (t & 1) ans = ans * num % MOD;
return ans;
}
inline int inv(const int num) {
return pow(num, MOD - 2);
}
扩展欧几里得
扩展欧几里得(EXGCD)算法可以在 的时间内求出关于 、 的方程
的一组整数解
当 为素数时,,此时有
时间复杂度为 。
代码
void exgcd(const int a, const int b, int &g, int &x, int &y) {
if (!b) g = a, x = 1, y = 0;
else exgcd(b, a % b, g, y, x), y -= x * (a / b);
}
inline int inv(const int num) {
int g, x, y;
exgcd(num, MOD, g, x, y);
return ((x % MOD) + MOD) % MOD;
}
递推法
设 ,(,),则有
两边同时乘上 ,得
移项,得
即
我们可以利用这个式子进行递推,边界条件为 ,时间复杂度为 。
代码
inv[1] = 1;
for (int i = 2; i <= MAXN; i++) inv[i] = ((-(MOD / i) * inv[MOD % i]) % MOD + MOD) % MOD;