如果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 returnret;9 }10 LL Fermat(LL a, LL p){//费马求a关于b的逆元
11 return pow_mod(a, p-2, p);12 }
View Code
2、欧拉定理求逆元
1 #include
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 using namespacestd;11 typedef long longLL;12 const int N = 100009;13
14 intcheck[N];15 intprime[N];16 intphi[N];17 intinv[N];18 intcnt;19
20 int fast_pow(int a,intb)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 returnans;31 }32
33 void is_prime(intn)34 {35 inti,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;jn)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 intmain()64 {65 inti,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
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #include
11 #include
12 #include
13 using namespacestd;14 typedef long longLL;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 intmain()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
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 using namespacestd;11 typedef long longLL;12 const int N = 100009;13
14 intinv[N];15 intmain()16 {17 inti,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