逆元的三种计算方法
逆元的三种计算方法
- 快速幂求逆元
- 扩展欧几里得求逆元
- 线性求逆元
快速幂求逆元
前提:p为质数
由费马小定理知:在p为质数的情况下,\(a^{p-1}(mod\quad p)\)的变形为:\(a\times a^{p-2}(mod\quad p)\)
则\(a^{p-2}(mod\quad p)\)就是逆元
int inv(int x,int p) {return qmi(x, p - 2, p) % p;}
int res = inv(a, p);
if (a % p) printf("%d\n", res);
else puts("impossible");
扩展欧几里得求逆元
前提:a与p互质
\(ax \equiv 1\ (mod\ p)\)
- 将乘法逆元转化为不定方程,等价变形\(ax+py = 1\)
- 扩展欧几里得求\(ax + py = gcd(a,p)\)的解\(x\),\((x\%p+p)\%p\)即答案
int a, p, x, y;
cin >> a >> p;
exgcd(a, p, x, y);
cout << (x % p + p) % p << endl;
线性求逆元
线性求逆元
在求某个数的逆元时使用费马小定理或扩展欧几里得定理,而在求\(1\)到\(p - 1\)连续的关于\(p\)的逆元,而\(p\)较大时,时间复杂度吃不消,可以使用下面算法在\(O(n)\)的时间复杂度内解决
前提:\(p\)为素数
递推式:
\[inv[i] = (M - M\ /\ i)\ *\ inv[M\ \%\ i]\ \%\ M
\]
推导过程:
\[\text{设:}t = M/i\quad k = M\%i \\
\Rightarrow t*i + k \equiv 0 (mod\ i) \\
\Rightarrow -t * i \equiv k(mod\ M) \\
\text{将上面两式同时除以i * k得:}\
-t * inv[k] \equiv inv[i](mod\ M) \\
\text{替换t和k得:}\
inv[i] = (M - M / i) * inv[M \% i]\% M \\
\]
\(1\)的逆元为\(1\),初始化\(inv[0] = inv[1] = 1\),这样就可以通过递推式得到\(1\to p\ (mod\ p)\)的所有逆元了
inv[0] = inv[1] = 1;
for (int i = 2; i <= n; i++) {
inv[i] = (p - p / i) * inv[p % i] % p;
}