Bootstrap

探索乘法逆元:模运算下的新概念

乘法逆元

在数学中,特别是在模运算中,乘法逆元是一个非常重要的概念。给定一个整数 a a a 和一个模数 m m m,如果存在一个整数 x x x,使得 a × x ≡ 1 ( m o d m ) a \times x \equiv 1 \pmod{m} a×x1(modm),那么 x x x 就被称为 a a a 在模 m m m 下的乘法逆元。换句话说, x x x a a a 的逆元,当且仅当 a × x a \times x a×x 除以 m m m 的余数为 1。需要注意的是,乘法逆元并不总是存在。只有当 a a a m m m 互质(即 gcd ⁡ ( a , m ) = 1 \gcd(a, m) = 1 gcd(a,m)=1)时, a a a 在模 m m m 下才有乘法逆元。

乘法逆元的应用

乘法逆元在数学、计算机科学和工程等领域有广泛的应用,特别是在模运算、密码学和算法设计中。以下是乘法逆元的一些主要应用场景:

1. 模运算中的除法

在模运算中,除法操作并不像加法和乘法那样直接定义, 模运算下的“除法”实际上是寻找一个数的乘法逆元的问题。具体来说,如果我们需要计算 a b ( m o d m ) \frac{a}{b} \pmod{m} ba(modm),我们可以找到 b b b 在模 m m m 下的乘法逆元 b − 1 b^{-1} b1,然后计算:

a b ≡ a × b − 1 ( m o d m ) \frac{a}{b} \equiv a \times b^{-1} \pmod{m} baa×b1(modm)

这种方法在密码学、编码理论和算法设计中非常常见。
假设我们需要在模 11 11 11 下计算 7 3 \frac{7}{3} 37

  1. 找到 3 3 3 在模 11 11 11 下的乘法逆元

    • 使用扩展欧几里得算法,我们可以找到 3 − 1 ≡ 4 ( m o d 11 ) 3^{-1} \equiv 4 \pmod{11} 314(mod11),因为 3 × 4 = 12 ≡ 1 ( m o d 11 ) 3 \times 4 = 12 \equiv 1 \pmod{11} 3×4=121(mod11)
  2. 计算 7 3 ( m o d 11 ) \frac{7}{3} \pmod{11} 37(mod11)

    • 7 3 ≡ 7 × 4 = 28 ≡ 6 ( m o d 11 ) \frac{7}{3} \equiv 7 \times 4 = 28 \equiv 6 \pmod{11} 377×4=286(mod11)

因此, 7 3 ≡ 6 ( m o d 11 ) \frac{7}{3} \equiv 6 \pmod{11} 376(mod11)

2. 线性同余方程的求解

线性同余方程的形式为:

a × x ≡ b ( m o d m ) a \times x \equiv b \pmod{m} a×xb(modm)

为了求解 x x x,我们可以找到 a a a 在模 m m m 下的乘法逆元 a − 1 a^{-1} a1,然后两边同时乘以 a − 1 a^{-1} a1

x ≡ b × a − 1 ( m o d m ) x \equiv b \times a^{-1} \pmod{m} xb×a1(modm)

这种方法在解决数论问题和算法设计中非常有用。

3. 中国剩余定理

中国剩余定理(Chinese Remainder Theorem, CRT)用于解决一组同余方程。在应用CRT时,通常需要计算模数的乘法逆元。具体来说,给定一组同余方程:

x ≡ a 1 ( m o d m 1 ) x \equiv a_1 \pmod{m_1} xa1(modm1)
x ≡ a 2 ( m o d m 2 ) x \equiv a_2 \pmod{m_2} xa2(modm2)
⋮ \vdots
x ≡ a n ( m o d m n ) x \equiv a_n \pmod{m_n} xan(modmn)

其中 m 1 , m 2 , … , m n m_1, m_2, \ldots, m_n m1,m2,,mn 两两互质,CRT 提供了一种求解 x x x 的方法。在这个过程中,计算各个模数的乘法逆元是关键步骤。

4. 密码学中的应用

