一,逆元的定义
当 ax≡1(modb), x即为 a 在mod b 意义下的逆元。
逆元的数学符号是 inv ,a 在mod b 意义下的逆元记作 inv(a,b)。注意不要写反了。
简单来说逆元就是在mod某个数意义下的倒数例如5x≡1(mod3)x=2是满足10=1(mod3)所以称2是5在mod3意义下的逆元、
二,逆元的应用
那么逆元有什么用呢?
(a + b) % p = (a%p + b%p) %p (对)
(a - b) % p = (a%p - b%p) %p (对)
(a * b) % p = (a%p * b%p) %p (对)
(a / b) % p = (a%p / b%p) %p (错)
在求余的过程中我们发现只有除法是不能分开运算的,而当a过大时,在计算除法过程中可能会造成比较大的精度损失,所以对于这种情况我们一般会把式子转换成那么(a / b) % p = (a * inv(b) ) % p = (a % p * inv(b) % p) % p来进行计算。这样就解决了除法不能分开计算的问题。
需要注意只有a和p互质,a才有关于p的逆元
三.逆元的两种简单求法
1,费马小定理
费马小定理(Fermat’s little theorem)是数论中的一个重要定理,在1636年提出。如果p是一个质数,而整数a不是p的倍数,则有a^(p-1)≡1(mod p)。 [1]
费马小定理规定了p一定为一个质数,所以a和p一定互质
那么双方在modp的意义下同时除a可得
a^(p-2) ≡1/a (mod p)
也就是a^(p-2) ≡ inv(a) (mod p)
所以inv(a) = a^(p-2) (mod p)
代码需要用快速幂实现,复杂度大概为o(logn)
LL pow(LL a, LL b, LL p){//a的b次方求余p
LL x = 1;
while(b){
if(b & 1) x = (x * a) % p;
a = (a * a) % p;
b >>= 1;
}
return ret;
}
LL Fm(LL a, LL p){//费马求a关于b的逆元
return pow(a, p-2, p);
}
2.扩展欧几里得算法求逆元
如果gcd(a,p)=1;
那么就有ax+py=1
双方同时modp
就有ax≡1(modp)
因为py是p的倍数全部约掉了
此时x就是a的逆元
所以只需解出该情况下的扩展欧几里得方程的解问题就解决了
#include<cstdio>
typedef long long LL;
void exgcd(LL a,LL b,LL &x,LL &y,LL &d){
if(b==0) d=a,x=1,y=0;
else exgcd(b,a%b,x,y,d),t=x,x=y,y=t-a/b*y;
}
LL inv(LL t, LL p){//如果不存在,返回-1
LL d, x, y;
ex_gcd(t, p, x, y, d);
return d == 1 ? (x % p + p) % p : -1;
}
int main(){
LL a, p;
while(~scanf("%lld%lld", &a, &p)){
printf("%lld\n", inv(a, p));
}
}