扩展欧几里得算法
1、思路
若a,b是整数,且gcd(a,b)=d,那么对于任意的整数x,y,ax+by=m中的m一定是d的倍数,特别地,一定存在整数x,y,使ax+by=d成立。如果a、b是整数,那么一定存在整数x、y使得ax+by=gcd(a,b)
那么贝祖定理的一个直接应用就是:如果ax+by=1有解,那么gcd(a,b)=1(将原公式两边同时除以gcd(a,b))。
扩展欧几里得算法用来解决这样一个问题:
给定两个非零的整数a和b,求一组整数解(x,y)使得ax+by = gcd(a,b)成立,其中gcd(a,b)表示a和b的最大公约数。(其中我们通过前面的贝祖定理可知解一定存在)
回忆我们知道的欧几里得算法,它总是把gcd(a,b)转化为求解gcd(b,a%b),而当b变为0时返回a,此时的a就等于gcd。
回忆我们知道的欧几里得算法,它总是把gcd(a,b)转化为求解gcd(b,a%b),而当b变为0时返回a,此时的a就等于gcd。
也就是说,欧几里得算法结束时变量a中存放的是gcd,变量b中存放的是0,因此此时显然有a × 1 + b × 0 = gcd成立,此时有x=1、y=0成立。
我们不妨利用上面的欧几里得算法的过程来计算x和y。目前已知的是递归边界成立时为x=1、y=0,需要想办法反推出最初始的x和y。
我们不妨利用上面的欧几里得算法的过程来计算x和y。目前已知的是递归边界成立时为x=1、y=0,需要想办法反推出最初始的x和y。
(1)用于求解方程
的解,
当b=0时,
,可以得到x=1,y=0
当b不为0时,
已知
对于a%b,有,(式子中为整除)
递归到下一层,有:
可得,
(2)对于求解一般方程ax+by=c
设d=gcd(a,b),则其有解当且仅当 d|c(d能整除c,即c是d的倍数)
求解方法如下:
用扩展欧几里得求出
的解
用扩展欧几里得求出
a(x0∗c/d)+b(y0∗c/d)=c
则有
对比式子ax+by=c,我们可以得到一个特解
而通解 = 特解 + 齐次解
齐次解为 方程ax+by=0的解
则方程的通解为:
(3)求解一次同余方程ax≡b(mod m)
对于b%m,我们可以转化成
那么原式就可以化为
有解条件为 gcd(a,m)|b,然后用扩展欧几里得求解
特别的 当 b=1且 a与m互质时 则所求的x即为a的逆元
特别的 当 b=1且 a与m互质时 则所求的x即为a的逆元
2、代码模板:
#include<iostream>
using namespace std;
const int N=1e5+10;
/*
int exgcd(int a,int b,int &x,int &y)//返回gcd(a,b) 并求出解(引用带回)
{
//欧几里得算法结束时,变量a中存放的是gcd,变量b中存放的是0,因此此时显然有a × 1 + b × 0 = gcd成立。
if(!b)//当b为0时,它总是把gcd(a,b)转化为求解gcd(b,a%b),而当b变为0时返回a,此时的a就等于gcd。
{
x=1,y=0;
return a;
}
int x1,y1;
int d=exgcd(b,a%b,x1,y1);//递归求最大公约数, 求完后d就是最大公约数,
x=y1;
y=x1-a/b*y1;
return d;
}
*/
int exgcd(int a,int b,int &x,int &y)//返回gcd(a,b) 并求出解(引用带回)
{
//欧几里得算法结束时,变量a中存放的是gcd,变量b中存放的是0,因此此时显然有a × 1 + b × 0 = gcd成立。
if(!b)//当b为0时,它总是把gcd(a,b)转化为求解gcd(b,a%b),而当b变为0时返回a,此时的a就等于gcd。
{
x=1,y=0;
return a;
}
int d=exgcd(b,a%b,y,x);//递归求最大公约数, 求完后d就是最大公约数,
y-=(a/b)*x;//公式是y1=x2-(a/b)*y2,由x1=y2转化为 y1=x2-(a/b)*x,又由x2为y1,所有化简为y-=(a/b)*x
return d;
}
int main()
{
int n;
scanf("%d",&n);
while(n--)
{
int a,b,x,y;
scanf("%d %d",&a,&b);
exgcd(a,b,x,y);
printf("%d %d\n",x,y);
}
return 0;
}