乘法逆元在密码学中有广泛的应用,特别是在公钥密码系统和对称密钥密码系统中。

  • RSA 加密算法

    • 在 RSA 算法中,密钥生成过程需要计算模数的乘法逆元。具体来说,选择两个大质数 p p p q q q,计算 n = p × q n = p \times q n=p×q ϕ ( n ) = ( p − 1 ) ( q − 1 ) \phi(n) = (p-1)(q-1) ϕ(n)=(p1)(q1)。然后选择一个整数 e e e 使得 gcd ⁡ ( e , ϕ ( n ) ) = 1 \gcd(e, \phi(n)) = 1 gcd(e,ϕ(n))=1,并计算 d d d 作为 e e e 在模 ϕ ( n ) \phi(n) ϕ(n) 下的乘法逆元,即 e × d ≡ 1 ( m o d ϕ ( n ) ) e \times d \equiv 1 \pmod{\phi(n)} e×d1(modϕ(n))。这里 d d d 就是私钥的一部分。
  • 椭圆曲线密码学

    • 在椭圆曲线密码学中,点的加法和倍乘运算涉及到模数的乘法逆元。计算椭圆曲线上的点的逆元是进行点加和倍乘操作的基础。

总结

乘法逆元在模运算、密码学、算法设计和数论中有着广泛的应用。通过理解和掌握乘法逆元的概念及其计算方法,我们可以更高效地解决各种数学和工程问题,特别是在需要处理模运算的场景中。

逆元相关的概念和定理

互质的定义

两个整数 a a a b b b 互质(或称为互素)是指它们的最大公约数(GCD)为 1,即 gcd ⁡ ( a , b ) = 1 \gcd(a, b) = 1 gcd(a,b)=1

存在性定理

  • 如果 a a a m m m 互质(即 gcd ⁡ ( a , m ) = 1 \gcd(a, m) = 1 gcd(a,m)=1),那么 a a a 在模 m m m 下存在唯一的乘法逆元。
  • 如果 a a a m m m 不互质,那么 a a a 在模 m m m 下没有乘法逆元。

费马小定理

  • 如果 m m m 是一个质数 p p p,且 a a a 不是 p p p 的倍数,那么 a p − 1 ≡ 1 ( m o d p ) a^{p-1} \equiv 1 \pmod{p} ap11(modp)
  • 由此可以推出, a a a 在模 p p p 下的乘法逆元是 a p − 2 a^{p-2} ap2 a p − 1 = a × a ( p − 2 ) ≡ 1 ( m o d p ) a^{p - 1}= a\times a^{(p-2)} \equiv 1 \pmod p ap1=a×a(p2)1(modp)

欧拉函数

欧拉函数(Euler’s Totient Function),记作 ϕ ( n ) \phi(n) ϕ(n),是数论中的一个重要函数,用于计算小于或等于正整数 n n n 且与 n n n 互质的正整数的个数。换句话说,欧拉函数 ϕ ( n ) \phi(n) ϕ(n) 给出了在 1 1 1 n n n 之间与 n n n 互质的整数的数量。

