Modular Arithmetic 模运算

Modular Arithmetic 模运算

简介

在模运算中,我们不是直接处理整数本身,而是处理它们除以 $m$ 后的余数。我们称这个过程为取模 $m$。例如,如果我们取 $m = 23$,那么我们不是处理 $x = 247$,而是使用 $x \bmod 23 = 17$。

通常,$m$ 会是一个大素数,题目中会给出;最常见的两个值是 $10^9 + 7$ 和 $998244353 = 119 \cdot 2^{23} + 1$。

模运算用于避免处理会溢出内置数据类型的数字,因为我们可以根据以下公式取余数:

  • $(a + b) \bmod m = ((a \bmod m) + (b \bmod m)) \bmod m$
  • $(a - b) \bmod m = ((a \bmod m) - (b \bmod m)) \bmod m$
  • $(a \cdot b) \bmod m = ((a \bmod m) \cdot (b \bmod m)) \bmod m$
  • $a^b \bmod m = (a \bmod m)^b \bmod m$

模幂运算

为什么需要快速幂?

计算 $x^n \bmod m$ 时,如果直接使用循环乘 $n$ 次,时间复杂度是 $O(n)$。但当 $n$ 达到 $10^9$ 甚至更大时,这种方法就太慢了。

二进制幂次方

二进制幂次方(Binary Exponentiation)可以将时间复杂度降到 $O(\log n)$。

核心思想:将指数 $n$ 分解为二进制表示。例如:

如果我们知道 $x^1, x^2, x^4, \dots, x^{2^{\lfloor \log_2 n \rfloor}}$,就可以用 $O(\log n)$ 的时间计算出 $x^n$。

实现

MOD = 10**9 + 7

def pow_mod(a, b, mod):
    """计算 a^b mod mod"""
    res = 1
    a %= mod
    while b:
        if b & 1:
            res = res * a % mod
        a = a * a % mod
        b >>= 1
    return res

Python 内置的 pow 函数已经实现了快速幂,可以直接使用:

MOD = 10**9 + 7

for _ in range(int(input())):
    first, second = map(int, input().split())
    print(pow(first, second, MOD))

模逆元

什么是模逆元?

模逆元相当于实数中的倒数。要将 $a$ 除以 $b$,可以乘以 $b$ 的模逆元。

对于素数模 $p$,$a$ 的模逆元定义为满足以下条件的数 $a^{-1}$:

例如,$2$ 模 $p = 10^9 + 7$ 的逆元是 $i = \frac{p+1}{2} = 5 \cdot 10^8 + 4 = 500000004$。

验证:

费马小定理

费马小定理(不要与费马大定理混淆)指出,所有不被 $p$ 整除的整数 $a$ 都满足:

因此:

所以 $a^{p-2}$ 就是 $a$ 模 $p$ 的逆元。

实现

MOD = 10**9 + 7

# 方法1:使用费马小定理
x = pow(2, MOD - 2, MOD)
print(x)  # 500000004
assert 2 * x % MOD == 1

扩展欧几里得算法

我们也可以通过扩展欧几里得算法求模逆元:

def modinv(x, mod):
    """扩展欧几里得算法求模逆元"""
    if x <= 1:
        return x
    return mod - mod // x * modinv(mod % x, mod) % mod

预计算逆元

如果需要频繁计算多个数的模逆元,可以利用以下递推公式预计算:

MOD = 10**9 + 7
inv = [0] * MOD
inv[1] = 1
for i in range(2, MOD):
    inv[i] = MOD - MOD // i * inv[MOD % i] % MOD

这样可以在 $O(MOD)$ 时间内预处理所有 $[1, MOD)$ 范围内数的逆元,之后每次查询只需 $O(1)$。


注意事项

  1. 除零错误:必须确保不尝试除以 0
  2. 取模后的零:要注意在应用模运算后,一个非零数可能会变成零,因此在对非常数值进行除法运算时要格外小心
  3. 性能:由于计算模逆元需要 $O(\log p)$ 的时间,在循环内部频繁使用除法会显著增加程序的运行时间。如果多次使用同一个数的模逆元,预先计算它是更好的选择

Modular Arithmetic 模运算
https://mingsm17518.github.io/2026/03/21/algorithm/03_Gold/01_Math/02_Modular Arithmetic 模运算/
作者
Ming
发布于
2026年3月21日
更新于
2026年3月21日
许可协议