如果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\n",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 \n",i,inv[i]); 24 25 return 0; 26 }