模逆元计算方法详解:扩展欧几里得算法与费马小定理

1. 模逆元简介

在模运算中,对于整数 a 和模数 M,如果存在整数 x 使得 a × x ≡ 1 (mod M),则称 xa 在模 M 下的逆元,记作 a⁻¹inv(a)。模逆元在密码学、组合数学和算法竞赛中有着广泛应用。

2. 方法一:扩展欧几里得算法(通用方法)

扩展欧几里得算法适用于 Ma 互质(即 gcd(a, M) = 1)的情况。该算法不仅能求出最大公约数,还能求出贝祖等式 ax + by = gcd(a, b) 的一组整数解。

2.1 算法实现

以下是扩展欧几里得算法的 C++ 实现:

cpp 复制代码
// 扩展欧几里得算法:求 ax + by = gcd(a,b) 的解
long long exgcd(long long a, long long b, long long &x, long long &y) {
    if (b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    long long x1, y1;
    long long d = exgcd(b, a % b, x1, y1);
    x = y1;
    y = x1 - (a / b) * y1;
    return d;
}

// 求 a 在模 M 下的逆元
long long mod_inv(long long a, long long M) {
    long long x, y;
    long long d = exgcd(a, M, x, y);
    if (d != 1) return -1;  // 不存在逆元
    return (x % M + M) % M;  // 保证结果为正
}



//简洁点的
//long long inv_exgcd(long long a, long long b){  //	long long b=MOD;
//	int u=1, v=0;
//	while(b) {
//		long long t=a/b;
//		a-=t*b;
//		swap(a,b);
//		u-=t*v;
//		swap(u,v);
//	}
//	return (u%MOD+MOD)%MOD;
//} 

2.2 算法原理

gcd(a, M) = 1 时,扩展欧几里得算法求出的 x 满足 ax + My = 1。对等式两边取模 M,得到 ax ≡ 1 (mod M),因此 x 就是 a 在模 M 下的逆元。

2.3 示例

求 2 在模 7 下的逆元:

text 复制代码
2 × 4 = 8 ≡ 1 (mod 7)
所以 2⁻¹ = 4

使用上述代码计算:mod_inv(2, 7) 返回 4。

3. 方法二:费马小定理(适用于质数模数)

当模数 M 是质数时,根据费马小定理:

text 复制代码
a^(M-1) ≡ 1 (mod M)

因此:

text 复制代码
a⁻¹ = a^(M-2) mod M

3.1 快速幂实现

以下是使用快速幂计算模逆元的 C++ 实现:

cpp 复制代码
long long mod_pow(long long a, long long b, long long mod) {
    long long res = 1;
    while (b > 0) {
        if (b & 1) res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}

// 求 a 在质数模 M 下的逆元
long long mod_inv_prime(long long a, long long M) {
    return mod_pow(a, M - 2, M);
}

3.2 时间复杂度

快速幂的时间复杂度为 O(log M),当 M 很大时(如 10⁹+7),这种方法比扩展欧几里得算法稍慢,但代码更简洁。

4. 方法对比与选择建议

方法 适用条件 时间复杂度 优点 缺点
扩展欧几里得 gcd(a, M) = 1 O(log min(a, M)) 通用性强,可判断逆元是否存在 代码稍复杂
费马小定理 M 为质数 O(log M) 代码简洁,易于实现 仅适用于质数模数

5. 实际应用场景

  • 组合数取模 :计算 C(n, k) mod p(p 为质数)时,需要用到阶乘的逆元。
  • 线性同余方程 :求解 ax ≡ b (mod M) 时,若 gcd(a, M) = 1,则 x ≡ b × a⁻¹ (mod M)
  • 密码学:RSA 算法中私钥的计算涉及模逆元。

6. 注意事项

  1. 使用扩展欧几里得算法时,务必检查 gcd(a, M) = 1,否则逆元不存在。
  2. 费马小定理方法仅当 M 为质数时成立,使用时需确保模数是质数。
  3. 计算结果可能为负数,需要通过 (x % M + M) % M 转换为正数。
  4. 对于大数运算,注意使用 long long 类型,避免溢出。

7.完整模板代码

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const long long MOD = 998244353;

// 方法1:快速幂求逆元(模数为质数)
long long inv_fermat(long long a) {
    long long res = 1, base = a, exp = MOD - 2;
    while (exp > 0) {
        if (exp & 1) res = res * base % MOD;
        base = base * base % MOD;
        exp >>= 1;
    }
    return res;
}

// 方法2:扩展欧几里得求逆元(通用)
long long inv_exgcd(long long a) {
    long long b = MOD, u = 1, v = 0;
    while (b) {
        long long t = a / b;
        a -= t * b; swap(a, b);
        u -= t * v; swap(u, v);
    }
    return (u % MOD + MOD) % MOD;
}

int main() {
    // 计算 15/2
    long long inv2 = inv_fermat(2);
    cout << 15 * inv2 % MOD << endl;  // 499122184
    
    // 计算 5/3
    long long inv3 = inv_fermat(3);
    cout << 5 * inv3 % MOD << endl;
    
    return 0;
}
相关推荐
Code额10 小时前
Python asyncio 异步编程全套学习文档(零基础完整版)
python·学习·oracle·async·异步·asyncio
郝学胜_神的一滴10 小时前
干货版《算法导论》18:遍历增删原理、时间复杂度与集合序列实现全解
数据结构·算法
-dzk-10 小时前
【滑动窗口】LC 3.无重复字符的最长子串
算法·滑动窗口
zzxdear10 小时前
用 OR-Tools CP-SAT 求解最简单的柔性作业车间调度(FJSP)
算法
luj_176810 小时前
变频技术核心原理揭秘
服务器·c语言·开发语言·经验分享·算法
rannn_11110 小时前
【力扣hot100】图论专题+模板|DFS、BFS、拓扑排序...
java·算法·leetcode·深度优先·图论
在线考试系统推荐10 小时前
2026年7月最新实测:三大考试软件导入试题能力对比
人工智能·学习·系统架构
_Narcissus_10 小时前
B树概念及操作笔记(含完整代码实现)
c语言·数据结构·数据库·c++·笔记·b树·算法
l12586510 小时前
# RAG多轮对话检索设计:Query重写如何让“那它呢“变成完整问题
前端·数据库·人工智能·python·算法·fastapi·milvus
小弥儿11 小时前
GitHub今日热榜 | 2026-08-20:Agent 上下文数据库接棒
数据库·学习·开源·github