欧拉函数的性质
  1. 对于质数 p p p

    • 如果 p p p 是一个质数,那么 ϕ ( p ) = p − 1 \phi(p) = p - 1 ϕ(p)=p1,因为 1 1 1 p − 1 p-1 p1 的所有整数都与 p p p 互质。
  2. 对于质数的幂 p k p^k pk

    • 如果 p p p 是一个质数,且 k k k 是一个正整数,那么 ϕ ( p k ) = p k − p k − 1 \phi(p^k) = p^k - p^{k-1} ϕ(pk)=pkpk1。这是因为在 1 1 1 p k p^k pk 之间,只有 p p p 的倍数不与 p k p^k pk 互质,而 p p p 的倍数有 p k − 1 p^{k-1} pk1 个。
  3. 对于两个互质的整数 m m m n n n

    • 如果 m m m n n n 互质,即 gcd ⁡ ( m , n ) = 1 \gcd(m, n) = 1 gcd(m,n)=1,那么 ϕ ( m × n ) = ϕ ( m ) × ϕ ( n ) \phi(m \times n) = \phi(m) \times \phi(n) ϕ(m×n)=ϕ(m)×ϕ(n)。这个性质称为欧拉函数的积性。
  4. 对于一般的正整数 n n n

    • 如果 n n n 的质因数分解为 n = p 1 k 1 × p 2 k 2 × ⋯ × p m k m n = p_1^{k_1} \times p_2^{k_2} \times \cdots \times p_m^{k_m} n=p1k1×p2k2××pmkm,那么欧拉函数可以表示为:
      ϕ ( n ) = n × ( 1 − 1 p 1 ) × ( 1 − 1 p 2 ) × ⋯ × ( 1 − 1 p m ) \phi(n) = n \times \left(1 - \frac{1}{p_1}\right) \times \left(1 - \frac{1}{p_2}\right) \times \cdots \times \left(1 - \frac{1}{p_m}\right) ϕ(n)=n×(1p11)×(1p21)××(1pm1)
    • 这个式可以这样来理解,我们取一种简单的情况: n = p 1 k 1 × p 2 k 2 n = p_1^{k_1} \times p_2^{k_2} n=p1k1×p2k2。质因数分解之后,首先去除 p 1 p_1 p1的倍数,一共有 ⌊ n p 1 ⌋ \left\lfloor \frac{n}{p_1}\right\rfloor p1n个,同理去除 p 2 p_2 p2的倍数,一共有 ⌊ n p 2 ⌋ \left\lfloor \frac{n}{p_2}\right\rfloor p2n个。再加上去除了两次的 p 1 , p 2 p_1,p_2 p1,p2的公倍数,有 ⌊ n p 1 × p 2 ⌋ \left\lfloor \frac{n}{p_1\times p_2}\right\rfloor p1×p2n个。因此 ϕ ( n ) = n − ⌊ n p 1 ⌋ − ⌊ n p 2 ⌋ + ⌊ n p 1 × p 2 ⌋ \phi (n) = n - \left\lfloor \frac{n}{p_1}\right\rfloor - \left\lfloor \frac{n}{p_2}\right\rfloor + \left\lfloor \frac{n}{p_1\times p_2}\right\rfloor ϕ(n)=np1np2n+p1×p2n,而 n n n是可以被 p 1 , p 2 p_1,p_2 p1,p2整除的(因为是 n n n的质因数),所以化简得到
      ϕ ( n ) = n × ( 1 − 1 p 1 − 1 p 2 + 1 p 1 × p 2 ) = n × ( 1 − 1 p 1 ) × ( 1 − 1 p 2 ) \phi(n) = n \times (1 - \frac{1}{p_1} - \frac{1}{p_2} + \frac{1}{p_1\times p_2})=n\times(1-\frac{1}{p_1})\times(1-\frac{1}{p_2}) ϕ(n)=n×(1p11p21+p1×p21)=n×(1p11)×(1p21)
      面对更加多质因数的情况,也是依据容斥原理进行推导,这里就不详细介绍了。
欧拉函数的计算示例
  1. 计算 ϕ ( 9 ) \phi(9) ϕ(9)

    • 9 = 3 2 9 = 3^2 9=32,所以 ϕ ( 9 ) = 9 − 3 = 6 \phi(9) = 9 - 3 = 6 ϕ(9)=93=6
    • 与 9 互质的数有 1, 2, 4, 5, 7, 8,共 6 个。
  2. 计算 ϕ ( 12 ) \phi(12) ϕ(12)

    • 12 = 2 2 × 3 12 = 2^2 \times 3 12=22×3,所以:
      ϕ ( 12 ) = 12 × ( 1 − 1 2 ) × ( 1 − 1 3 ) = 12 × 1 2 × 2 3 = 4 \phi(12) = 12 \times \left(1 - \frac{1}{2}\right) \times \left(1 - \frac{1}{3}\right) = 12 \times \frac{1}{2} \times \frac{2}{3} = 4 ϕ(12)=12×(121)×(131)=12×21×32=4
    • 与 12 互质的数有 1, 5, 7, 11,共 4 个。
  3. 计算 ϕ ( 15 ) \phi(15) ϕ(15)

    • 15 = 3 × 5 15 = 3 \times 5 15=3×5,且 3 和 5 互质,所以:
      ϕ ( 15 ) = ϕ ( 3 ) × ϕ ( 5 ) = ( 3 − 1 ) × ( 5 − 1 ) = 2 × 4 = 8 \phi(15) = \phi(3) \times \phi(5) = (3 - 1) \times (5 - 1) = 2 \times 4 = 8 ϕ(15)=ϕ(3)×ϕ(5)=(31)×(51)=2×4=8
    • 与 15 互质的数有 1, 2, 4, 7, 8, 11, 13, 14,共 8 个。

