乘法逆元
逆元
取模相信都会了
\(a\)%\(b=…………\)
就这样而已嘛。
但是有个条件,那就是\(a,\) \(b\)都是整数。
那如果我把取模运算扩展到有理数呢?
\(a/b\)%\(mod=?\)
好我知道你傻了,不然你也不会来看这篇博客。
那就直接上吧。
BEGIN:
乘法逆元有多种方法,我们可以根据情况选择最合适的。
偶,忘了说逆元是啥了
若\(a·x≡1(mod\) \(b)\)
则称\(x\)是\(a\)在\(mod\) \(b\)意义下的逆元。
为什么这么定义呢?
因为啊:
先转换一下:\(a/b\) \(mod\) \(p\)\(-->\)\(a·b^{-1}\) \(mod\) \(p\)
∵\(b·b^{-1}≡1(mod\) \(p)\)(显然);
并且假设我们已经求了一个 \(x\) 使\(b·x≡1(mod\) \(p)\)
那么\(b^{-1}≡x(mod\) \(p)\)
则原式就可以写成\(a·x\) \(mod\) \(p\)
现在一个乘法的取模运算你肯定会了
那么问题就在于这个 \(x\) (整数昂)怎么求。
REAL BEGIN
好,首先来看一看
\(b·x≡1(mod\) \(p)\)你能想到什么?
没错!!就是扩欧!!
再转换一下:\(bx+py=1\)
\(b\)已知,\(p\)已知,求\(x\)
void exgcd(int a, int b, int &x, int &y) {
if(b) exgcd(b, a%b, y, x), y-=a/b*x;
else x=1, y=0;
}
int b, p, x, y;
int main(){
cin>>b>>p;//求b在mod p意义下的逆元
exgcd(b, p, x, y);
x=(x%p+p)%p;
cout<<x;
}
这就是单个数逆元的\(log\)求法。
当然,如果数很多的话咋办内?
其实有线性方法。
线性:
求\(i\)在\(mod\) \(p\)意义下的的逆元(因为逆元\(x≡i^{-1}\),接下来所说的\(i^{-1}\)就是 \(i\) 的逆元)
设$$k·i+r=p$$
两边同乘 \(r^{-1}i^{-1}\)
得到:
由 \(k·i+r=p\) 得:
代入得:
\((p\)%\(i)^{-1}\) 也是逆元
当 \(p\) 比较大时
我们从1开始推
∵\(1·1^{-1}≡1(mod\) 任意数 \()\)(显然)
所以我们把逆元存到数组里,就有
\(A[1]=1\) (注意!0没有逆元,因为 \(0^{-1}\) 无意义)
在枚举 \(i\) 的时候 \(A[p\% i]\) 肯定已经算出来了
因为 \(p\% i<i\) 啊
而且 \(p\)%\(i\), \(i∈[1,p]\) 是不同余的(保证p是个大质数,其实如果p不是质数,不保证有逆元,这个时候需要扩欧,就不能线性了)
所以 \(p\)%\(i\) 已经被算出来了,保证了线性递推是可行的。
代码:
int A[100005];
int main(){
int n, p;
cin>>n>>p;
A[1]=1;
for(int i=2; i<=n ;++i) {
A[i]=((-(p/i)*A[p%i])%p+p)%p;//最小正整数逆元
}
for(int i=1; i<=n; ++i) {
printf("%d\n", A[i]);
}
}
由此发现,可以递归求单个数逆元:
int n, p;
int nyuan(int n) {
if(n==1) return 1;
return ((-(p/n)*nyuan(p%n))%p+p)%p;
}
int main(){
cin>>n>>p;
cout<<nyuan(n);
}
当然了,或许你觉得有些难理解
所以还有个很简单的方式(log)求单个逆元
即:快速幂
费马小定理:\(a^{p-1}≡1(mod\) \(p)\)
这里不作证明(懒)
那就很简单了\(a^{p-1}·a^{-1}≡a^{-1}(mod\) \(p)\)
∴\(a^{p-2}≡a^{-1}(mod\) \(p)\)
那对\(a^{p-2}\)快速幂就行了
点击查看代码
快速幂你还不会吗?恩???