扩展欧几里得算法(exgcd)

前置知识

数论基本概念,欧几里得算法

裴蜀定理

在学习exgcd之前,我们需要先学习一下裴蜀定理

裴蜀定理有两条:

  1. 对于任意的整数 a , b a,b a,b,存在一组整数 x , y x,y x,y,使得 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)
  2. a , b a,b a,b 是整数,那么对于任意的整数 x , y x,y x,y,都有 g c d ( a , b ) ∣ a x + b y gcd(a,b)|ax+by gcd(a,b)ax+by

第一条比第二条难证,我们先来证明第一条,下面是证明过程,我尽量讲的通俗易懂:

我们考虑辗转相除的最后一步,此时 b = 0 , a = g c d ( a , b ) b=0,a=gcd(a,b) b=0,a=gcd(a,b),所以 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b) 一定有解( 解为 x = 1 , y = 0 x=1,y=0 x=1,y=0 )。

而辗转相除这一步的 a a a 为上一步的 b b b,这一步的 b b b 为上一步的 a   m o d   b a \bmod b amodb,所以我们代进去,得: b x + ( a   m o d   b ) y = g c d ( b , a   m o d   b ) bx+(a \bmod b)y=gcd(b,a\bmod b) bx+(amodb)y=gcd(b,amodb)

因为最后一步的 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b) 有解,所以 b x + ( a   m o d   b ) y = g c d ( b , a   m o d   b ) bx+(a \bmod b)y=gcd(b,a\bmod b) bx+(amodb)y=gcd(b,amodb) 必定有解。我们设它的解为 { x = x ′ y = y ′ \begin{cases}x=x'\\y=y'\end{cases} {x=xy=y,则有:
b x ′ + ( a   m o d   b ) y ′ = g c d ( b , a   m o d   b ) bx'+(a \bmod b)y'=gcd(b,a\bmod b) bx+(amodb)y=gcd(b,amodb)

而我们知道:
g c d ( b , a   m o d   b ) = g c d ( a , b ) gcd(b,a\bmod b)=gcd(a,b) gcd(b,amodb)=gcd(a,b)
a   m o d   b = a − ⌊ a b ⌋ b a \bmod b=a-\left\lfloor\dfrac{a}{b}\right\rfloor b amodb=abab

所以可得:
b x ′ + ( a − ⌊ a b ⌋ b ) y ′ = g c d ( a , b ) bx'+(a-\left\lfloor\dfrac{a}{b}\right\rfloor b)y'=gcd(a,b) bx+(abab)y=gcd(a,b)

转化一下:
a y ′ + b x ′ − y ′ ⌊ a b ⌋ b = g c d ( a , b ) ay'+bx'-y'\left\lfloor\dfrac{a}{b}\right\rfloor b=gcd(a,b) ay+bxybab=gcd(a,b)
a y ′ + b ( x ′ − y ′ ⌊ a b ⌋ ) = g c d ( a , b ) ay'+b(x'-y'\left\lfloor\dfrac{a}{b}\right\rfloor)=gcd(a,b) ay+b(xyba)=gcd(a,b)

我们发现,式子的左边成为了 a x + b y ax+by ax+by 的形式,所以我们得到,方程 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b) 的解为: { x = y ′ y = x ′ − y ′ ⌊ a b ⌋ \begin{cases}x=y'\\y=x'-y'\left\lfloor\dfrac{a}{b}\right\rfloor\end{cases} {x=yy=xyba

这样,对于任何的 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b),我们都可以通过这种方法证得有解(数学归纳法)。

下面我们来证明第二条:

我们设 d = g c d ( a , b ) d=gcd(a,b) d=gcd(a,b),则有
d ∣ a , d ∣ b d|a,d|b da,db

因为 x , y x,y x,y 均为整数,所以
d ∣ a x , d ∣ b y d|ax,d|by dax,dby

所以得
d ∣ a x + b y d|ax+by dax+by


g c d ( a , b ) ∣ a x + b y gcd(a,b)|ax+by gcd(a,b)ax+by

得证。

这样,我们就证明了裴蜀定理。我们来看一道例题:洛谷 P4549 【模板】裴蜀定理

这个题目主要运用了裴蜀定理的第二条:若 a , b a,b a,b 是整数,那么对于任意的整数 x , y x,y x,y,都有 g c d ( a , b ) ∣ a x + b y gcd(a,b)|ax+by gcd(a,b)ax+by。那么,我们得到, g c d ( a , b ) gcd(a,b) gcd(a,b) a x + b y ax+by ax+by 的最小正整数值。

推广到多个形如 A i X i A_iX_i AiXi 的单项式上, ∑ i = 1 n A i X i \sum\limits^n_{i=1}A_iX_i i=1nAiXi 的最小正整数值为 g c d { A i } gcd\{A_i\} gcd{Ai},也就是题目中的 S S S

注意输入中有负数,取个绝对值就行了。

C o d e Code Code

#include<iostream>
#include<cstdio>
using namespace std;
int n;
int a[30];
int ans;
int gcd(int x,int y)
{
	if(y==0)
		return x;
	return gcd(y,x%y);
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
	{
		if(a[i]<0)
			a[i]=-a[i];
		ans=gcd(ans,a[i]);
	}
	printf("%d",ans);
	return 0;
}

扩展欧几里得算法

扩展欧几里得算法(exgcd),正如名字一样,建立在欧几里得算法的基础上。事实上,它还依赖于上述的裴蜀定理。

exgcd是干什么用的呢?我们看到裴蜀定理的第一条:

对于任意的整数 a , b a,b a,b,存在一组整数 x , y x,y x,y,使得 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)

