一.模意义下的除法
我们在平时计算中可能遇到一种情况:$\frac{3}{4}\times 20$ 这个算式,在刚开始计算时,出现了分数(在模意义下是不允许的),但最终结果仍然是个整数,所以这个式子在模意义下依然是合法的。遇到这种情况,我们能否找到一种类似于除法的操作,使其在模意义下可以代替除法?
二.逆元
我们需要一种特殊的数,在“除以a”的时候,我们把“除以a”替换成乘这个特殊的数,依然能得到正确结果。
在有理数下,这个“特殊的数”,显然是$\frac{1}{x}$,那么在模意义下这个“特殊的数”又是多少?
先来看几个例子吧:
$\frac{3}{4}\times 20\equiv 4 \: \: (mod\: \: \: 11)$
我们可以将“除以4”替换成“乘3”,则算式变为:
$3\times 3\times 20\equiv 4\, \, (mod\, \, 11)$
在这个算式中的“特殊的数”就是3
$\frac{4}{7}\times 14\equiv 8\: \: (mod\: \: \: 19)$
我们可以将“除以7”替换成“乘11”,则算式变为:
$4\times 11\times 14\equiv 8\, \, (mod\, \, 19)$
在这个算式中的“特殊的数”就是11
对于一个模数p和一个除数n,一般可以找到一个特殊的数x,可以起到除法的效果
我们称这个特殊的数为“n在模p意义下的逆元”。一个数x的逆元一般记作inv(x)。
逆元的性质:
对于一个x和x的逆元inv(x),满足
$x \times inv(x) \equiv 1\, \, (mod\, p)$
问题:什么时候找不到逆元?
显然当除数x和模数p不互质时,永远找不到x在模p意义下的逆元
三.如何找一个数的逆元?
1.费马小定理
当模数p为素数(质数)时,我们可以用费马小定理来求逆元。
费马小定理:$x^{p-1}\equiv 1\, \, (mod\, \, p)$
根据逆元的性质我们有:
$x \times inv(x) \equiv 1\, \, (mod\, p)$
又据费马小定理:
$x^{p-1}\equiv 1\, \, (mod\, \, p)$
故:
$x \times inv(x) \equiv x^{p-1}\, \, (mod\, p)$
式子两边同时除以x,得到:
$inv(x) \equiv x^{p-2}\, \, (mod\, p)$
x的p-2次方可以用快速幂来求
Code:
#include<iostream>
#include<cstdio>
using namespace std;
inline int read(){
register int x=0,v=1,ch=getchar();
while(!isdigit(ch)){if(ch=='-')v=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^'0');ch=getchar();}
return x*v;
}
inline int ksm(int x,int p,int Mod){
register int res=1;
while(p){
if(p&1)res=res%Mod*x%Mod;
p>>=1;
x*=x%Mod;
}
return res;
}
int x,p;
int main(){
x=read(),p=read();
printf("%d\n",ksm(x,p-2,p));
return 0;
}
2.exgcd
我们有 $a\times inv(a)\equiv 1\, \, (mod\, \, p)$
已知a,p求inv(a)的值
方程中有恒等号,可以先进行变式,得到
$a\times inv(a)= kp+1\, \, (mod\, \, p)$
其中,k为任意值(因为在模意义下加上任意整数倍的模数,式子还是成立的)
将方程移项,得到:
$a\times inv(a)-kp=1\, \, (mod\, \, p)$
因为k为任意值,所以将k取负值后,得到
$a\times inv(a)+kp=1\, \, (mod\, \, p)$
因为一个数有逆元,需要满足此数与模数互质(即满足$a\perp p $),所以方程右边可以写成
$a\times inv(a)+kp=gcd(a,p)\, \, (mod\, \, p)$
这样我们就将式子转化成了exgcd可以解决的问题
Code:
#include<iostream>
#include<cstdio>
using namespace std;
inline int read(){
register int x=0,v=1,ch=getchar();
while(!isdigit(ch)){if(ch=='-')v=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^'0');ch=getchar();}
return x*v;
}
#define pr pair<int,int>
#define x tmp.first
#define y tmp.second
inline pr Exgcd(int a,int b){
if(b==0)return pr(1,0);
pr tmp=Exgcd(b,a%b);
return pr(y,x-a/b*y);
}
inline void Getans(int a,int p){
pr tmp=Exgcd(a,p);
printf("%d\n",(x%p+p)%p);//注意exgcd可能会出负数,C++在负数%的时候还会出负数,需要特殊处理一下
}
int a,p;
int main(){
a=read(),p=read();
Getans(a,p);
return 0;
}
3.线性递推逆元
这个操作可以在O(p)的时间复杂度内处理出$\left [ 1 ,\right p-1]$中所有数在模p意义下的逆元
递推式:$inv(i)=(p-p/i)\times inv(p\, \, mod\, \, i)\, \, mod\, \, p$
证明过程:
假设该式成立,则有
$i\times inv(i)=i\times (p-\lfloor\frac{p}{i}\rfloor)\times inv(p\, \, mod\, \, i)\, \, mod\, \, p$
变式后可得
$i\times inv(i)=(ip-i\times\lfloor \frac{p}{i} \rfloor ))\times inv(p\, \, mod\, \, i)\, \, mod\, \, p$
根据随时取模性质,得到
$i\times inv(i)=(p\, \, mod\, \, i)\times inv(p\, \, mod\, \, i)\, \, mod\, \, p$
显然,这个式子是成立的
Code:
#include<iostream>
#include<cstdio>
using namespace std;
inline int read(){
register int x=0,v=1,ch=getchar();
while(!isdigit(ch)){if(ch=='-')v=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^'0');ch=getchar();}
return x*v;
}
const int MAX=105;
int inv[MAX],p,n;
int main(){
n=read();
p=read();
inv[1]=1;
for(register int i=2;i<=p-1;++i){
inv[i]=(p-p/i)*inv[p%i]%p;
}
for(register int i=1;i<=n;++i){
printf("%d ",inv[i]);
}
printf("\n");
return 0;
}
有什么需要修改的地方请您留言给作者,谢谢
希望这篇文章对大家有帮助