首先,我想说——学会了乘法逆元,就相当于打开了模世界的大门!
定义
若在mod p意义下,对于一个整数a,有a*b≡1(mod p),那么这个整数d即为a的 乘法逆元,同时a也为d的乘法逆元
(所以,我们可以根据上述公式,将除法转化为乘法。)
性质
-
存在唯一性
证明:假设存在a′′也满足a×a′′≡1(mod p) 则有a×a′≡a×a′′(mod p) ∴a′ ≡ a′′(modp) 矛盾。
-
完全积性函数 (把 a 的逆元表示为 inv[a] )
∀a,b∈N,inv[a]×inv[b]≡inv[a×b](mod p) 证明: ∵ a×inv[a]≡1(mod p),b×inv[b]≡1(mod p) ∴(a×b)×(inv[a]×inv[b]) ≡ 1×1 = 1(modp) 由【性质1】知(a×b)的逆元只有一个。 ∴inv[a×b]≡inv[a]×inv[b](mod p)
-
a*inv[b]=a/b(mod p) (灰常重要的一个结论哦)
证明: ∵ b×inv[b] ≡ 1(mod p) ∴ a × b × inv[b] ≡ a (mod p) ∴ a × inv[b] ≡ a / b(mod p)
求解
- 费马小定理(p是质数)
a(p-1) ≡ 1(mod p)
∴ a × a(p-2) ≡ 1(mod p)
∴ a(p-2) 是 a 的乘法逆元
const int p=1e9+7; // 一个质数
Fermat(int a,int x) // x=p-2 // 一个快速幂啊
{
long long s=1;
while(x)
{
if(x&1) s=(s+a)%p;
a=(a*a)%p;
x=x>>1;
}
}
-
扩展欧几里得算法 (p可以是任意整数)
ax ≡ 1 (mod p) 可以等价的转化为 ax−py=1 设当前状态为gcd(a,b) ① 当b = 0时,gcd(a,b)= a ∴ x=1,y=0; ② 当b ≠ 0时,先执行gcd(b,a mod b) ∴ x × b + y (a mod b) = 1 ∴ y × a + [ x−(a / b)y ] b = 1 然后构造x′,y′作为新的x,y满足ax+by=1 那么就可以使 x′=y,y′= x−(a / b)y
int a,p,x,y;
void exgcd(int a,int b)
{
int xx,yy;
if(!b)
{
x=1,y=0;
return;
}
exgcd(b,a%b);
xx=x , yy=y;
x=xx , y=yy*(a/b)-xx;
x=x%p , y=y%p;
}
int main()
{
scanf("%d%d",&a,&p);
exgcd(a,p);
printf("%d\n",(x+p)%p);
return 0;
}
- 欧拉函数
ϕ(m) 表示小于m且与m互质的正整数的个数。
如果a和m互质,则有aϕ(m) ≡ 1(mod m )
∴ a × a(ϕ(m) - 1) ≡ 1(mod m )
在m为质数的情况下,ϕ(m) = m−1,即为费马小定理。
- 对于任意整数n,可以将它分解 n=p1k1∗p2k2∗p3k3…pmkm,其中pi为质数其中ϕ(n)=ϕ(p1k1)∗ϕ(p2k2)…ϕ(pmkm)
最后转化为ϕ(n)=n∗∏(pi − 1) / pi
int vis[N],p[N],n,tot,phi[N];
void eurler_phi()
{
vis[1]=1;
phi[1]=1;
for(int i=2; i<=n; i++)
{
if(!vis[i])
{
p[tot++]=i;
phi[i]=i-1;
}
for(int t=0; t<tot; t++)
{
int j=p[t]*i;
if(j>n) break;
vis[j]=1;
phi[j]=phi[p[t]]*phi[i];
if(i%p[t]==0)
{
phi[j]=p[t]*phi[i];
break;
}
}
}
}
int main()
{
scanf("%d",&n);
eurler_phi();
for(int i=1; i<=n; i++) printf("%d\n",phi[i]);
}
- 递推法 (1~n在mod p 意义下的逆元)
∵ 1-1 ≡ 1 (mod p)
∴ 设 p = k × i + r ≡ 0 (mod p)
∴ 两边同时乘上 r-1 + i-1 , 得:
k × r-1 + i-1 ≡ 0 (mod p)
∴ i-1 ≡ - k × r-1 (mod p)
∴ i-1 ≡ - [ p / i ] × (p mod i)-1
inv[i]]=1;
inv[i]=(p-p/i)*inv[p%i]%p;
5. 欧拉筛法 (批量) (a 为合数,完全积性函数O(1)解决。)
完全积性函数
int p;
int vis[N],pri[N];
int inv[N];
int ksm(int a,int x) // 快速幂
{
long long s=1;
while(x)
{
if(x&1) s=(s+a)%p;
a=(a*a)%p;
x=x>>1;
}
}
int main( )
{
scanf("%d",&p);
vis[1]=1,inv[1]=1;
for(int i=2; i<p; i++)
{
if(!vis[i]) pri[++pri[0]]=i,inv[i]=mi(i,p-2);
for(int j=1; j<=pri[0]; j++)
{
if(i*pri[j]>=p) break;
inv[i*pri[j]]=inv[i]*inv[pri[j]];
if(i%pri[j]==0) break;
}
}
for(int i=1; i<p; i++)
printf("inv[%d] = %d\n",i,inv[i]);
return 0;
}
总结
#include<bits/stdc++.h>
using namespace std;
int inv[1000010];
int ksm(int a,int b,int mod)
{
int ans=1;
while(b)
{
if(b&1) ans=(ans*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return ans;
}
int exgcd(int a,int b,int &x,int &y)
{
if(!b)
{
x=1;
y=0;
return a;
}
int GCD=exgcd(b,a%b,x,y);
int tmp=x;
x=y;
y=tmp-a/b*y;
return GCD;
}
int inv1(int a,int mod) //扩展欧几里得
{
int x,y;
int d=exgcd(a,mod,x,y);
if(d==1) return (x%mod+mod)%mod;
return -1;
}
int inv2(int a,int mod) //费马小定理
{
return ksm(a,mod-2,mod);
}
void inv3(int mod)//线性递推求逆元
{
inv[1]=1;
for(int i=2; i<=mod-1; i++)
{
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
cout<<inv[i]<<" ";
}
}
int main()
{
int n,mod;
scanf("%d%d",&n,&mod);
printf("%d\n",inv1(n,mod));
printf("%d\n",inv2(n,mod));
printf("%d\n",inv3(mod));
}
给我自己鼓掌