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

本文深入探讨了组合数的计算方法,特别是在大数情况下使用模数进行运算的技巧。介绍了乘法逆元的概念,包括逆元的定义、求解方法如快速幂算法和线性递推算法,并通过实例展示了如何在模意义下求解逆元。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题引入

众所周知,有一个神奇的东西,叫做“组合数”,简称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<r<i<p)也就是 k 是 p / i 的商,r 是余数

再将这个式子放到 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]);

压行大法好

总结

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

大家一定要好好掌握

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值