求逆元的四种方法

 

如果ax1(mop),且a与p互质(gcd(a,p)=1),则称a关于模p的乘法逆元为x。(不互质则乘法逆元不存在)

求逆元的三种方法:

  1. 费马小定理
  2. 欧拉定理求逆元 (相当于费马小定理的扩展)
  3. 扩展欧几里德
  4. 递推打表

1、费马小定理 (p为素数)

 

费马小定理:  ( a^p - p ) 是 p 的倍数,所以可推出 a^{​{p-1}}\equiv 1{\pmod  {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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

转载于:https://www.cnblogs.com/daybreaking/p/9342060.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值