扩展欧几里得算法
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; }