乘法逆元 | 分数 (小数) 取模

作用

比赛中大多数情况为了避免大整数运算,大多都会要求答案对一个数(大多是质数)取模。

  1. 题目中用到除法
    因为$\frac{a}{b} \% p \neq (\frac{a\%p }{b\%p}) \%p $ ,此时就无法计算,需要用到乘法逆元

  2. 当题目遇到小数
    因为取模运算时对于整数来说的,所以无法计算,需要用到乘法逆元。
    虽然c语言有fmod函数,但是函数fmod后结果还是小数,如果题目要求mod后为整数,还需要用到逆元。

定义

逆元素(简称逆元) 是指一个可以取消另一给定元素运算的元素,如加法中的加法逆元(相反数)和乘法中的倒数。简单来说,A的逆元就是能抵消A的影响的元素。

如果说 a 在模 p 意义下的乘法逆元是 x,
那么 \(ax \equiv 1 \pmod{p}\)
(ax模p等于1模p)

不难发现,如同 0 没有倒数一样,0没有乘法逆元
a 在模 p 意义下的乘法逆元均为 [1,p−1] 中的整数。

然后,因为求模意义下的a/b,就是求\(bc \equiv a \pmod{p}\)的c,
这时给a乘上b的逆元就消去了乘b的影响,结果为c,即

\[b\, c \, ×\,b^{-1}≡a \, × \,b^{-1}\pmod{p} \]

\[c≡a \, × \,b^{-1} \pmod{p} \]

模意义下除以一个数就等价于乘这个数的乘法逆元

性质

  1. 存在唯一性:对 a 来说,若它有逆元,则在p范围内一定只有一个逆元。

  2. a 在模 p 意义下的乘法逆元存在,当且仅当 a,p 互质。

这也在一定程度上解释了大多数题目模数为什么为质数的原因:(设模数为 p)可以保证在$ [1,p - 1]$ 中的所有整数都有模 p 意义下的逆元。

求逆元

快速幂求逆元

对于\(a x\equiv 1 \pmod {p}\)求x
因为: a 在模 p 意义下的乘法逆元存在,当且仅当 a,p 互质。
有费马定理得到:

\[a^{p-1}\equiv 1 \pmod {p} \]

那么

\[a × a^{p-2}\equiv 1 \pmod {p} \]

那么a的逆元就是:\((a^{p-2}) \mod p\)

代码:

int qmi(int a, int k, int p)
{
    int res = 1;
    while(k)
    {
        if(k & 1) res = res * a % p;
        k >>= 1;
        a = a * a % p;
    }
    return res;
}
int get(int a,int p){
   if(a % p == 0) return -1;
   else  return qmi(a,p-2,p);
}

拓展欧几里得求逆元

补充:下面3个公式都成立
image

不难发现\(ax \equiv 1 \pmod {p}\)等价于\(ax + py = 1\)

点击查看证明

将同余号换为等号得:

\[ax\,mod\,p = 1\,mod\,p \]

移项得:

\[(ax - 1)\,mod\,p = 0 \]

由上式可知\(ax - 1\)为 p 的整数倍,不妨设其为 p的k倍$ (k \in Z)$:

\[ax−1=kp \]

继续移项得:

\[ax - kp = 1 \]

\(y = -k\)就写成了上文的形式:

\[ax + py = 1 \]

又因为乘法逆元存在,当且仅当 a,p 互质,即$ gcd(a,p) = 1$。

故我们可以直接运用拓展欧几里得算法解 \(ax + py = gcd(a,p)\)这个方程。

时间复杂度: \(log(p)\)
手算模拟:
image

整个求解的过程就是使用辗转相除法求两个数的公约数,并且我们知道公约数的结果为1,所以一直计算到1为止即可。
例1:求在p = 14的条件下 a = 5的逆元
等号左边为余数

\[4 = 14 - 5 * 2 \]

\[1 = 5 − 4 ∗ 1 \]

等号左侧得到1,进行等式代换:

\[1 = 5 − ( 14 − 5 ∗ 2 ) ∗ 1 \]

\[1 = 5 − 14 ∗ 1 + 5 ∗ 2 \]

\[1 = 5 ∗ 3 − 14 ∗ 1 \]

得到结果为3
例2:求在p = 18的条件下 a = 5的逆元

前面的步骤相同,使用辗转相除,会得到3个式子:

\[3 = 18 − 5 ∗ 3 \]

\[2 = 5 − 3 ∗ 1 \]

\[1 = 3 − 2 ∗ 1 \]

在代换时会发现一个问题,由于式子是奇数个,所以最后整理时a的系数为负:

\[1 = 3 − ( 5 − 3 ∗ 1 ) ∗ 1 \]

\[1 = ( 18 − 5 ∗ 3 ) − ( 5 − ( 18 − 5 ∗ 3 ) ∗ 1 ) ∗ 1 \]

\[1 = 18 − 5 ∗ 3 − 5 + 18 − 5 ∗ 3 \]

\[1 = 18 ∗ 2 − 5 ∗ 7 \]

此时将结果表示出来会是这个样子:

\[1 \equiv 5 \cdot (-7) \bmod 18 \]

但是利用两个数互质的性质以及最小公倍数,我们可以直接得到想要的结果:

\[1 \equiv 5 \cdot (-7) \bmod 18 \equiv 5 \cdot (11) \bmod 18 \]

代码:

#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

long long a, b;
//b是p,x是逆元
int exgcd(long long a, long long b, long long &x, long long &y)
{
    if (b == 0)
    {
        x = 1, y = 0;
        return a;
    }
    int d = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}

int main()
{
    scanf("%lld%lld", &a, &b);
    long long x = 0, y = 0;
    exgcd(a, b, x, y);
    printf("%lld", (x % b + b) % b);//这里防止出现负数
    return 0;
}

原文 1
原文 2

原文 3

posted @ 2022-07-20 14:07  kingwzun  阅读(3406)  评论(0编辑  收藏  举报