乘法逆元初探(快速幂算法,线性算法)

问题引入

众所周知,有一个神奇的东西,叫做“组合数”,简称C,在各种数论题里常常出现
同样众所周知,组合数的公式是
C(n,m)=n!/((n-m)!*m!)
但是因为这个数可能很大,连传说中的__int128都存不下,所以我们往往需要一个模数
所以组合数的公式就变成了
C(n,m)=n!/((n-m)!*m!)%p
这时候,问题就来了,我们都知道(a/b)%p!=a%p/b%p,这时候,我们就需要使用乘法逆元了

逆元定义

我们规定若a*x mod p = 1 ,且gcd(a,b)=1 我们定义:x为a的逆元,记作a-1
我们也可以称 x 为 a 在 mod b 意义下的倒数

求逆元的方法

求逆元的方法有很多,比如扩展欧几里得,快速幂,还有线性递推的方法,这里先介绍快速幂算法和线性算法

快速幂算法

这里需要引用一个定理:费马小定理(或者欧拉定理)
先说说费马小定理
当p是质数的时候,ap-1mod p = 1
欧拉定理是 a^phi( p ) mod p =1
phi(i)表示从1到n中和n互质的数的个数
但是,我们发现当p是质数的时候,phi( p )=p-1,所以费马小定理是欧拉定理的一部分
也就是说,当p是质数的时候,a-1 mod p = ap-2mod p
使用快速幂解决就可以了
这里就默认大家都会快速幂了

例题:求a在mod p 意义下的逆元
代码:

inline int read(){
	int s=0,w=1;
	char c=gc;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=gc;}
	while(c>='0'&&c<='9')s=s*10+c-'0',c=gc;
	return s*w;
}
int n,p;
inline int Qpow(int base,int ind){
	int ans=1;
	while(ind){
		if(ind&1)ans=1ll*ans*base%p;
		base=1ll*base*base%p;
		ind>>=1;
	}
	return ans;
}
int main()
{
	n=read(),p=read();
	printf("%d\n",Qpow(n,p-2));
	return 0;
}

好像大部分都是快速幂的样子
这个方法可以和组合数联系起来,正如我们引入的那个问题一样,在后面会有讲解,这里先不再多赘述了

线性算法

线性递推可以用来计算连续一串数的逆元
这里刚好有例题,直接引用了

洛谷 P3811 [模板]乘法逆元

这个是一个线性递推的题,其实我觉得他这道题出的并不好,因为我真的没见过几道用线性递推做的题
也可能是我太菜了
这道题使用快速幂的o(nlogn)应该是过不了的
所以我们只能用这种方法
首先我们有这个式子1-1 mod p = 1
然后设p=k*i+r(0

再将这个式子放到 mod p意义下就会得到:

(k*i+r)mod p = 0

然后乘上i-1r-1

k * r-1 + i-1 mod p = 0

i-1 和 -k * r-1对于p同余

i-1 和 -(p/i)*(p mod i)-1 mod p

核心代码非常的短

	inv[1]=1;
	printf("1\n");
	Rep(i,2,n)inv[i]=(ll)(p-p/i)*inv[p%i]%p,printf("%d\n",inv[i]);

压行大法好

总结

乘法逆元是数论中非常重要的一块,很多省选的数论题都要用到乘法逆元

大家一定要好好掌握

你可能感兴趣的:(C++,数论)