乘法逆元的 exgcd 求法

乘法逆元的 exgcd 求法

乘法逆元

对于一个整数 a a a,它的逆元 d d d 等于 a − 1 a^{-1} a−1。

显然有 a d = 1 ad=1 ad=1。

在 OI 中,大部分情况都是对一个整数 p p p 进行取模的情况下求逆元。

即求出满足 a d ≡ 1 (   m o d   p ) ad\equiv 1(\bmod p) ad≡1(modp) 的 d d d。

费马小定理求法与扩展欧几里得求法

费马小定理求法

这种求法仅限于 p p p 是一个质数的情况下。

费马小定理:对于质数 p p p 和任意整数 a a a,有 a p − 1 ≡ 1 (   m o d   p ) a^{p-1}\equiv 1(\bmod p) ap−1≡1(modp)。

所以令 d = a p − 2 d=a^{p-2} d=ap−2,一定有 a d = a × a p − 2 = a p − 1 ≡ 1 (   m o d   p ) ad=a\times a^{p-2}=a^{p-1}\equiv 1(\bmod p) ad=a×ap−2=ap−1≡1(modp)。

这里用快速幂用 log 的时间随便求求就好了。本文的重点不在这里。

扩展欧几里得

扩展欧几里得,简称 exgcd。用到了普通 gcd 的性质。

gcd 的性质: gcd ⁡ ( a , b ) = gcd ⁡ ( b , a   m o d   p ) \gcd(a,b)=\gcd(b,a\bmod p) gcd(a,b)=gcd(b,amodp)。

扩展欧几里得算法是求解类似于给定 a , b a,b a,b,求 a x + b y = gcd ⁡ ( a , b ) ax+by=\gcd(a,b) ax+by=gcd(a,b) 的一组可行解 ( x , y ) (x,y) (x,y)。

先来看看这个式子与乘法逆元有什么关系。
a x ≡ 1 (   m o d   p ) a x − 1 ≡ 0 (   m o d   p ) ax\equiv 1(\bmod p)\\ ax-1\equiv 0(\bmod p)\\ ax≡1(modp)ax−1≡0(modp)

所以我们设 a x − 1 = k p ax-1=kp ax−1=kp。其中 k k k 是一个整数。

那么式子又变成了:
a x − 1 = k p a x − k p = 1 ax-1=kp\\ ax-kp=1 ax−1=kpax−kp=1

我们令 b = p , y = − k b=p,y=-k b=p,y=−k,就可以转化为 a x + b y = 1 ax+by=1 ax+by=1 的情况了。

所以我们只要解出 a x − k p = 1 ax-kp=1 ax−kp=1 这个方程,就得到了 x x x。它就是我们所求的答案。

注意其实 y y y 并不重要。

然后我们讨论如何求类似于这一类的方程。

根据上文的公式 gcd ⁡ ( a , b ) = gcd ⁡ ( b , a   m o d   b ) \gcd(a,b)=\gcd(b,a\bmod b) gcd(a,b)=gcd(b,amodb),如果我们知道了在 gcd ⁡ ( b , a   m o d   b ) \gcd(b,a\bmod b) gcd(b,amodb) 情况下的解 ( x ′ , y ′ ) (x',y') (x′,y′),就显然有 b x ′ + ( a   m o d   b ) y ′ = 1 bx'+(a\bmod b)y'=1 bx′+(amodb)y′=1。

而 a   m o d   b a\bmod b amodb 又可以转化为 a − ⌊ a b ⌋ b a-\lfloor\frac{a}{b}\rfloor b a−⌊ba⌋b。

所以原式又有:
b x ′ + ( a − ⌊ a b ⌋ b ) y ′ = 1 b x ′ + a y ′ − ⌊ a b ⌋ b y ′ = 1 a y ′ + b ( x ′ + ⌊ a b ⌋ y ′ ) = 1 bx'+(a-\lfloor\frac{a}{b}\rfloor b)y'=1\\ bx'+ay'-\lfloor\frac{a}{b}\rfloor by'=1\\ ay'+b(x'+\lfloor\frac{a}{b}\rfloor y')=1 bx′+(a−⌊ba⌋b)y′=1bx′+ay′−⌊ba⌋by′=1ay′+b(x′+⌊ba⌋y′)=1

所以可以得出 x = y ′ , y = x ′ + ⌊ a b ⌋ y ′ x=y',y=x'+\lfloor\frac{a}{b}\rfloor y' x=y′,y=x′+⌊ba⌋y′。

于是就可以递归下去求答案了。

那么对于递归的尽头,即当 b = 0 b=0 b=0 时, a x + 0 y = gcd ⁡ ( a , 0 ) = a ax+0y=\gcd(a,0)=a ax+0y=gcd(a,0)=a 时,令 x = 1 x=1 x=1, y y y 取任何恰当的值即可。别整什么 2147483647 2147483647 2147483647 之类的作死行为就好。

哦对, y y y 建议是个非负数,比如 0 0 0、 1 1 1 之类的都挺不错。如果你取个 − 1 -1 −1 之类的,最后算出来可能要多次取模,十分麻烦。

例题

link

代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ljl;
ljl a,b,ans,t;//t就是来打酱油的
void exgcd(ljl a,ljl b,ljl &x,ljl &y)
{
	if(!b)
	{
		x=1;y=0;
		return;
	}
	exgcd(b,a%b,x,y);
	ljl tx=x;
	x=y;y=tx-a/b*y;
	return;
}
int main(){
	ios::sync_with_stdio(0);
	cin>>a>>b;
	exgcd(a,b,ans,t);
	cout<<(ans+b)%b<<'\n';//这里注意的ans可能是个负数,要取模
	return 0;
}
相关推荐
张张123y2 小时前
AI应用开发工程师面试题:深度技术拷打
人工智能·学习·面试
qcwl662 小时前
深入理解Linux进程与内存 学习笔记#1
笔记·学习
xuhaoyu_cpp_java2 小时前
Servlet学习
java·笔记·学习
阿Y加油吧2 小时前
力扣打卡——接雨水、无重复字符的最长子串
算法·leetcode·职场和发展
米啦啦.2 小时前
函数模板,namespace名字空间,动态内存管理,C++11新特性,
c++·动态内存管理·函数模板·c++新特性·名字空间
ADHD多动联盟2 小时前
提升学生注意力涣散问题的情绪管理与学习能力策略
学习·学习方法·玩游戏
康一夏2 小时前
学习文档 Monorepo + pnpm + 项目结构
学习
茉莉玫瑰花茶2 小时前
CMake 工程指南 - 工程场景(5)
开发语言·c++·cmake
说实话起个名字真难啊2 小时前
深入学习openclaw之记忆基础
人工智能·学习·openclaw