逆元

逆元


定义

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

标准定义:

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

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

求解方法

费马小定理快速幂法

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

首先,有费马小定理

ap11(modp)a^{p-1} \equiv 1 (mod \,\, p)

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

我们要对线性同余方程

ax1(modp)ax \equiv 1(mod \,\, p)

求解,可以发现存在解为

ap1ax(modp)a^{p-1} \equiv ax (mod \,\, p)

则此时

x=ap2modpx = a^{p-2} \,\, mod \,\, p

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

// 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

可知求解线性同余方程

ax1(modm)ax \equiv 1(mod \,\, m)

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

ax+my=1ax+my = 1

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

// 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_iii 在模 pp 意义下的逆元。

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

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

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

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

两边对 pp 取模,得到

0qi+rmodp(modp)0 \equiv qi + r \,\, mod \,\, p \,\, (mod \,\, p)

两边同时乘以 inviinvrinv_i \cdot inv_r

0qinvr+invi(modp)0 \equiv q \cdot inv_r + inv_i \,\, (mod \,\, p)

从而

inviqinvr(modp)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(nlogp)O(nlog \, p) ,现在给出 O(n)O(n) 的方法。

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

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

prek=prek1akpre_k = pre_{k-1} \cdot a_k

则:

ak=prekprek1a_k = \frac {pre_k}{pre_{k-1}}

又可知在模 pp 意义下, prekprek1=prekinvprek1\dfrac {pre_k}{pre_{k-1}} = pre_k \cdot inv_{pre_{k-1}}

两边同时求逆元得:

invak=invprekprek1inv_{a_k} = inv_{pre_k} \cdot pre_{k-1}

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

同样的,有:

prek=prek1akpre_k = pre_{k-1} \cdot a_k

两边同时求逆元得:

invprek=invprek1invakinv_{pre_k} = inv_{pre_{k-1}} \cdot inv_{a_k} invprek1=invprekinvakinv_{pre_{k-1}} = \frac {inv_{pre_k}}{inv_{a_k}}

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

综上,有:

invprek1=invprekakinv_{pre_{k-1}} = inv_{pre_k} \cdot a_k

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

🐙