扩展欧几里得求逆元
求 \(a*a^{-1}≡1(mod p)\)
即求 \[a*a^{-1}+k*p=1\]
因为
p是质数,\(a<p\)
所以 \(gcd(a,p)=1\)
进一步转化为\(a*a^{-1}+k*p=gcd(a,p)\)
恰好的,\(exgcd4在求解\)gcd\(的时候正好可以求出一组解\)x,y$
解得的\(x\)即为\(4a\)在\(%p\)意义下的逆元
复杂度就当做O(logn)吧
附带\(exgcd\)的证明
求解\(a*x+b*y=gcd(a,b)\)的一组解
由辗转相除法容易知道
\(gcd(a,b)=gcd(b,a%b)\)
所以
\(b*x+a%b*y=gcd(b,a%b)\)
和
\(a*x+b*y=gcd(a,b)\)
是相等的
即\(a*x+b*y=b*x+a%b*y\)
a%b=a-[a/b]*b
//([]就是计算机中的整除)
\(a*x+b*y=b*x+(a-a/b*b)*y\)
去括号
\(a*x+b*y=b*x+a*y-[a/b]*b*y\)
结合
\(a*x+b*y=b*x+a*y-[a/b]*b*y\)
\(a*x+b*y=a*y+b*(x-[a/b]*y)\)
易得
\(x=y\)
\(y=x-[a/b]*y\)
void exgcd(int a,int b,int &x,int &y)
{
if (!b)
{
x=1;y=0;
return;
}
exgcd(b,a%b,x,y);
int tmp=x;
x=y;
y=tmp-a/b*y;
}
int inv_exgcd(int a,int b)
{
int x,y;
exgcd(a,b,x,y);
while(x<0) x+=b;
return x;
}
费马小定理求逆元
前提条件:当p为质数的情况下
费马小定理
\(a^{p-1}≡1(mod p)\)
变形一下
\(a*a^{p-2}≡1(mod p)\)
然后我们就很开心的求得了a的逆元啦
逆元就是\(a^{p-2}\)
\(a\)的\(p-2\)次方可以快速幂O(logn)求出
int f_pow(int a,int b,int p)
{
int ans=1;
while(b) {
if(b&1) ans*=a,ans%=p;
a*=a;
a%=p;
b>>=1;
}
return ans;
}
int inv_feima(int a,int p)
{
return f_pow(a,p-2,p);
}
线性求逆元
前提条件:当p为质数的情况下
线性求逆元,两种方法,这里介绍只一种
(那一种求阶乘的在代码量和理解和常数在我感觉下都不如我介绍的优秀,不再介绍)
设\(p=k*i+r\)
\(k=p/i,r=p%i\)
\(p≡0(mod p)\)
\(k*i+r≡0(mod p)\)
\(r≡-k*i(mod p)\)
两边同乘\(i^{-1},r^{-1}(在mod p意义下)\)
得到
\(i^{-1}≡-k*r^{-1}(mod p)\)
代入k和r的值
\(i^{-1}≡-p/i*(p%i)^{-1}(mod p)\)
为了求得的逆元是正数,变一下
\(i^{-1}≡(p-p/i)*(p%i)^{-1}(mod p)\)
因为\((p%i)^{-1}\)在我们之前就求出来了,所以就我们开心的就求出i的逆元啦
而且代码炒鸡剪短
int inv[1011111];
void get_inv(int p)
{
inv[0]=inv[1]=1;
for(int i=2;i<p;++i)
inv[i]=(p-p/i)*inv[p%i]%p;
}
如果我写错了,欢迎评论提出