求乘法逆元方法总结

12 篇文章 0 订阅

简介

若有线性同余方程 a x ≡ 1 ( m o d   p ) ax\equiv 1\left( mod~p\right) ax1(mod p),则称 x x x a a a 在模 p p p 意义下的逆元,记作 a − 1 a^{-1} a1

扩展欧几里得法

Idea

原方程 a x ≡ 1 ( m o d   p ) ax\equiv 1\left( mod~p\right) ax1(mod p) 可转化为 a x + p y = 1 ax + py = 1 ax+py=1
然后就可以采用和解线性同余方程相同的方法,通过求解 a x + p y = g c d ( a , p ) ax + py = gcd(a,p) ax+py=gcd(a,p) 来得到方程的一组解
根据裴蜀定理,我们发现方程有解的条件为 g c d ( a , p ) = 1 gcd(a,p) = 1 gcd(a,p)=1 a a a p p p 互质
所以得出结论: a a a 在 模 p p p 意义下的乘法逆元存在当且仅当 a a a p p p互质
故题目往往给的模数为一大质数,从而保证了 [ 1 , p − 1 ] [1, p-1] [1,p1] 中所有整数都有模 p p p 意义下的逆元

因此,我们只需要求出方程 a x + p y = g c d ( a , p ) ax + py = gcd(a,p) ax+py=gcd(a,p) 的最小整数解 x 0 x_0 x0,同时由于 g c d ( a , p ) = 1 gcd(a,p) = 1 gcd(a,p)=1
x 0 x_0 x0即为 a − 1 a^{-1} a1

时间复杂度: O ( log ⁡ p ) O(\log p) O(logp)

Code

//b 即为 p
ll exgcd(ll a, ll b, ll &x, ll &y) {
    if (b == 0) {
        x = 1, y = 0;
        return a;
    }
    int d = exgcd(b, a % b, x, y);
    int t = x; x = y; y = t - (a / b) * y;
    return d;
}

快速幂法

Idea

Note使用这种方法需满足 p p p 为质数

费马小定理:若p为质数,且整数 a a a 满足 p   ∣   a p~|~a p  a ,则有 a b − 1 ≡ 1   ( m o d   p ) a^{b-1} \equiv 1~(mod ~ p) ab11 (mod p)

原方程转化为 a p − 2 ≡ a − 1   ( m o d   p ) a^{p-2} \equiv a^{-1}~(mod ~ p) ap2a1 (mod p)
所以只需要求 a p − 2 m o d   p a^{p-2} mod~p ap2mod p 即可,可以用快速幂来求

时间复杂度: O ( log ⁡ p ) O(\log p) O(logp)

Code

//qpow(a,p-2)
inline int qpow(ll a,int n){
	int res = 1;
	a = (a % mod + mod) % mod;
	while(n){
		if(n & 1) res = (res * a) % mod;
		a = (a * a) % mod;
		n >>= 1;
	}
	return res;
}

线性求[1,n]逆元

Note使用这种方法需满足 p p p 为质数

Idea

若要求 [ 1 , n ] [1,n] [1,n] 每个数的逆元,虽然可以使用上面介绍的两种求单个数逆元的方法
但复杂度来到了 O ( n log ⁡ p ) O(n\log p) O(nlogp),数据大的时候可能会TLE

故得用线性时间来求

时间复杂度: O ( n ) O(n) O(n)

Code

inv[1] = 1;
for (int i = 2; i <= n; ++i) inv[i] = (long long)(p - p / i) * inv[p % i] % p;

线性求任意n个数的逆元

Idea

若要求任意n个数 ( a 1 , a 2 . . .   a n ) (a_1,a_2...~a_n) (a1,a2... an) 的逆元 ( 1 ≤ a i < p 1 \leq a_i < p 1ai<p)的逆元, O ( m a x { a i } ) O(max\left\{a_i\right\}) O(max{ai}) 的复杂度显然不行

就需要使用下面的方法:
先计算出 n n n 个数的前缀积 s i s_i si, 然后用exgcd / qpow 计算 s n s_n sn 的逆元,记为 s v n sv_n svn

因为 s v n sv_n svn n n n 个数的积的逆元,所以当我们把它乘上 a n a_n an 时,就会和 a n a_n an 的逆元抵消,于是就得到了 a 1 a_1 a1 a n − 1 a_{n-1} an1 的积逆元,记为 s v n − 1 sv_{n-1} svn1

同理我们可以依次计算出所有的 s v i sv_i svi,于是 a i − 1 {a_i}^{-1} ai1就可以用 s i − 1 ∗ s v i s_{i-1} * sv_i si1svi 求得

所以我们就在几乎线性的时间内计算出了 n n n 个数的逆元

时间复杂度: O ( n + log ⁡ p ) O(n + \log p) O(n+logp)

Code

s[0] = 1;
for (int i = 1; i <= n; ++i) s[i] = s[i - 1] * a[i] % p;
sv[n] = qpow(s[n], p - 2);
for (int i = n; i >= 1; --i) sv[i - 1] = sv[i] * a[i] % p;
for (int i = 1; i <= n; ++i) inv[i] = sv[i] * s[i - 1] % p;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值