【数论】乘法逆元

【数论】乘法逆元

Definition

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

[x imes y equiv 1 pmod p ]

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

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

Algorithm

Lemma

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

Proof

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

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

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

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

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

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

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

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

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

整理得到

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

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

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

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

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


以下介绍求逆元的算法:

求单个数字的逆元

Algorithm 1

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

显然可以转化成方程

(x imes x^-1 = 1 + kp)

(y = -k),移项得到

[x imes x^{-1} + y imes 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 leftlfloorfrac{p}{i} ight floor imes inv_{p mod 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} imes r^{-1}),整理得到

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

由于 (k = leftlfloorfrac{p}{i} ight floor)(r = p mod 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
", 1);
  for (int i = 2; i <= n; ++i) {
    inv[i] = 1ll * (p - p / i) * inv[p % i] % p;
    printf("%d
", inv[i]);
    factinv[i] = 1ll * factinv[i - 1] * inv[i] % p;
  }
  return 0;
}
原文地址:https://www.cnblogs.com/yifusuyi/p/12158367.html