• 【初等数论】乘法逆元略解


    乘法逆元

    (1、)定义:(frac{1}{a} equiv bpmod{p}),则称(b)(a)在模(p)意义下的乘法逆元,记为(inv(a))(a^{-1})

    (2、)在模(p)意义下讨论乘法逆元的存在性

    ​  设(a^{-1} = x)

    ​  (acdot x equiv 1)

    ​  (Leftrightarrow a cdot x = 1 - py)

    ​  (Leftrightarrow a x+py = 1)

    ​  裴蜀定理告诉我们,该方程当且仅当(gcd(a,p) = 1)时有解。也就是说,当且仅当(a)与模数(p)互质时存在(a)的乘法逆元。因此大部分有理数取模问题给定的模数都是质数,这样能够在(amod p eq 0)的情况下保证存在(a)的逆元。

    (3、)乘法逆元的四种推导方式

    1. 扩展欧几里得算法

      (a)的逆元,也就是求出(Leftrightarrow a x+py = 1)的一组解,使用扩展欧几里得算法直接求解得(x)即可。

      void exgcd(int& x, int& y, int a, int b) {
        if (!p) {
          x = 1, y = 0;
          return;
        }
        exgcd(y, x, b, a % b);
        y -= a / b * x;
      }
      
    2. 费马小定理/扩展欧拉定理

      费马小定理:对于质数(p)(a^{p} equiv a pmod{p})

      (a^{p-1} equiv 1pmod {p})

      也就是说,(a^{p-2})即为(a mod p)意义下的逆元,使用快速幂求解。

      费马小定理是欧拉定理在(p)为质数时的特殊情况,实现起来没有区别。这里不深入展开叙述欧拉定理。

      LL fpow(LL a, LL p) {
        LL b = p - 2, ret = 1;
        for (; b; b >>= 1) {
          if (b & 1) ret = ret * a % p;
          a = a * a % p;
        }
        return ret;
      }
      
    3. 线性递推(1sim n)的逆元

      显然有(1^{-1} = 1).

      (r = p mod i, k = lfloor frac{p}{i} floor)

      (Rightarrow ki + r equiv 0 pmod p)

      两边同乘(i^{-1}r^{-1}),得

      (kr^{-1} + i^{-1} equiv 0 pmod p)

      (Leftrightarrow i^{-1} = -lfloor frac{p}{i} floor cdot (p mod i)^{-1} pmod p)

      得到如上递推式,解毕。

      void get_inv(int n, int p) {
        inv[1] = 1;
        for (int i = 2; i <= n; ++i)
          f[i] = (LL)(p - p / i) * inv[p%i] % p;//p - p / i:去掉负数
      }
      
    4. 线性递推(a_1 sim a_n)的逆元((a_i in N_+))

      以下表达式均在(mod p) 意义下。

      (f_i = prod_{j = 1}^{i}a_j)

      首先可以线性递推求出(f_{1 sim n})

      我们发现,对((f_i)^{-1})可推导出如下递推式:

      $ f_i imes (f_{i})^{-1} equiv 1$

      (Rightarrow a_i cdot f_{i-1} cdot (f_i)^{-1} equiv 1)

      ((f_i)^{-1} equiv (f_{i+1})^{-1} imes a_{i+1})

      那么我们可以结合费马小定理/扩展欧几里得算法

      求出((f_n)^{-1}),然后倒着线性迭代求出((f_{1 sim n-1})^{-1})

      现在我们求出了原数列前缀积数列的逆元,考虑着手由({f}^{-1})数组推导出({a}^{-1})

      (a_i imes a_i^{-1} equiv 1)

      (Leftrightarrow frac{f_i}{f_{i-1}} cdot a_i^{-1} equiv 1)

      (Leftrightarrow a_i^{-1} equiv frac{f_{i-1}}{f_i})

      (a_i^{-1} equiv f_{i-1} cdot (f_i)^{-1})

      得到如上递推式,解毕。

      因此,最后我们再使用费马小定理求出(a_1^{-1}),就可以递推一遍求出原数列各项的逆元了。

      int a[maxn], inv[maxn], V[maxn], f[maxn], n, k, p;
      LL ans;
      int fpow(LL a, LL b) {
      	LL ret = 1;
      	while (b) {
      		if (b & 1) ret = ret * a % p;
      		b >>= 1;
      		a = a * a % p;
      	}
      	return ret;
      }
      void get_inv() {
      	f[0] = 1;
      	for (int i = 1; i <= n; ++i) {
      		read(a[i]);
      		f[i] = (LL)a[i] * f[i-1] % p;
      	}
      	V[n] = fpow(f[n], p - 2);
      	for (int i = n - 1; i; --i) V[i] = (LL)V[i+1] * (a[i+1]) % p;
      	for (int i = n; i > 1; --i) inv[i] = (LL)f[i-1] * V[i] % p;
      	inv[1] = fpow(a[1], p - 2);
      }
      
  • 相关阅读:
    spring自定义标签
    shell脚本实战
    redis使用场景
    了解并安装Nginx
    查看jar包依赖树
    从一道索引数据结构面试题看B树、B+树
    11条sql技巧
    or/in/union与索引优化
    动态规划
    实现快速迭代的引擎设计
  • 原文地址:https://www.cnblogs.com/TY02/p/11763495.html
Copyright © 2020-2023  润新知