求解逆元(C/C++)

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
(a / b) % p = (a % p / b % p) % p
 1.1. 这就是四种常用的求余的运算,可以发现前面三种其实都是正确的,而对于第四种这是不正确的,我们必须要先除得到结果再去计算,这就可能导致如果里面还有乘法,就必须要先进行连乘然后再整除,而当进行多个数的连乘的时候就可能爆掉long long 的一个数值范围所以我们想要将第四个式子进行一个转换,能不能将 a / b 转换成为 a * b(逆元) % p进行计算。将其称为逆元b称为b在模p上的乘法逆元
 1.2. 逆元存在的充要条件时 b 和 p 互质
 1.3. 快速幂求解逆元:借助费马小定理,必须保证 p 为质数
 1.4. 扩展欧几里得求逆元:可以不要求 p 为质数,从逆元充要条件 gcd(b, p) == 1 开始推理

2. 快速幂求解逆元

 2.1 快速幂介绍
 原理:
在这里插入图片描述 代码:

LL qmi(LL a, LL b, LL mod){ // a 的 b 次方 取模 mod 
    LL res = 1;
    while(b){ 
        if(b & 1) res = res * a % mod; // 不断得到b的最低位,看其是否为1,为1就可以乘上
        b >>= 1; // 删去最低位的1
        a = a * a % mod; // 得到相应位上的 a 的次方大小
    }
    return res;
}

 2.2 费马小定理推出逆元公式
 原理:
在这里插入图片描述
 2.3 代码运行展示
 a. 快速幂代码展示

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
int qmi(LL a, LL b, LL mod){
    LL res = 1;
    while(b){
        if(b & 1) res = res * a % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return res;
}

int main(){
    cout << "假设求解 4 的 3 次方, 取膜 100 的结果" << endl;
    int a = 4, b = 3, mod = 100;
    cout << qmi(a, b, mod) << endl;
}

在这里插入图片描述

 b. 快速幂求解逆元代码展示
 只需要利用快速幂改为 求解 b ^(mod - 2) 的逆元即可

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
int qmi(LL a, LL b, LL mod){
    LL res = 1;
    while(b){
        if(b & 1) res = res * a % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return res;
}

int main(){
    cout << "假设求解 4 取模 3 的逆元" << endl;
    int a = 4, b = 4, mod = 3;
    cout << qmi(b, mod - 2, mod) << endl; //
}

在这里插入图片描述

3. 扩展欧几里得求解逆元

 3.1 扩展欧几里得介绍
 原理:
在这里插入图片描述

#include<iostream>
using namespace std;
int x, y;
void exgcd(int a, int b){ // void exgcd(int a, int b, int &x, int &y)
    if(b == 0){
        x = 1, y = 0;
        return ;
    }
    exgcd(b, a % b);
    int xx = x, yy = y;
    x = yy;
    y = xx - a / b * yy;
    /* 优化写法 将上面四个语句可以换成这两个语句
    exgcd(b, a % b, y, x);
    y = y - a / b * x;
    */
}

int main(){
    int n, a, b;
    cin >> n;
    while(n -- ){
        cin >> a >> b;
        exgcd(a, b);
        // 优化写法 exgcd(a, b, x, y);
        cout << x << ' ' << y << endl;
    }
}

 3.2 扩展欧几里推逆元公式
 原理:
相当于求解exgcd(a, m, x, y) 最终的 x 就是结果

 3.3 代码运行展示

#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;

int exgcd(int a, int b, int &x, int &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(){
	int a, m;
    a = 4, m = 3;
    int x, y, d;
    d = exgcd(a, m, x, y);
    if(d == 1){ // 保证互质才会有逆元
        cout << "4 % 3 的逆元大小为" << endl;
        cout << ((LL)x % m + m) % m << endl; // 保证输出的逆元为正整数
    }
    else{
        puts("不存在逆元");
    }
}

在这里插入图片描述

  • 8
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

psudd

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

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

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

打赏作者

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

抵扣说明:

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

余额充值