欧拉定理

欧拉函数的一个重要应用是欧拉定理(Euler’s Theorem),该定理指出:

  • 如果两个正整数 a a a n n n 互质,即 gcd ⁡ ( a , n ) = 1 \gcd(a, n) = 1 gcd(a,n)=1,那么:
    a ϕ ( n ) ≡ 1 ( m o d n ) a^{\phi(n)} \equiv 1 \pmod{n} aϕ(n)1(modn)

欧拉定理是费马小定理的推广。费马小定理是欧拉定理的一个特例,当 n n n 是质数 p p p 时, ϕ ( p ) = p − 1 \phi(p) = p - 1 ϕ(p)=p1,所以:
a p − 1 ≡ 1 ( m o d p ) a^{p-1} \equiv 1 \pmod{p} ap11(modp)
简单练习
ACWing 快速幂求逆元
题目要求对给定的每一对 a i , p i a_i, p_i ai,pi,求出 a i ( m o d p i ) a_i\pmod {p_i} ai(modpi)的逆元
对于给定的每一对 a i , p i a_i, p_i ai,pi,我们首先确认 a i a_i ai 是否与 p i p_i pi 互质(因为 p i p_i pi 是质数,所以除非 a i a_i ai p i p_i pi 的倍数,否则它们总是互质的)。然后使用快速幂算法计算 a i p i − 2 m o d    p i a_i^{p_i-2} \mod p_i aipi2modpi 来找到乘法逆元。
快速幂算法
快速幂算法是一种用于计算大幂次方对某个数取模的结果的有效方法。它利用了指数运算的性质,将时间复杂度从 O ( n ) O(n) O(n) 降低到了 O ( log ⁡ n ) O(\log n) O(logn),其中 n n n 是指数。基本思想是通过将指数分解为二进制形式来减少乘法次数。例如,计算 a 5 a^5 a5 可以看作 a 10 1 2 a^{101_2} a1012(二进制表示),因此可以通过两次平方再乘一次原数得到结果。

#include <iostream>
using namespace std;
typedef long long ll;
ll pow(ll a, ll b, ll mod){
    ll res = 1;
    while(b){
        if(b & 1){
            res *= a;
            res %= mod;
        }
        b >>= 1;
        a *= a;
        a %= mod;
    }
    return res;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin>>n;
    while(n--) {
        ll a, b, mod;
        cin >> a >> b;
        if(a % b)
            cout << pow(a, b-2, b) << endl;
        else
            cout<<"impossible"<<endl;
    }
    return 0;
}

扩展欧几里得算法

扩展欧几里得算法(Extended Euclidean Algorithm)不仅可以用来计算两个整数的最大公约数(GCD),还可以用来求解贝祖等式(Bézout’s identity),即对于给定的整数 a a a b b b,找到整数 x x x y y y,使得:

a × x + b × y = gcd ⁡ ( a , b ) a \times x + b \times y = \gcd(a, b) a×x+b×y=gcd(a,b)

a a a m m m 互质时, gcd ⁡ ( a , m ) = 1 \gcd(a, m) = 1 gcd(a,m)=1,因此贝祖等式可以写成:

a × x + m × y = 1 a \times x + m \times y = 1 a×x+m×y=1

将这个等式对 m m m 取模,可以得到:

a × x ≡ 1 ( m o d m ) a \times x \equiv 1 \pmod{m} a×x1(modm)

这意味着 x x x 就是 a a a 在模 m m m 下的乘法逆元。


伪代码
函数 ExtendedEuclidean(a, b):
    如果 b == 0:
        返回 (a, 1, 0)  // gcd(a, b) = a,且 x = 1, y = 0
    否则:
        (gcd, x1, y1) = ExtendedEuclidean(b, a mod b)  // 递归调用
        x = y1
        y = x1 - floor(a / b) * y1
        返回 (gcd, x, y)

