逆元的两种常见计算方法

概念引入

学习逆元之前,首先要知道什么叫逆元

  • 逆元这个概念通常是用来解决除法求模问题的,关于求模,有下面的公式
    ( a + b ) % c = ( a % c + b % c ) % c ( a − b ) % c = ( a % c − b % c ) % c ( a ∗ b ) % c = ( a % c ∗ b % c ) % c (a+b)\%c=(a\%c+b\%c)\%c\\(a-b)\%c=(a\%c-b\%c)\%c\\(a*b)\%c=(a\%c*b\%c)\%c (a+b)%c=(a%c+b%c)%c(ab)%c=(a%cb%c)%c(ab)%c=(a%cb%c)%c
  • 可以看出,对于除法没有相应的取模公式,这是因为除法的类似公式是不成立的,可以举例说明。有时候a和b很大,计算机存不下,不能计算之后再取模,所以我们想找到一些途径在中间过程中取模,这样,就想能不能变除法为乘法呢?
  • b × k % c = 1 b\times k\%c=1 b×k%c=1,则有
    ( a b ) % c = ( a b % c ∗ ( b k ) % c ) = ( a ∗ k ) % c (\frac{a}{b})\%c=(\frac{a}{b}\%c*(bk)\%c)=(a*k)\%c (ba)%c=(ba%c(bk)%c)=(ak)%c
    这样就把除法转化为了乘法,这里面的k就叫做b关于c的乘法逆元,写成数学表达式就是
    b k ≡ 1 ( m o d c ) bk\equiv1\pmod c bk1(modc)
    读作 b k bk bk 1 1 1对于模 c c c同余
  • 初学时很容易就能联想到我们曾经学过的倒数这一概念,也就是一个数的倒数乘以自身等于1,在这里逆元是数论倒数(算术倒数)并非普通意义上的倒数,是取模引入的概念,不要弄混

求解方法

费马小定理

这个定理的内容是这样的:
如果p为质数,a不是p的倍数,那么有
a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv1\pmod p ap11(modp)

  • 关于这个定理,不作证明,可以自行百度。研究问题有时候也不要太面面俱到,关系不大。以有涯逐无涯,殆矣
  • 观察此定理和上面的式子,可以发现这个式子可以变化为
    a × a p − 2 ≡ 1 ( m o d p ) a\times a^{p-2}\equiv1\pmod p a×ap21(modp)
    正好和上面式子里的b和k对应,也就是说,a的乘法逆元为ap-2,当然,需要满足费马小定理的条件
    模板题
    尝试一下
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 100;
const double eps = 1e-6;
int Data[MAXN];
ll GCD(ll a, ll b){
    return b == 0 ? a : GCD(b, a % b);
}
ll fastpow(ll base, ll power, ll MOD){
    ll ans=  1;
    while(power){
        if(power & 1) ans = ans * base % MOD;
        base = base * base % MOD;
        power >>= 1;
    }
    return ans % MOD;
}
int main(){
    int t;
    ll y, p;
    cin >> t;
    while(t--){
        cin >> y >> p;
        if(GCD(y, p) != 1) cout << -1 << "\n";
        else cout << fastpow(y, p - 2, p) << "\n";
    }
    return 0;
}
  • 使用时候注意需要满足条件

扩展欧几里德

  • 还是这样一个式子

b k ≡ 1 ( m o d c ) bk\equiv1\pmod c bk1(modc)

  • 回顾一下,b关于c的逆元为k,现在我们要求k,这里面b、c都已知
  • 很容易能够知道GCD(b, c)或者说b和c的最大公约数应该为1,或者说b,c互质,如果不互质那么模就不会是1,所以这个方程等价于求下面这个方程的解
    b x + c y = 1 bx+cy=1 bx+cy=1
  • 求出的x即为逆元(还需要考虑负数的情况),至于方程的解法,因为b和c互质,右边是1,所以是一个明显的扩展欧几里德(关于扩展欧几里德请看这篇),如果让判断是否无解,那么b和c如果不互质,那么无解
  • 如果解出来x是负数,那么需要通过c来调整x为正数,具体如下
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 100;
const double eps = 1e-6;
int Data[MAXN];
void extend_gcd(ll a, ll b, ll &d, ll &x, ll &y){
    if(b == 0){
        x = 1;
        y = 0;
        d = a;
        return;
    }
    extend_gcd(b, a % b, d, x, y);
    ll tmp = x;
    x = y;
    y = tmp - a / b * y;
}
ll mod_reverse(ll y, ll p){
    ll x, d;
    extend_gcd(y, p, d, x, y);
    return d == 1 ? (p + x % p) % p : -1;
}
int main(){
    int t;
    ll y, p;
    cin >> t;
    while(t--){
        cin >> y >> p;
        cout << mod_reverse(y, p) << "\n";
    }
    return 0;
}

模板题

链接

  • 可以用上面的两种算法尝试一下这道题,扩展欧几里得更快一些,但是最后一个点都是TLE,由于这道题比较特殊,它要求求解前n个数的逆元,而n范围又比较大,上面两种算法适用于的情况是单一计算逆元,或者计算数量较少的逆元,如果要求很多个数的逆元,需要使用更好的方法

另一重要结论

  • 如果分母是固定的且比较小,模数与分母不互质,那么有下面的公式成立
    a b % m = a % ( b × m ) b \frac{a}{b}\%m=\frac{a\%(b\times m)}{b} ba%m=ba%(b×m)
  • 证明,令 x = a b m , y = a b % m x=\frac{a}{bm},y=\frac{a}{b}\%m x=bma,y=ba%m,有 a b = m x + y , a = b m x + b y , a % ( b m ) = b y \frac{a}{b}=mx+y,a=bmx+by,a\%(bm)=by ba=mx+y,a=bmx+by,a%(bm)=by
  • 所以 y = a % ( b × m ) b , a b % m = a % ( b × m ) b y=\frac{a\%(b\times m)}{b},\frac{a}{b}\%m=\frac{a\%(b\times m)}{b} y=ba%(b×m),ba%m=ba%(b×m)
  • 这个结论有时能用上,注意如果使用快速幂计算 a a a,那么这个时候需要对 b m bm bm取模
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Clarence Liu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值