乘法逆元
写在前面
在
要解决这个问题,就需要用到一个概念:乘法逆元。
含义
什么是乘法逆元呢?
如果您到百度上搜索逆元,您会看到如下定义:(这里逆元素是逆元的全称)
逆元素是指一个可以取消另一给定元素运算的元素
---百度百科
这个定义似乎不太那么直观……
我们来举个例子吧,先再实数范围举例,由小学知识可知,如果一个代数式
不难发现这符合逆元的定义,故我们可以说一个数和其倒数互为乘法逆元。除此之外,我们还能发现一个数和其相反数互为加法逆元等等。
接下来回到代数式的例子,考虑为什么
上文在实数范围内讨论了乘法逆元,现在我们来看本文主要讨论的内容,数论模意义下的乘法逆元。
由上文的分析来看,我们可以借助“任何数与
若
我们通常将
如同
不难发现,
计算逆元的方法
1. 费马小定理
费马小定理:若
为质数,且 与 互质,那么
对于定理中的式子,我们可以将两边同除
而
故我们只需计算
时间复杂度为
2. 扩展欧几里德定理
定理内容:
a 和 b 的最大公约数是 gcd ,那么,我们一定能够找到这样的 x 和 y ,使得:
不难发现
将同余号换为等号得:
移项得:
由上式可知
继续移项得:
令
我们发现这个方程有解的条件是
接下来的问题就是如何求出
根据辗转相除法可得:
两式整合一下:
提取公因数
最终我们得到:
这样我们就能在递归求
code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll a,p,x,y;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(b==0)
{
x=1; y=0; return a;
}
int gcd=exgcd(b,a%b,x,y);
int tmp=y;
y=x-a/b*y;
x=tmp;
return gcd;
}
int main()
{
scanf("%lld%lld",&a,&p);
exgcd(a,p,x,y);
x=(x%p+p)%p;//这里防止出现负数
printf("%d",x);
}
3. 线性求逆元
注意,这种求逆元的方法仅使用在
我们用
我们设
再将这个式子放到
两边同时乘上
即
于是就可以从前面推出当前的逆元了,为了防止得到负数,在计算时
code:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 3e6 + 10;
long long inv[maxn], n, p;
int main()
{
scanf("%lld%lld", &n, &p);
inv[0] = 0, inv[1] = 1;//初始化1的逆元为1
printf("%lld\n", inv[1]);
for (int i = 2; i <= n; ++i)
{
inv[i] = (p - p / i) * inv[p % i] % p;//上文中的式子
printf("%lld\n", inv[i]);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】