伪代码说明
  1. 输入

    • 两个整数 a a a b b b
  2. 输出

    • 返回一个三元组 ( gcd , x , y ) (\text{gcd}, x, y) (gcd,x,y),其中:
      • gcd \text{gcd} gcd a a a b b b 的最大公约数。
      • x x x y y y 是满足贝祖等式 a × x + b × y = gcd ( a , b ) a \times x + b \times y = \text{gcd}(a, b) a×x+b×y=gcd(a,b) 的整数系数。
  3. 递归终止条件

    • 如果 b = 0 b = 0 b=0,则 gcd ( a , b ) = a \text{gcd}(a, b) = a gcd(a,b)=a,且 x = 1 x = 1 x=1 y = 0 y = 0 y=0
  4. 递归调用

    • 递归调用 ExtendedEuclidean(b, a mod b),得到 gcd ( b , a m o d    b ) \text{gcd}(b, a \mod b) gcd(b,amodb)以及对应的系数 x 1 x_1 x1 y 1 y_1 y1
  5. 更新系数

    • 根据递归结果,更新 x x x y y y
      • x = y 1 x = y_1 x=y1
      • y = x 1 − ⌊ a b ⌋ × y 1 y = x_1 - \left\lfloor \frac{a}{b} \right\rfloor \times y_1 y=x1ba×y1
  6. 返回结果

    • 返回 ( gcd , x , y ) (\text{gcd}, x, y) (gcd,x,y)

设计思路

扩展欧几里得算法的设计思路是基于递归数学归纳法

  1. 递归终止条件

    • b = 0 b = 0 b=0 时, gcd ⁡ ( a , b ) = a \gcd(a, b) = a gcd(a,b)=a,此时 x = 1 x = 1 x=1 y = 0 y = 0 y=0,因为:
      a × 1 + 0 × 0 = a a \times 1 + 0 \times 0 = a a×1+0×0=a
  2. 递归关系

    • 假设我们已经知道 gcd ⁡ ( b , a m o d    b ) \gcd(b, a \mod b) gcd(b,amodb) 以及对应的系数 x 1 x_1 x1 y 1 y_1 y1,即:
      b × x 1 + ( a m o d    b ) × y 1 = gcd ⁡ ( b , a m o d    b ) b \times x_1 + (a\mod b) \times y_1 = \gcd(b, a \mod b) b×x1+(amodb)×y1=gcd(b,amodb)
    • 由于 gcd ⁡ ( a , b ) = gcd ⁡ ( b , a m o d    b ) \gcd(a, b) = \gcd(b, a \mod b) gcd(a,b)=gcd(b,amodb),同时 a m o d    b = ( a − ⌊ a b ⌋ × b ) a\mod b = (a - \left\lfloor \frac{a}{b} \right\rfloor \times b) amodb=(aba×b)我们可以将上式改写为:
      b × x 1 + ( a − ⌊ a b ⌋ × b ) × y 1 = gcd ⁡ ( a , b ) b \times x_1 + (a - \left\lfloor \frac{a}{b} \right\rfloor \times b) \times y_1 = \gcd(a, b) b×x1+(aba×b)×y1=gcd(a,b)
    • 整理后得到:
      a × y 1 + b × ( x 1 − ⌊ a b ⌋ × y 1 ) = gcd ⁡ ( a , b ) a \times y_1 + b \times (x_1 - \left\lfloor \frac{a}{b} \right\rfloor \times y_1) = \gcd(a, b) a×y1+b×(x1ba×y1)=gcd(a,b)
    • 因此,系数 x x x y y y 可以表示为:
      x = y 1 x = y_1 x=y1
      y = x 1 − ⌊ a b ⌋ × y 1 y = x_1 - \left\lfloor \frac{a}{b} \right\rfloor \times y_1 y=x1ba×y1
  3. 递归调用

    • 通过递归调用,逐步缩小问题规模,直到 b = 0 b = 0 b=0,然后回溯计算系数 x x x y y y

示例

