【简单数论】求乘法逆元

在数论中,对于模乘运算,除法是一个很“不好”的运算。
先来看模的性质

  1. ( a + b ) % p = ( a % p + b % p ) % p (a+b)\%p=(a\%p+b\%p)\%p (a+b)%p=(a%p+b%p)%p
  2. ( a − b ) % p = ( a % p − b % p ) % p (a-b)\%p=(a\%p-b\%p)\%p (ab)%p=(a%pb%p)%p
  3. ( a ∗ b ) % p = ( a % p ∗ b % p ) % p (a*b)\%p=(a\%p * b\%p)\%p (ab)%p=(a%pb%p)%p
    注意:除法运算不满足。

举个例子,假设给一个数 b b b,和一个数 a a a,满足 b ∣ a b|a ba,现在要求 a b % p \frac{a}{b}\%p ba%p的值,这个值是不等于 ( a % p ) ( b % p ) \frac{(a\%p)}{(b\%p)} (b%p)(a%p),直接做的话,难免会有些精度问题。所以要设法把运算转换为乘法,也即求一个 x x x,使得 a b ≡ a x ( m o d p ) \frac{a}{b}≡ax(modp) baax(modp)。我们把这个 x x x记作 b − 1 ( m o d p ) b^{-1}(modp) b1(modp),称为 b b b p p p的乘法逆元。

要学习求逆元,需要先掌握快速幂扩展欧几里得算法,可以参考我的这篇文章【简单数论】欧拉函数、快速幂、扩展欧几里得算法
对于 p p p是否为质数,有两种求解方法。其中法一适用于 p p p是质数,而法二适用于 p p p是任何数

1、快速幂求逆元

当要模的数 p p p是质数时,进行如下推导:

a b ≡ a x ( m o d p ) \frac{a}{b}≡ax(modp) baax(modp),要求 x x x
两侧同乘 b b b,得 a ≡ b a x ( m o d p ) a≡bax(modp) abax(modp)
消掉 a a a,得 b x ≡ 1 ( m o d p ) bx≡1(modp) bx1(modp)
根据费马小定理,有 b p − 1 ≡ 1 ( m o d p ) b^{p-1}≡1(modp) bp11(modp)
提出一个 b b b,得 b ⋅ b p − 2 ≡ 1 ( m o d p ) b·b^{p-2}≡1(modp) bbp21(modp)
对比可得: x = b p − 2 x=b^{p-2} x=bp2
b b b p p p的倍数时 b x ≡ 1 ( m o d p ) bx≡1(modp) bx1(modp)一定无法成立,所以无解

因此,只要求 b p − 2 b^{p-2} bp2就是答案了,这里直接使用快速幂即可。给出代码:

#include <iostream>

using namespace std;

typedef long long LL;

LL qmi(int a, int b, int p)
{
    LL res = 1 % p;
    while (b)
    {
        if (b & 1) res = res * a % p;
        a = (LL)a * a % p;
        b >>= 1;
    }
    
    return res;
}

int main()
{
   	//求解a模p的乘法逆元
    int a, p;
    scanf("%d%d", &a, &p);
    if (a % p == 0) puts("impossible");
    else printf("%d\n", qmi(a, p - 2, p));
    
    return 0;
}

2、扩展欧几里得算法求逆元

p p p不是质数时,就不能用费马小定理了。这时候进行如下推导:

b x ≡ 1 ( m o d p ) bx≡1(modp) bx1(modp),要求 x x x
也即有 b x % p = 1 bx\%p=1 bx%p=1
也即有 b x + p y = 1 bx+py=1 bx+py=1
此形式和扩展欧几里得算法相同,因此只要最大公约数为1,所求 x x x即为 b − 1 b^{-1} b1

通过这个,也得到了 b b b p p p的乘法逆元存在的条件 b b b p p p互质
给出代码:

#include <iostream>

using namespace std;

int exgcd(int a, int b, int &x, int &y)
{
    if (!b)
    {
        x = 1, y = 0;
        return a;
    }
    int d = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}

int main()
{
    int a, p;
    cin >> a >> p;
    int x, y;
    //如果a和p不沪互质(也即最大公约数不为1),那么无解
    //求出来的逆元是落在0~p-1之间的
    if (exgcd(a, p, x, y) == 1) printf("%d\n", (x % p + p) % p);
    else printf("impossible\n");
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值