扩展欧几里得算法,Exgcd
前言
(exgcd)恶心欧几里得
一些问题变得简单了!
1. 引入:
有裴蜀定理:对于任意正整数 \(a,b\),总存在多组不为 \(0\) 的整数 \(x,y\),使得:
扩展欧几里得算法(Extended Gcd,exgcd)即是来求解 \(x,y\) 的算法。
2.原理
我们首先从 \(\operatorname{gcd}\) 函数的计算过程开始思考,这个递归函数的边界是什么?
我们知道,当 \(b=0\) 时,函数要返回 \(a\)。\(a\) 就是 \(a,b\) 的最大公约数。
把 \(x,y\) 带上,可以写成这个式子:
可以发现,此时满足这个式子的其中一组 \(x,y\) 可以取 \(x=1,y=0\)。
回到一般情况,对于 \(\operatorname{gcd}(a,b) \ \ (b\ne0)\),我们向 \(\operatorname{gcd}(b,a \operatorname{mod} b)\) 递归求解。
则对于:
可以写成:
由于 \(a \operatorname{mod} b=a-\left \lfloor \frac{a}{b} \right \rfloor b\),代入可得:
对比原式,我们发现可以用从 \(\operatorname{gcd}(b,a\operatorname{mod}b)\) 处传下来的 \(x,y\) 求解出 \(\operatorname{gcd}(a,b)\) 的 \(x,y\)。
我们设从 \(\operatorname{gcd}(b,a\operatorname{mod}b)\) 处传下来的 \(x,y\) 为 \(x_1,y_1\) 则可以表示 \(x,y\) :
如果我们换一下位置,即求解:
与上文同理可以推得:
则可以表示 \(x,y\) :
但是在递归时需要反着传入 \(x,y\)。
定义函数 \(\operatorname{exgcd}(a,b,x,y)\) 为求解满足 \(ax+by=\operatorname{gcd}(a,b)\) 的一组解 \(x,y\) 的函数,代码如下:
long long x,y;
ll exgcd(ll a,ll b,ll &x,ll &y){//这里引用传参以保证 x,y 可以在全局范围内被更改
if(b==0){
x=1;
y=0;
return a;//返回最简单的一种解
}
ll d=exgcd(b,a%b,y,x);//由于我们推导 x,y 时将 x,y 反放,因此在递归时也要反着递归。
x=x;//可以不写
y=y-a/b*x;
return d;
}
int gcd=exgcd(a,b,x,y);
//调用时不须将 x,y 反放
//gcd 既是 a,b 的最大公因数
3.求多组解
我们可以发现,满足这样的 \(x,y\) 有无数对,如果 \(x,y\) 的大小,正负等在题目中被规定,我们该如何调整 \(x,y\) 呢?
我们不妨使用因式分解中的添项法,把原式变成这样:
可以发现,由于分母是 \(\operatorname{gcd}(a,b)\),因此这个分数 \(\frac{ab}{\operatorname{gcd}(a,b)}\) 一定不会产生浮点数。
把这个式子整理一下:
可以发现,这个式子和 \(ax+by=\operatorname{gcd}(a,b)\) 是等价的,于是可以得出 \(x,y\) 的递推求解公式,我们设从上一组 \(x,y\) 为 \(x_1,y_1\):
如果答案只与一个系数 \(x\) 或 \(y\) 有关,则在递推时可以只改变答案值而不去改变另一个。如果答案与 \(x,y\) 均有关则需要同时改变 \(x,y\)。
以下是将 \(x\) 调整为题目中要求的最小正整数解的代码样例:
while(x<0){
x=x+b/gcd(a,b);
/*
这里的+其实是公式中k值为负数的情况
事实上,x加y减 和 x减y加 都是可以的,只是k值正负的问题
*/
}
//当x大于0时,其最小正整数解为
cout<<x%(b/gcd(a,b));

浙公网安备 33010602011771号