假设我们调用 ExtendedEuclidean(30, 12),算法的执行过程如下:

  1. 第一次调用

    • a = 30 a = 30 a=30, b = 12 b = 12 b=12
    • a m o d    b = 30 m o d    12 = 6 a \mod b = 30 \mod 12 = 6 amodb=30mod12=6
    • 递归调用 ExtendedEuclidean(12, 6)
  2. 第二次调用

    • a = 12 a = 12 a=12, b = 6 b = 6 b=6
    • a m o d    b = 12 m o d    6 = 0 a \mod b = 12 \mod 6 = 0 amodb=12mod6=0
    • 递归调用 ExtendedEuclidean(6, 0)
  3. 第三次调用

    • a = 6 a = 6 a=6, b = 0 b = 0 b=0
    • 返回 ( 6 , 1 , 0 ) (6, 1, 0) (6,1,0)
  4. 回溯更新系数

    • 在第二次调用中:
      • gcd = 6 \text{gcd} = 6 gcd=6
      • x = y 1 = 0 x = y_1 = 0 x=y1=0
      • y = x 1 − ⌊ 12 6 ⌋ × y 1 = 1 − 2 × 0 = 1 y = x_1 - \left\lfloor \frac{12}{6} \right\rfloor \times y_1 = 1 - 2 \times 0 = 1 y=x1612×y1=12×0=1
      • 返回 ( 6 , 0 , 1 ) (6, 0, 1) (6,0,1)
  5. 回溯更新系数

    • 在第一次调用中:
      - gcd = 6 \text{gcd} = 6 gcd=6
      • x = y 1 = 1 x = y_1 = 1 x=y1=1
      • y = x 1 − ⌊ 30 12 ⌋ × y 1 = 0 − 2 × 1 = − 2 y = x_1 - \left\lfloor \frac{30}{12} \right\rfloor \times y_1 = 0 - 2 \times 1 = -2 y=x11230×y1=02×1=2
      • 返回 ( 6 , 1 , − 2 ) (6, 1, -2) (6,1,2)

最终结果为 ( 6 , 1 , − 2 ) (6, 1, -2) (6,1,2),即:
30 × 1 + 12 × ( − 2 ) = 6 30 \times 1 + 12 \times (-2) = 6 30×1+12×(2)=6


代码实战

ACWing 扩展欧几里得算法

#include <iostream>
using namespace std;

typedef long long ll;  // 使用long long类型来避免大数溢出

// 变量用于存储扩展欧几里得算法计算过程中产生的系数x和y
ll coefficientX = 1, coefficientY = 0;

/**
 * 扩展欧几里得算法函数
 * @param a 第一个整数
 * @param b 第二个整数
 * @return 返回a和b的最大公约数gcd(a, b)
 */
ll extendedEuclidean(ll a, ll b) {
    if (b == 0) {
        return a;  // 当b为0时,返回a作为最大公约数
    }
    ll gcd = extendedEuclidean(b, a % b);  // 递归调用,计算gcd
    ll tempCoefficientY = coefficientY;  // 暂存当前的coefficientY值
    coefficientY = coefficientX - (a / b) * coefficientY;  // 更新coefficientY
    coefficientX = tempCoefficientY;  // 更新coefficientX
    return gcd;
}

int main() {
    ios::sync_with_stdio(false);  // 提高I/O效率
    cin.tie(0);
    cout.tie(0);

    int testCases;
    cin >> testCases;  // 输入测试案例的数量

    while (testCases--) {
        ll numberA, modulusB;
        cin >> numberA >> modulusB;  // 输入每个测试案例的a和b
        
        // 初始化系数
        coefficientX = 1;
        coefficientY = 0;
        
        // 调用扩展欧几里得算法
        ll gcdResult = extendedEuclidean(numberA, modulusB);
        
        cout << coefficientX <<" "<< coefficientY << endl;
    }

    return 0;
}

总结

乘法逆元在模运算中扮演着重要的角色,特别是在密码学和数论中。扩展欧几里得算法提供了一种高效的方法来计算乘法逆元,尤其是在模数较大的情况下。通过理解逆元的概念和相关定理,我们可以更好地应用这些工具来解决实际问题。

;