exgcd 就是求 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b) 的解。

仔细阅读上述裴蜀定理第一条的证明过程, a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b) 的解是什么?

{ x = y ′ y = x ′ − y ′ ⌊ a b ⌋ \begin{cases}x=y'\\y=x'-y'\left\lfloor\dfrac{a}{b}\right\rfloor\end{cases} {x=yy=xyba

那么 x ′ x' x y ′ y' y 又是怎么求出来的呢?它们是方程 b x + ( a   m o d   b ) y = g c d ( b , a   m o d   b ) bx+(a \bmod b)y=gcd(b,a\bmod b) bx+(amodb)y=gcd(b,amodb) 的解。

可以发现,这是一个递归的过程,不断的递归求出 x ′ x' x y ′ y' y,从而得到 x x x y y y

那么递归的边界又是什么呢?当 b = 0 b=0 b=0 时,解为 { x = 1 y = 0 \begin{cases}x=1\\y=0\end{cases} {x=1y=0

int x,y;
void exgcd(int a,int b)
{
	if(b==0)
	{
		x=1;
		y=0;
		return;
	}
	exgcd(b,a%b);
	int t=x;//暂存x
	x=y;
	y=t-a/b*y;//注意不能写成y*a/b,必须先计算a/b
}

来道例题:洛谷 P1082 [NOIP2012 提高组] 同余方程

我们转换一个形式:
a x   m o d   b = 1 ax \bmod b=1 axmodb=1

因为我们知道, a   m o d   b = a − ⌊ a b ⌋ × b a\bmod b=a-\left\lfloor\dfrac{a}{b}\right\rfloor\times b amodb=aba×b,所以得:
a x − ⌊ a x b ⌋ × b = 1 ax-\left\lfloor\dfrac{ax}{b}\right\rfloor\times b=1 axbax×b=1

稍微改动,它成了这个形式:
a x + b y = 1 ax+by=1 ax+by=1

因为题目告诉我们 a x   m o d   b = 1 ax \bmod b=1 axmodb=1 有解,所以 a x + b y = 1 ax+by=1 ax+by=1 有解。

裴蜀定理第二条告诉我们, g c d ( a , b ) ∣ a x + b y gcd(a,b)|ax+by gcd(a,b)ax+by,而 a x + b y = 1 ax+by=1 ax+by=1,则 g c d ( a , b ) ∣ 1 gcd(a,b)|1 gcd(a,b)∣1,那么 g c d ( a , b ) gcd(a,b) gcd(a,b) 只能等于 1 1 1

1 1 1 代换一下,方程又成了:
a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)

可以使用exgcd求解。

题目要求的是最小正整数解,而exgcd求出的可能更大,也可能是负数。那我们怎么把它转化为最小正整数解呢?

我们可以证明,所有的解之间的差均为 b b b,证明过程如下:

我们知道 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b),即 g c d ( a , b ) − a x b = y \dfrac{gcd(a,b)-ax}{b}=y bgcd(a,b)ax=y,因为 y y y 是整数,所以 b ∣ g c d ( a , b ) − a x b|gcd(a,b)-ax bgcd(a,b)ax

现在我们设 x x x 增加了 Δ x \Delta x Δx,则原式变为 g c d ( a , b ) − a x − a Δ x gcd(a,b)-ax-a\Delta x gcd(a,b)axaΔx

而因为 g c d ( a , b ) = 1 gcd(a,b)=1 gcd(a,b)=1 a , b a,b a,b 互质,则 b ∣ Δ x b|\Delta x b∣Δx,得证。

那么,我们就可以通过加减 b b b 的方式找到答案。具体做法么,直接模 b b b 就行了……

C o d e Code Code

#include<iostream>
#include<cstdio>
using namespace std;
long long a,b;
long long x,y;
void exgcd(long long a,long long b)
{
	if(b==0)
	{
		x=1;
		y=0;
		return;
	}
	exgcd(b,a%b);
	long long t=x;
	x=y;
	y=t-a/b*y;
}
int main()
{
	scanf("%lld%lld",&a,&b);
	exgcd(a,b);
	printf("%lld",(x%b+b)%b);
	return 0;
}
  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值