简单的来说,已知a和m,求a的逆元(如果存在的话等于1/a mod m)。
现分几种情况讨论。
1. m是素数(a<m)。
a的逆元必然存在。两种方法求逆元,在线用拓展欧几里得算,打表用递推。
不用费马小定理在线算逆元是因为拓展欧几里得复杂度O(logn),费马小定理复杂度O(log mod),后者比前者慢一些。
1 #include<bits/stdc++.h> 2 #define ll long long 3 #define scan(i) scanf("%d",&i) 4 #define f(i,a,b) for(int i=a;i<=b;i++) 5 #define pf printf 6 using namespace std; 7 ll exgcd(ll a,ll b,ll &x,ll &y)//扩展欧几里得算法 8 { 9 if(b==0) 10 { 11 x=1,y=0; 12 return a; 13 } 14 ll ret=exgcd(b,a%b,y,x); 15 y-=a/b*x; 16 return ret; 17 } 18 ll getinv(int a,int mod)//求a在mod下的逆元,不存在逆元返回-1 19 { 20 ll x,y; 21 ll d=exgcd(a,mod,x,y); 22 return d==1?(x%mod+mod)%mod:-1; 23 } 24 int main(){ 25 cout<<getinv(3,7); 26 return 0; 27 }
1 #include<bits/stdc++.h> 2 #define ll long long 3 #define scan(i) scanf("%d",&i) 4 #define f(i,a,b) for(int i=a;i<=b;i++) 5 #define pf printf 6 using namespace std; 7 const int M=10000; 8 ll inv[M+5]; 9 void getInv(ll mod)//离线打表求逆元 10 { 11 inv[1]=1; 12 for(int i=2;i<mod;i++) 13 inv[i]=(mod-mod/i)*inv[mod%i]%mod; 14 } 15 int main(){ 16 getInv(7); 17 cout<<inv[3]; 18 return 0; 19 }
2. m不是素数
当gcd(a,m)!=1时,a的逆元不存在。其他情况下,打表就不行了,只能在线算了。具体方法是用上面的拓展欧几里得求,复杂度O(logn),下面贴个费马小定理的板子吧~~
ll qkpow(ll a,ll p,ll mod) { ll t=1,tt=a%mod; while(p) { if(p&1)t=t*tt%mod; tt=tt*tt%mod; p>>=1; } return t; } ll getInv(ll a,ll mod) { return qkpow(a,mod-2,mod);//这里本来该放的是欧拉函数,可以详见本人的其他博客~ }