乘法逆元

本文介绍了三种在模意义下计算乘法逆元的方法:拓展欧几里得算法、费马小定理结合快速幂以及线性递推。这些方法都在O(logp)的时间复杂度内完成,适用于大规模质数下的计算。同时,提供了具体的C++代码实现,便于理解和应用。
摘要由CSDN通过智能技术生成

定义

如果 a b ≡ 1 ( m o d p ) ab \equiv 1 \pmod p ab1(modp) ,则 a a a b b b 在模 p p p 意义下互为乘法逆元,记作 a = i n v ( b ) a = \mathrm{inv} (b) a=inv(b) .

求法

拓展欧几里得(exgcd)

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

#define ll long long
ll exgcd(ll a, ll b, ll &x, ll &y) {
	if(b == 0) {
		x = 1;
		y = 0;
		return a;
	}
	ll d = exgcd(b, a % b, x, y);
	ll t = x;
	x = y;
	y = t - a / b * x;
	return d;
}
ll inv(ll a, ll p) {
	ll x, y;
	if(exgcd(a, p, x, y) != 1) return -1;
	return ((x % p) + p) % p;
}

费马小定理 + 快速幂

p p p 是质数,且 gcd ⁡ ( a , p ) = 1 \gcd (a, p) = 1 gcd(a,p)=1 ,则有 a p − 1 ≡ 1 ( m o d p ) a ^ {p-1} \equiv 1 \pmod p ap11(modp) .
a ⋅ i n v ( a ) ≡ 1 ≡ a p − 1 ( m o d p ) a \cdot \mathrm{inv}(a) \equiv 1 \equiv a ^ {p-1} \pmod p ainv(a)1ap1(modp),于是有 i n v ( a ) ≡ a p − 2 ( m o d p ) \mathrm{inv} (a) \equiv a ^ {p-2} \pmod p inv(a)ap2(modp) .

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

#define ll long long
ll ksm(ll a,ll b,ll mod){
	ll ans=1;
	for(;b;b>>=1,a=a*a%mod)
		if(b&1) ans=ans*a%mod;
	return ans%mod;
}
ll inv(ll a, ll p) {
	return ksm(a, p - 2, p);
}

线性递推

首先有 i n v ( 1 ) = 1 \mathrm{inv} (1) = 1 inv(1)=1 .
⌊ p i ⌋ = k \lfloor \frac {p} {i} \rfloor = k ip=k p   m o d   i = r p \bmod i = r pmodi=r , 即 p = k ⋅ i + r p = k \cdot i + r p=ki+r .
在模 p p p 意义下,有 k ⋅ i + r ≡ 0 ( m o d p ) k \cdot i + r \equiv 0 \pmod p ki+r0(modp) .
等式两边同乘 i n v ( i ) ⋅ i n v ( r ) \mathrm{inv} (i) \cdot \mathrm{inv} (r) inv(i)inv(r) , 得 k ⋅ i n v ( r ) + i n v ( i ) ≡ 0 ( m o d p ) k \cdot \mathrm{inv} (r) + \mathrm{inv} (i) \equiv 0 \pmod p kinv(r)+inv(i)0(modp) .
移项并化简得 i n v ( i ) ≡ − ⌊ p i ⌋ ⋅ i n v ( p   m o d   i ) ( m o d p ) \mathrm{inv} (i) \equiv - \lfloor \frac {p} {i} \rfloor \cdot \mathrm{inv} (p \bmod i) \pmod p inv(i)ipinv(pmodi)(modp) .

时间复杂度为 O ( n ) O(n) O(n)
Luogu - P3811 【模板】乘法逆元

//Luogu P3811 乘法逆元
#include<bits/stdc++.h>
#define N 3000006
#define ll long long
using namespace std;
ll n, p, inv[N];
int main() {
	scanf("%lld%lld", &n, &p);
    inv[1] = 1;
	printf("%lld\n", inv[1]);
	for(ll i = 2; i <= n; ++i) {
		inv[i] = inv[p % i] * (p - p / i) % p;
		printf("%lld\n", inv[i]);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值