乘法逆元
本文介绍模意义下乘法运算的逆元(Modular Multiplicative Inverse),并介绍如何使用扩展欧几里德算法(Extended Euclidean algorithm)求解乘法逆元
逆元简介¶
如果一个线性同余方程
如何求逆元¶
扩展欧几里得法¶
模板代码
1 2 3 4 5 6 7 8 9 | // C++ Version
void exgcd(int a, int b, int& x, int& y) {
if (b == 0) {
x = 1, y = 0;
return;
}
exgcd(b, a % b, y, x);
y -= a / b * x;
}
|
1 2 3 4 5 6 7 | # Python Version
def exgcd(a, b, x, y):
if b == 0:
x, y = 1, 0
return
exgcd(b, a % b, y, x)
y = y - (a // b * x)
|
扩展欧几里得法和求解 线性同余方程 是一个原理,在这里不展开解释。
快速幂法¶
因为
所以
所以
然后我们就可以用快速幂来求了。
模板代码
1 2 3 4 5 6 7 8 9 10 | // C++ Version
inline int qpow(long long a, int b) {
int ans = 1;
a = (a % p + p) % p;
for (; b; b >>= 1) {
if (b & 1) ans = (a * ans) % p;
a = (a * a) % p;
}
return ans;
}
|
1 2 3 4 5 6 7 8 9 10 | # Python Version
def qpow(a, b):
ans = 1
a = (a % p + p) % p
while b:
if b & 1:
ans = (a * ans) % p
a = (a * a) % p
b >>= 1
return ans
|
注意使用 费马小定理 需要限制
线性求逆元¶
求出
如果对于每个数进行单次求解,以上两种方法就显得慢了,很有可能超时,所以下面来讲一下如何线性(
首先,很显然的
证明
对于
其次对于递归情况
两边同时乘
再带入
我们注意到
故我们就可以推出逆元,利用递归的形式,而使用迭代实现:
代码实现
1 2 3 4 5 | // C++ Version
inv[1] = 1;
for (int i = 2; i <= n; ++i) {
inv[i] = (long long)(p - p / i) * inv[p % i] % p;
}
|
1 2 3 4 | # Python Version
inv[1] = 1
for i in range(2, n + 1):
inv[i] = (p - p // i) * inv[p % i] % p
|
使用
另外我们注意到我们没有对 inv[0]
进行定义却可能会使用它:当 inv[p % i]
,也就是 inv[0]
,这是因为当 inv[i]
的值是未定义的。
另外,根据线性求逆元方法的式子:
递归求解
中间优化可以加入一个记忆化来避免多次递归导致的重复,这样求
注意:如果用以上给出的式子递归进行单个数的逆元求解,目前已知的时间复杂度的上界为
线性求任意 n 个数的逆元¶
上面的方法只能求
首先计算
因为
同理我们可以依次计算出所有的
所以我们就在
代码实现
1 2 3 4 5 6 7 | // C++ Version
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);
// 当然这里也可以用 exgcd 来求逆元,视个人喜好而定.
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;
|
1 2 3 4 5 6 7 8 9 10 | # Python Version
s[0] = 1
for i in range(1, n + 1):
s[i] = s[i - 1] * a[i] % p
sv[n] = qpow(s[n], p - 2)
# 当然这里也可以用 exgcd 来求逆元,视个人喜好而定.
for i in range(n, 0, -1):
sv[i - 1] = sv[i] * a[i] % p
for i in range(1, n + 1):
inv[i] = sv[i] * s[i - 1] % p
|
逆元练习题¶
build本页面最近更新:,更新历史
edit发现错误?想一起完善? 在 GitHub 上编辑此页!
people本页面贡献者:OI-wiki
copyright本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用