逆元

逆元


892 字 约 5 分钟

定义

非零实数 a∈Ra \in \mathbb{R} 的乘法逆元即为它的倒数 a−1a^{-1} ,类似地,在模 pp 意义下,我们可以定义 aa 的乘法逆元 a−1  mod  ma^{-1} \,\, mod \,\, m ,即为模逆元。

标准定义:

对于非零整数a,m,如果存在b使得ab≡1(mod  m),则称b是a在模m意义下的逆元对于非零整数 a,m ,如果存在 b 使得 ab \equiv 1 (mod \,\, m) ,则称 b 是 a 在模 m 意义下的逆元

这相当于 bb 是线性同余方程 ax≡1(mod  m)ax \equiv 1 (mod \,\, m) 的解,则当且仅当 gcd(a,m)=1gcd(a,m) = 1 时,方程有解即 aa 的逆元存在。

求解方法

费马小定理快速幂法

这一方法主要适用于 mm 为素数的情形。

首先,有费马小定理

ap−1≡1(mod  p)a^{p-1} \equiv 1 (mod \,\, p)

其中 pp 为素数,且 gcd(a,p)=1gcd(a,p) = 1

我们要对线性同余方程

ax≡1(mod  p)ax \equiv 1(mod \,\, p)

求解,可以发现存在解为

ap−1≡ax(mod  p)a^{p-1} \equiv ax (mod \,\, p)

则此时

x=ap−2  mod  px = a^{p-2} \,\, mod \,\, p

xx 即为 aa 在模 pp 意义下的逆元,使用快速幂求解。时间复杂度为 O(log p)O(log \, p)

JavaScript
// Binary exponentiation.
int pow(int a, int b, int m) {
  long long res = 1, po = a;
  for (; b; b >>= 1) {
    if (b & 1) res = res * po % m;
    po = po * po % m;
  }
  return res;
}

// Returns the modular inverse of a prime modulo p.
int inverse(int a, int p) { return pow(a, p - 2, p); }

扩展欧几里得算法

此方法适用于任意正整数 mm 满足 gcd(a,m)=1gcd(a,m) = 1

可知求解线性同余方程

ax≡1(mod  m)ax \equiv 1(mod \,\, m)

即等价于求解二元一次不定方程

ax+my=1ax+my = 1

其中 xx 即为所求逆元,此时可用扩展欧几里得算法求解。时间复杂度为 O(log min{a,m})O(log \, min\{a,m\})

JavaScript
// Extended Euclidean algorithm.
void ex_gcd(int a, int b, int& x, int& y) {
  if (!b) {
    x = 1;
    y = 0;
  } else {
    ex_gcd(b, a % b, y, x);
    y -= a / b * x;
  }
}

// Returns the modular inverse of a modulo m.
// Assumes that gcd(a, m) = 1, so the inverse exists.
int inverse(int a, int m) {
  int x, y;
  ex_gcd(a, m, x, y);
  return (x % m + m) % m;
}

线性递推

此方法适用于对前 nn 个整数进行批量求逆元。

我们设 inviinv_i 为 ii 在模 pp 意义下的逆元。

首先,对于 n=1n=1 ,有逆元 inv1=1inv_1 = 1

从而对于任意正整数 1<i≤n1 < i \le n ,考虑带余除法。

p=qi+r(0≤r<i)p = qi + r (0 \le r < i)

其中 q=⌊pi⌋,r=p  mod  iq = \lfloor \dfrac {p}{i} \rfloor ,r = p \,\, mod \,\, i

两边对 pp 取模,得到

0≡qi+r  mod  p  (mod  p)0 \equiv qi + r \,\, mod \,\, p \,\, (mod \,\, p)

两边同时乘以 invi⋅invrinv_i \cdot inv_r 得

0≡q⋅invr+invi  (mod  p)0 \equiv q \cdot inv_r + inv_i \,\, (mod \,\, p)

从而

invi≡−q⋅invr  (mod  p)inv_i \equiv -q \cdot inv_r \,\, (mod \,\, p)

又 r<ir < i ,则显然对任意正整数 ii 均可由该地推关系计算得到逆元。

总体时间复杂度为 O(n)O(n)

前缀积逆推

设有序列 a1,a2,…,ana_1,a_2, \dots ,a_n ,要求出它们的逆元。若直接使用扩欧或者快速幂计算逆元,则时间复杂度为 O(nlog p)O(nlog \, p) ,现在给出 O(n)O(n) 的方法。

首先计算前缀积 pren=a1a2…an  (mod  p)pre_n = a_1a_2 \dots a_n \,\, (mod \,\, p) ,然后计算 prefixnprefix_n 的逆元 invpreninv_{pre_n}

随后观察逆推关系,显然我们有:

prek=prek−1⋅akpre_k = pre_{k-1} \cdot a_k

则:

ak=prekprek−1a_k = \frac {pre_k}{pre_{k-1}}

又可知在模 pp 意义下, prekprek−1=prek⋅invprek−1\dfrac {pre_k}{pre_{k-1}} = pre_k \cdot inv_{pre_{k-1}}

两边同时求逆元得:

invak=invprek⋅prek−1inv_{a_k} = inv_{pre_k} \cdot pre_{k-1}

则我们只需要求出所有的 invpreiinv_{pre_i} ,如何求解呢?同样是使用递推关系来求解。

同样的,有:

prek=prek−1⋅akpre_k = pre_{k-1} \cdot a_k

两边同时求逆元得:

invprek=invprek−1⋅invakinv_{pre_k} = inv_{pre_{k-1}} \cdot inv_{a_k} invprek−1=invprekinvakinv_{pre_{k-1}} = \frac {inv_{pre_k}}{inv_{a_k}}

同理,模 pp 意义下,invprekinvak=invprek⋅ak\dfrac {inv_{pre_k}}{inv_{a_k}} = inv_{pre_k} \cdot a_k

综上,有:

invprek−1=invprek⋅akinv_{pre_{k-1}} = inv_{pre_k} \cdot a_k

通过这个方法,可以做到逆推求解每个 aia_i 的逆元。

评论

评论会在审核后展示

正在加载人机验证...

查看原图