乘法模运算逆元

逆元 (Multiplicative Inverse)

逆元是在特定运算下的"反转"
举几个例子

  1. 加法
    a的加法逆元就是-a, 它们相加为0
  2. 乘法逆元
    a的乘法逆元是1/a, 它们相乘为1
  3. 矩阵乘法
    矩阵的逆元是逆矩阵, 它们相乘为单位矩阵

有人会问了, 为什么它们运算结果并不一样呢? 他们的共性在哪里?
这里的关键在于, 他们的运算结果都是源于运算的"基本性质"和"单位元素"
所谓单位元素就是, 在加法中, 任意元素加0不会改变, 而乘法中, 所有元素乘以1不会改变, 这就是单位元素, 矩阵同理

乘法模运算逆元

很多算法题目中会涉及到乘法模运算的逆元
我举2个例子

  1. 题目给了100个数的乘积模1e9+7之后的结果, 然后给了后50个数乘积模1e9+7的结果
    请问前50个数的乘积模1e9+7是多少?
  2. 组合数学涉及到计算非常大的C(n, m) mod M 的时候

这都是需要"回撤"或"反转"的时候

求逆元算法

  1. 扩展欧几里得 (exgcd)
  2. 费马小定理 (需要模数M是素数)
  3. 欧拉定理

费马小定理

说一下费马小定理
因为它比较简单

费马小定理: 若M是质数,且B、M互质,那么B^(M-1) mod M = 1
所以可以扩展到
A/B mod M
= A/B mod M * 1
= A/B mod M * B^(M-1) mod M
= A*B^(M-2) mod M

于是除法就被转换成了乘法
这里求B的M-2幂次的时候用快速幂, 所以是个O(logn)的算法

计算C(n, m)

首先, 定义一下阶乘为fac(x)
组合数的公式很简单
C(n, m) = fac(n) / (fac(m) * fac(n-m))
那首先我们需要预处理两个数组
fac, inv
前者计算从1到n的阶乘的模M结果(复杂度O(n))
后者计算从1到n的阶乘的模M的逆元(复杂度O(n))

前面讲了单次求逆元复杂度是O(logn), 但是这里整体复杂度为什么不是O(nlogn)呢? 因为这里有个递推关系, 他们彼此之间是有依赖关系的, 并不需要每个数组元素都单独计算一遍
inv[i] * fac[i] == 1 (% MOD)
inv[i] * fac[i] * (i+1) == (i+1) (% MOD)
inv[i] * fac[i+1] == (i+1) (% MOD)
inv[i] = (i+1) / fac[i+1] (% MOD)
我们一开始这俩数组目的是什么? 是不是inv_arr[i] * f_arr[i] == 1 (%MOD)
注意到右边其实就是(i+1) * inv[i+1]
所以只要倒着算就可以递推了

void init()
{
fac[0]=inv[0]=1;
for (long long i = 1; i < maxn; i++)
{
fac[i] = fac[i - 1] * i % MOD;
}
inv[maxn-1] = quickpower(fac[maxn-1], MOD - 2);
for(int i = maxn-2; i>=0; i--){
inv[i] = inv[i+1] * (i+1) % MOD;
}
}

所以C(n, m)是不是可以张口就来?
C(n, m) = fac[n] * inv[n-m] % MOD * inv[n] % MOD;
除法转换为乘法就可以了

完整小case

计算 fac(1到99) / fac(5到99) = 24

#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 7;
long long pow(long long a, int b)
{
long long ret = 1;
while (b)
{
if (b & 1)
{
ret *= a;
ret %= MOD;
}
a *= a;
a %= MOD;
b >>= 1;
}
cout << "ret " << ret << endl;
return ret;
}
int main()
{
long long a = 1;
for (int i = 1; i < 100; i++)
{
a = a * i % MOD;
}
cout << a << endl;
long long b = 1;
for (int i = 5; i < 100; i++)
{
b = b * i % MOD;
}
cout << b << endl;
// A/B MOD M = A * B^(M-2) MOD M
long long c = a * pow(b, MOD - 2) % MOD;
cout << c << endl;
return 0;
}
// 费马小定理: 若M是质数, 且B, M互质, 那么 B^(M-1) MOD M = 1

posted on   tianlonghuangwu  阅读(61)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示