扩展欧几里得

扩展欧几里德算法是用来在已知a, b求解一组x,y,使它们满足贝祖等式: ax+by = gcd(a, b) =d(解一定存在,根据数论中的相关定理)。

扩展欧几里德常用在求解模线性方程及方程组中。

首先,证明一下gcd(a,b)==gcd(b,a%b)

设gcd(a,b) = k
a = n1 * k
b = n2 * k
a%b = (n1%n2)*k
b = n2 * k
现在只需证n2 和 n1%n2 没有公因子
假设有公因子,为r
n2 = num2 * r
n1%n2 = num1 * r
n1 = k*n2 + num1*r
n1 = (k*num2+num1)*r
n1和n2有公因子,这与设gcd(a,b) = k矛盾,因为如果n1和n2有公因子,那么gcd(a,b)=k*r
所以gcd(b,a%b) = gcd (a,b)

以上证明出处

由上面的等式,我们可得:

∵gcd(a,b)==gcd(b,a%b)

∴a*x1+b*y1==b*x2+(a%b)*y2            //一定有整数解x,y

∵在计算机科学里a%b的实际运算是 a-(a/b)*b        //a/b因为都是整形变量,所以计算时a/b==⌊a/b⌋

∴a*x1+b*y1==b*x2+(a-(a/b)*b)*y2

整理可得a*x1+b*y1==a*y2+b*(x2-(a/b)*y2)

x1=y2,y1=x2-(a/b)*y2

显然,我们只要找到递归边界,就可以不断求解,直到得出答案

辗转相除法:

int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
也就是说边界是 a*x+b*y==gcd(a,b)==a  //因为此时b==0
那么现在x是不是就是1,y可以取任意整数
 
以下为最简c++语言写法:

void gcd(int a,int b,int &x,int &y)
{
  if (b==0){x=1,y=0;return;}
  gcd(b,a%b,y,x);          //注意x,y的位置
  y-=a/b*x;
}

 
这里的引用解决了x1=y2,所以仅剩y1=x2-(a/b)*y2;
又是因为引用,y1=x2,x1=y2;
所以y1=y1-(a/b)*x1;
不得不说,能首先写出这个的大佬,的确思维逻辑很好!!!
 
 
当然,不用引用的话,返回一个结构体也行(貌似会更好理解)

struct ans
{
  int x,y;
};
ans gcd(int a,int b)
{
  ans myans,t;
  if(b==0){myans.x=1,myans.y=0;return myans;}
  t=gcd(b,a%b);
  myans.x=t.y;
  myans.y=t.x-a/b*t.y;
  return myans;
}

可以试一下改变递归边界中的y的值,会有惊喜;

我们得出了一组解  x,y

显然a*x0+b*y0==a*x+b*y  //x0,y0为已得解,x,y为其他答案

x=x0+k*b,y=y0-k*a    //k为整数,其实你将x,y带入原式,就会发现显然成立

 
原文地址:https://www.cnblogs.com/callmebg/p/6849672.html