什么是逆元
数论倒数,又称逆元
a*x = 1 (mod p) (只有a,p互为质数,a才有关于p的逆元)
a与x关于模p互为逆元
求取逆元的方法
1.费马小定理
仅当p为质数时可以使用
a^(p-1) ≡1 (mod p)
两边同除以a
这里以inv(a)来表示a的逆元
a^(p-2)≡inv(a) (mod p)
则inv(a)= a^(p-2) (mod p)
使用快速幂即可求出
#include <bits/stdc++.h>
using namespace std;
long long quickmi (long long a,long long b,long long c ) {
if (b==1) {
return a%c;
} else {
if (b%2==0) {
long long result=quickmi(a,b/2,c);
return result*result%c;
} else {
long long result=quickmi(a,b/2,c);
result=result*result%c;
result=result*a%c;
return result;
}
}
}
int main() {
long long a,b,c,x;
cin>>a>>c;
x=quickmi(a,c-2,c);
cout<<x<<endl;
return 0;
}
2.递归
仅当p为质数时可以使用
inv(a) = (p - p / a) * inv(p % a) % p
然后一直递归到1为止,因为1的逆元就是1
#include <bits/stdc++.h>
using namespace std;
long long inv(long long t, long long p) {//求t关于p的逆元,注意:t要小于p,最好传参前先把t%p一下
return t == 1 ? 1 : (p - p / t) * inv(p % t, p) % p;
}
int main(){
long long a, p;
while(~scanf("%lld%lld", &a, &p)){
printf("%lld\n", inv(a%p, p));
}
return 0;
}
3.拓展欧几里得算法
转自拓展欧几里得
p为任何数据均可使用
当到达递归边界的时候,b==0,a=gcd(a,b) 这时可以观察出来这个式子的一个解:a*1+b*0=gcd(a,b),x=1,y=0,注意这时的a和b已经不是最开始的那个a和b了,所以我们如果想要求出解x和y,就要回到最开始的模样。
假设当前我们在求的时a和b的最大公约数,而我们已经求出了下一个状态:b和a%b的最大公因数,并且求出了一组x1和y1使得 b*x1+(a%b)*y1=gcd
这时我们可以试着去寻找这两个相邻状态的关系:
a%b=a-(a/b)*b;
b*x1 + (a-(a/b)*b)*y1
= b*x1 + a*y1 – (a/b)*b*y1
= a*y1 + b*(x1 – a/b*y1) = gcd 发现 x = y1 , y = x1 – a/b*y1
这时即可求解
#include <bits/stdc++.h>
using namespace std;
long long x,y;
long long exgcd(long long a,long long b) {
if (b==0) {
x=1;
y=0;
return a;
}
long long z=exgcd(b,a%b);
long long temp=y;
y=x-a/b*y;
z=temp;
return z;
}
int main()
{
long long a,b;
cin>>a>>b;
long long z=exgcd(a,b);//x为a,b的最大公因数
x=(x%b+b)%b;
cout<<x<<endl;
return 0;
}