扩展欧几里得算法是欧几里得算法(又叫辗转相除法)的扩展。除了计算a、b两个整数的最大公约数,此算法还能找到整数x、y(其中一个很可能是负数)。通常谈到最大公因子时, 我们都会提到一个非常基本的事实: 给予二整数 a 与 b, 必存在有整数 x 与 y 使得ax + by = gcd(a,b)。有两个数a,b,对它们进行辗转相除法,可得它们的最大公约数——这是众所周知的。然后,收集辗转相除法中产生的式子,倒回去,可以得到ax+by=gcd(a,b)的整数解。(百度)
先复习求gcd的内容
原理:两个整数的最大公约数等于较小的那个数和两数相除余数的最大公约数。
即gcd(a,b) = gcd(b,a%b)
再看扩展gcd
先了解裴蜀(贝祖)定理
裴蜀(贝祖)定理:若a,b是整数,且gcd(a,b)=d,那么对于任意的整数x、y,ax+by都一定是d的倍数,特别地,一定存在整数x,y,使ax+by=d成立。
同时可知对于一般方程ax+by=c有解的条件为c%gcd(a,b)=0。
拓展gcd即是要求ax+by=gcd(a,b)中x、y的整数解
设有ax+by=gcd(a,b),显然,当b = 0时,gcd(a,b) = a,x = 1,y = 任何数。为了方便这里使 y = 0。
当 b != 0时
假设
a * x1+b * y1 = gcd(a,b)
b * x2+(a % b) * y2 = gcd(b,a % b) 且gcd(a,b) = gcd(b,a%b)
根据取余的本质a % b = a - a / b * b进行推导
得到公式:
以47和30为例:
。
可以从下往上从x = 1,y = 0开始用刚刚得到的公式依次向上推导x 和 y。
推导过程转换为代码:
typedef long long ll;
void ex_gcd(ll a,ll b,ll &x1,ll &y1)
{
if(b==0){
x1=1;
y1=0;
}else{
ll x2,y2;
ex_gcd(b,a%b,x2,y2);
x1=y2;
y1=(x2-(a/b)*y2);
}
}
于是就得到了求ax+by=gcd(a,b)一组解的方法
稍改代码还可以求出gcd(a,b)
typedef long long ll;
ll ex_gcd(ll a,ll b,ll &x1,ll &y1)
{
if(b==0){
x1=1;
y1=0;
return a;
}else{
ll g;
ll x2,y2;
ll gcd = ex_gcd(b,a%b,x2,y2);
x1=y2;
y1=(x2-(a/b)*y2);
return gcd;
}
}
得到一组解后再求通解
已知a * x0 + b * y0 = gcd(a,b);
设 a1 = a / gcd(a,b)
b1 = b / gcd(a,b)
则
a1 * x0 + b1 * y0 = 1;
把式子加n * a1 * b1再减去n * a1 * b1变换得
a1 * (x0 + n * b1) + b1 * (y0 - n * a1) = 1;(n为整数)
得到通解为 x = x0 + n * b1
求最小正整数解:
if(x <= 0)
x = x % b1 + b1;
else
x = x % b1;
再求一般方程ax+by=c的整数解
首先要判断是否有解,有解的条件为c % gcd(a,b)=0
因为 a * x0+b * y0=gcd(a,b)
所以 x = x0 * (c / gcd(a,b)) y = y0 * (c / gcd(a,b))