如果ax≡1(mod p),且a与p互质(gcd(a,p)=1),则称a关于模p的乘法逆元为x。(不互质则乘法逆元不存在)
求逆元的四种方法:
- 费马小定理
- 欧拉定理求逆元 (相当于费马小定理的扩展)
- 扩展欧几里德
- 递推打表
1、费马小定理 (p为素数)
费马小定理: ( a^p - p ) 是 p 的倍数,所以可推出 , 这也是更为常用的书写形式。
因为 a^(p-1) = a * a^(p-2) , 故费马小定理可写成逆元的形式,( a * a^(p-2) ) ≡ 1 (mod p)
因此 a^(p-2) 就是所求的逆元,用快速幂求出即可。
1 LL pow_mod(LL a, LL b, LL p){//a的b次方求余p 2 LL ret = 1; 3 while(b){ 4 if(b & 1) ret = (ret * a) % p; 5 a = (a * a) % p; 6 b >>= 1; 7 } 8 return ret; 9 } 10 LL Fermat(LL a, LL p){//费马求a关于b的逆元 11 return pow_mod(a, p-2, p); 12 }
2、欧拉定理求逆元
摘自大佬博客: https://www.cnblogs.com/vongang/archive/2013/06/04/3117370.html
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #include<math.h> 5 #include<algorithm> 6 #include<queue> 7 #include<stack> 8 #include<deque> 9 #include<iostream> 10 using namespace std; 11 typedef long long LL; 12 const int N = 100009; 13 14 int check[N]; 15 int prime[N]; 16 int phi[N]; 17 int inv[N]; 18 int cnt; 19 20 int fast_pow(int a,int b) 21 { 22 int ans=1; 23 while(b) 24 { 25 if(b&1) 26 ans=a*ans; 27 a=a*a; 28 b>>=1; 29 } 30 return ans; 31 } 32 33 void is_prime(int n) 34 { 35 int i,p,j; 36 cnt=0; 37 phi[1]=1; 38 for(i=2;i<=n;i++) 39 { 40 if(!check[i]) 41 { 42 prime[cnt++]=i; 43 phi[i]=i-1; 44 } 45 for(j=0;j<cnt;j++) 46 { 47 if(i*prime[j]>n) 48 break; 49 check[i*prime[j]]=1; 50 51 if(i%prime[j]==0) 52 { 53 phi[i*prime[j]]=phi[i]*prime[j]; 54 break; 55 } 56 else 57 { 58 phi[i*prime[j]]=phi[i]*(prime[j]-1); 59 } 60 } 61 } 62 } 63 int main() 64 { 65 int i,p,j,n; 66 is_prime(100); 67 scanf("%d",&p); 68 for(i=0;i<=10;i++) 69 if(i%p) 70 inv[i]=(fast_pow(i,phi[p]-1)%p); //// i关于1模p的逆元 71 else 72 inv[i]=-1; 73 for(i=0;i<=10;i++) 74 { 75 printf("%d ",inv[i]); 76 } 77 return 0; 78 }
3、扩展欧几里德
a*x=1 (mod p) 相当于 a*x-y*p=1 (y为整数,最小的x应当是y等于零的时候,所以最后的时候把y赋成0)
a*x-y*p=1 , 为了便于理解,将p写成b,
即, a*x+b*y=1。 理解为 a关于 1 模 b 的乘法逆元为 x 。
开始的 x,y,d 为任意值,但是不能等于零!!!
因为d是 gcd(a,b),所以最后给d赋值为gcd的值。若d为1,说明存在这样的x,否则不存在。
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #include<math.h> 5 #include<algorithm> 6 #include<queue> 7 #include<stack> 8 #include<map> 9 #include<set> 10 #include<vector> 11 #include<deque> 12 #include<iostream> 13 using namespace std; 14 typedef long long LL; 15 const int N = 100009; 16 17 void exgcd(LL a,LL b,LL &d,LL &x,LL &y) 18 { 19 if(b==0) 20 { 21 x=1; 22 y=0; 23 d=a; 24 return ; 25 } 26 else 27 { 28 exgcd(b,a%b,d,x,y); 29 LL mid=x; 30 x=y; 31 y=mid-a/b*y; 32 } 33 34 return ; 35 } 36 LL inv(LL a, LL b) 37 { 38 LL x,y,d; 39 exgcd(a,b,d,x,y); 40 return d == 1 ? (x+b)%b : -1; 41 42 } 43 int main() 44 { 45 LL a,p; 46 scanf("%lld%lld",&a,&p); 47 printf("%lld ",inv(a,p)); 48 return 0; 49 }
4、递推打表
令 a*x + b = p.
b * inv[b] ≡ 1 (mod p) , 将 b 替换为 p - a*x
(p - a * x) * inv[b] ≡ 1(mod p) ,即 p * inv[b] - (a * x * inv[b] ) ≡ 1(mod p)
因为 p mod p 等于零, 所以上式变为 -(a * x * inv[b]) ≡ 1(mod p)
观察 a * x + b = p 得 , 在计算机中 a = p/x , b = p%x .
故 - (p/x * inv[p % x] * x) ≡ 1(mod p)
因此 -p/x *inv[p % x] ≡ inv[x] (mod p)
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #include<math.h> 5 #include<algorithm> 6 #include<queue> 7 #include<stack> 8 #include<deque> 9 #include<iostream> 10 using namespace std; 11 typedef long long LL; 12 const int N = 100009; 13 14 int inv[N]; 15 int main() 16 { 17 int i,p,j,n; 18 scanf("%d%d",&n,&p); 19 inv[1]=1; 20 for(i=2;i<=n;i++) 21 inv[i] = LL(p-p/i)*inv[p%i]%p; 22 for(i=2;i<=n;i++) 23 printf("%d %d ",i,inv[i]); 24 25 return 0; 26 }