乘法逆元

逆元

取模相信都会了
\(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$$

\[∴k·i+r≡p(mod~p) \]

\[∴k·i+r≡0(mod~p) \]

\[∴r≡-k·i(mod~p) \]

两边同乘 \(r^{-1}i^{-1}\)
得到:

\[i^{-1}≡-k·r^{-1}(mod~p) \]

\(k·i+r=p\) 得:

\[k=p/i, r=p\% i \]

代入得:

\[i^{-1}≡-(p/i)·(p \% i)^{-1}(mod~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}\)快速幂就行了

点击查看代码
快速幂你还不会吗?恩???
posted @ 2022-01-23 19:22  Konnya_ku  阅读(75)  评论(3编辑  收藏  举报