【数论】乘法逆元

【数论】乘法逆元

Definition

对于一个数 \(x\) 和一个模数 \(p\),若存在一个数字 \(y\),满足

\[x \times y \equiv 1 \pmod p \]

则称 \(y\)\(x\) 在模 \(p\) 意义下的逆元,记做 \(x^{-1}~\equiv y \pmod p\)

一个数字逆元在模意义下的运算中可以完全取代该数字的倒数。例如 \(\frac{x}{y}~\equiv x \times y^{-1} \pmod p\),其中 \(y^{-1}\) 代表 \(y\) 的逆元。

Algorithm

Lemma

首先需要指出的是,一个数 \(x\) 在模 \(p\) 意义下存在逆元,当且仅当 \(x\)\(p\) 互质。

Proof

这里只证明当 \(x\)\(p\) 不互质时不存在逆元。对于逆元的存在性,由于下面的部分给出了逆元的构造算法,这就已经证明了在互质时逆元是存在的。

反证法,设对于任意的 \(x \in Z^+\),存在 \(x^{-1} \in Z^+\)

\[x \times x^{-1} \equiv 1 \pmod p~~~~~~(1) \]

\[\gcd(x,~p) = d \neq 1~~~~~~(2) \]

根据同余的定义,\((1)\) 可以写成:

\[x \times x^{-1} = k \times p + 1~~~~~~(3) \]

其中 \(k\) 是一个非负整数。

\((3)\) 的等号两侧同时除以 \((2)\) 中的 \(d\)

\[\frac{x \times x^{-1}}{d}~=~\frac{k \times p + 1}{d}~~~~~~(4) \]

整理得到

\[\frac{x}{d} \times x^{-1}~=~\frac{p}{d} \times k + \frac{1}{d}~~~~~~(5) \]

因为 \(d = \gcd(x, p)\),所以 \(d\) 一定是 \(x\)\(p\) 的因数。所以

\(\frac{x}{d}\)\(\frac{p}{d}\) 都是整数,进而 \(\frac{p}{d} \times k\) 是整数,\(\frac{x}{d} \times x^{-1}\) 是整数。

而因为 \(d \neq 1\),所以 \(\frac{1}{d}\) 一定不是整数,因此 \(\frac{p}{d} \times k + \frac{1}{d}\) 不是整数。

于是等号左侧是整数,等号右侧不是整数,左侧一定不等于右侧,产生矛盾。这就矛盾证明了 \(x\) 在模 \(p\) 意义下存在逆元仅当 \(x\)\(p\) 互质。


以下介绍求逆元的算法:

求单个数字的逆元

Algorithm 1

\[x \times x^{-1}~\equiv 1 \pmod p \]

显然可以转化成方程

\(x \times x^-1 = 1 + kp\)

\(y = -k\),移项得到

\[x \times x^{-1} + y \times p = 1 \]

注意到这个式子就是扩展欧几里得算法所求的式子

\[ax + by = 1 \]

只不过 \(x\) 作为一个常数,是欧式式子里的 \(a\),同理 \(p\) 是欧式式子里的 \(b\)。使用扩展欧几里得算法求解上面这个式子即可。时间复杂度 \(O(\log x)\)

Algorithm 2

根据欧拉定理

\(x^{\phi(p)} \equiv 1 \pmod p\)

其中 \(\phi\) 为欧拉函数,\(\phi(p)\) 表示小于 \(p\) 的正整数中与 \(p\) 互质的数的个数。

等式两侧同乘 \(x^{-1}\) 可以得到

\[x^{\phi(p) - 1} \equiv x^{-1} \pmod p \]

显然当 \(p\) 是一个质数时,\(\phi(p) = p - 1\),这时可以 \(O(1)\) 算出 \(\phi(p) - 1\) 的值,即可用快速幂 \(O(\log x)\) 求出 \(x\) 的逆元。这个算法好写好记,常数也较小。一般当 \(p\)int 范围内的质数时选择此算法。当 \(p\) 不在 int 范围内时,由于快速幂时需要两个 long long 相乘,会爆精度。

有关欧拉定理的证明可以看这里

\(n\) 以内所有正整数模 \(p\) 的逆元

显然,由于 \(n\) 以内所有正整数都有在模 \(p\) 意义下的逆元,所以 \(p\)\(n\) 以内的所有数互质。

结论:设 \(inv_i\)\(i\) 的逆元,则有递推式

\[inv_i \equiv \left\lfloor\frac{p}{i}\right\rfloor \times inv_{p \bmod i} \pmod p \]

边界条件为

\[inv_1 = 1 \]

Proof

首先 \(inv_1 = 1\) 显然成立。

对于 \(i > 1\),写出 \(p\) 除以 \(i\) 的带余除法表达式:

\[p = ki + r \]

其中 \(r \in [0, i - 1]\)

等式两侧对 \(p\) 取余数,有

\[0 \equiv ki + r \pmod p \]

移项得到

\[r \equiv -ki \pmod p \]

两侧同乘 \(i^{-1} \times r^{-1}\),整理得到

\[i^{-1} \equiv -kr^{-1} \pmod p \]

由于 \(k = \left\lfloor\frac{p}{i}\right\rfloor\)\(r = p \bmod i\),所以原式得证。

又因为 \(r < i\),所以在计算 \(inv_i\) 时,\(inv_r\) 已经被计算完成,所以上述递推可以完成。

证毕。

这样做的时间复杂度显然是 \(O(n)\)

\(n!\) 的逆元

因为 \((n!)^{-1} \equiv \frac{1}{n!} \equiv \prod_{i = 1}^n n^{-1}\),所以线性筛出 \(n\) 以内所有数字的逆元时,可以顺便求出 \(n!\) 的逆元。时间复杂度 \(O(n)\)

Code

Ex_Gcd

#include <iostream>

typedef long long int ll;

ll x, p;

void Ex_gcd(const ll a, const ll b, ll &X, ll &Y);

int main() {
  std::cin >> x >> p;
  ll a, b;
  Ex_gcd(x, p, a, b);
  std::cout << (a % p + p) % p << std::endl;
  return 0;
}

void Ex_gcd(const ll a, const ll b, ll &X, ll &Y) {
  if (b == 0) {
    X = 1; Y = 0;
  } else {
    Ex_gcd(b, a % b, Y, X);
    Y -= a / b * X;
  }
}

欧拉定理

#include <iostream>

typedef long long int ll;

ll X, p;

ll mpow(ll x, ll y);

int main() {
  std::cin >> X >> p;
  std::cout << mpow(X, p - 2) << std::endl;
  return 0;
}

ll mpow(ll x, ll y) {
  ll _ret = 1;
  while (y) {
    if (y & 1) (_ret *= x) %= p;
    y >>= 1;
    (x *= x) %= p;
  }
  return _ret;
}

线性求逆元

这里的 factinv 即为阶乘逆元。

#include <cstdio>

const int maxn = 3000005;

int n, p;
int inv[maxn], factinv[maxn];

int main() {
  scanf("%d%d", &n, &p);
  factinv[1] = inv[1] = 1;
  printf("%d\n", 1);
  for (int i = 2; i <= n; ++i) {
    inv[i] = 1ll * (p - p / i) * inv[p % i] % p;
    printf("%d\n", inv[i]);
    factinv[i] = 1ll * factinv[i - 1] * inv[i] % p;
  }
  return 0;
}
posted @ 2020-01-06 20:29  一扶苏一  阅读(818)  评论(0编辑  收藏  举报