逆元的三种求法
费马小定理,扩展欧几里得,递推求阶乘逆元
逆元
对于一个实数
如果存在一个
使得
,我们就把这个
叫做
的逆元,记做
。
在一般数学中,我们所说的逆元就是倒数。
但是在数论中,如果一个数字 存在一个对 的逆元 ,就可以写成 的形式(此处 与 互质,若不互质,则不存在逆元)。
逆元的作用
我们知道 取余 的性质:
对于基本的四种运算而言,加减乘都符合“分配率”,唯独除法不满足。
上面这种运算是错误的!
如果要实现这种运算,就要把除法转化为乘法,假设
是
关于
的逆元。
可以转化为
。
逆元求法
费马小定理
费马小定理:假设
是一个质数,且
,那么
。
我们也可以的得到一个费马小定理的特例:假设
是一个整数,且
,那么
。
根据费马小定理
,可以发现
也成立。
是不是很像上面说到的逆元的形式:
,
是
关于
的逆元。
那根据费马小定理也可得知
是
关于
的逆元。
所以求
的逆元,就直接用快速幂求
就可以了。
LL power(LL a, int x) {
LL ans = 1;
while(x) {
if(x&1) ans = (ans * a) %mod;
a = (a * a) %mod;
x >>= 1;
}
return ans;
}
LL inv(LL a) {
return power(a, mod - 2);
}
扩展欧几里得
扩展欧几里得:
的解一定存在。
当我们要求
关于
的逆元时,若逆元存在,则
。假设逆元为
,即:
。
我们可以展开一下变成
,由于
可正可负。
所以我们可以得到
,其实就是
。
所以我们用扩展欧几里得求出一个最小的
就是
关于
的一个逆元啦。
我们来试着解这个欧几里得吧!
现在已经有了
了。我们想试着求出一个特解
。
根据欧几里得算法我们可以知道 。
而且我们可以看出
由此我们可得:(由于两边的
,
值不同,我们用
和
进行区分)
我们想要把式子化简一下,可以从
入手,即
。
所以我们可以化简得到:
移项:
系数相等,所以我们可以解得
根据欧几里得算法,我们一直递归下去,总会到要一个最终位置的
。
所以式子变成了
。此时我们取一个特解
,
。然后往回推,就可以得到一开始的那个
。
此时解出来的
就是
关于
的一个逆元。
void exgcd(LL a, LL b, LL &x, LL &y) {
if (b == 0) {
x = 1;y = 0;
} else {
exgcd(b, a%b, y, x);
y -= (a/b) * x;
}
}
递推求阶乘逆元。
我们经常会用到阶乘的逆元,我们可以考虑用费马小定理先求出最大那个阶乘的逆元,然后再往回推,直接看代码再解释。
void init() {
fact[0] = 1;
for (int i = 1; i < maxn; i++) {
fact[i] = fact[i - 1] * i %mod;
}
inv[maxn - 1] = power(fact[maxn - 1], mod - 2);
for (int i = maxn - 2; i >= 0; i--) {
inv[i] = inv[i + 1] * (i + 1) %mod;
}
}
我们可以假设把
的逆元表示为
。
我们要求
的逆元,我们可以考虑给
乘上一个
把他变为
。
即
因此
是
关于
的一个逆元。