欧几里德及其扩展

欧几里德是用来求最大公约数的,可以把它看成是状态转移,

对任意两个数a,b(a>b),d=gcd(a,b),如果b不为零,那么gcd(a,b)=gcd(b,a%b)

        证明: 令 r=a%b,即存在k,使得 a=b*k+r,那么r=a-b*k;显然r>=0,  r%d=((a%d)-(b*k)%d)%d,因为a%d=b%d=0,所以r%d=0;

因此求gcd(a,b)可以转移到求gcd(b,a%b),那么这就是个递归过程了,那什么时候递归结束呢,想一下,a,b不能为零,则可以把当b为零,作为递归的结束(当然还可以以其它结束条件),这就是求最大公约数的方法可以以其它结束条件),这就是求最大公约数的方法


  1. int gcd(int a,int b)  
  2. {  
  3.     if(b==0)return a;  
  4.    
  5.     else return gcd(b,a%b);  
  6.  }  

是不是很简单呀.

理解了上面,那么扩展欧几里得就能很容易理解了,对任意a,b(a>b),我们列出这样一个式子: a*x+b*y=gcd(a,b);

不要觉得扩展欧几里得很牛逼,它就是一个算x,y的一个方法,只是在上面gcd中多了处理x,y的步骤

我们这样来想:

已知当前的一个状态:a1  b1  x1  y1,    a1*x1+b1*y1=gcd(a1,b1),注意这里的a1,b1是求gcd(a,b)中的一个状态,

假设 (a1,b1)是由(a0,b0)转移过去的

那么:   a1=b0 ;     b1=a0%b0=a0-k*b0 (k=int(a0/b0));gcd(a0,b0)=gcd(a1,b1);

代入a1*x1+b1*y1=gcd(a1,b1),变化成:b0*x1+(a0-k*b0)*y1=gcd(a1,b1)=gcd(a0,b0);

a0*y1+b0*(x1-k*y1)=gcd(a0,b0);

这样可以得到: x0=y1;  y0=x1-k*y1;(理解这个过程了么,由当前状态可以算出上一状态的x,y,即当前状态可以由它的下一个状态的x,y得到)

code:

  1. int exGcd(int a,int b,int &x,int &y)  
  2. {  
  3.     if(b==0)  
  4.     {  
  5.         x=1;  
  6.         y=0;  
  7.         return a;// 此时a是最开始(a,b)的最大公约数,那么  gcd(a,b)*1+ 0*0=gcd(a,b),肯定对的,在这里,我认为,y可以为任何值都对  
  8.     }  
  9.       
  10.     int d=exGcd(b,a%b,x,y);  
  11.       
  12.     int t=x;  
  13.       
  14.     x=y;// 更新当前状态的x  
  15.       
  16.     y=t-a/b*y;//更新当前状态的y  
  17.       
  18.     return d;//返回最大公约数  
  19. }  

来看扩展欧几里得的一个应用,求逆元

逆元的定义: 对任意两个数a,p,如果gcd(a,p)=1,那么存在x,y,使得  a*x+p*y=gcd(a,p)=1,那么这个x就称为a的逆元

来看逆元的用途:

大家都知道(a/b)%c=a%(c*b)/b(不知道的到网上搜搜)

但是如果c很大,在你的计算中如果出现两个这样mod后的数相乘,在取余,那么就可能超过long long 的范围,这个时候如果用逆元,可以把除法变成乘法。

先告诉结论: (a/b)%c=(a*x)%c  (x是b关于c的逆元,那么这里的前提就是b,c互素,这样才满足gcd(b,c)=1   );

  证明:另  a=b*k,(a/b)%c=k%c

  x=(1-c*y)/b (由逆元的定义那里可以得到)

 a*x=k*b*(1-c*y)/b=k*(1-c*y)=k-c*k*y,这个式子对c取余,很明显就等于k%c,故式子是等价的

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

欧几里得算法扩展(extended gcd)--解不定方程

扩展欧几里德算法是用来在已知a, b求解一组p,q使得p * a+q * b = Gcd(p, q) (解一定存在,根据数论中的相关定理)。

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

下面是一个使用C++的实现:(拓展欧几里得算法的时间复杂度跟gcd()欧几里得算法复杂度是一样的)

int extended_gcd(int a, int b, int &x, int &y)
{
    int ret, tmp;
    if (!b)
    {
        x = 1;
        y = 0;
        return a;
    }
    ret = extended_gcd(b, a % b, x, y);
    tmp = x;
    x = y;
    y = tmp - a / b * y;
    return ret;
}

以下这一小段蓝色的只是证明过程,看不太懂的可以过……记住公式,懂得如何用就行。
把这个实现和Gcd的递归实现相比,发现多了下面的x,y赋值过程,这就是扩展欧几里德算法的精髓。
可以这样思考:
对于a' = b, b' = a % b 而言,我们求得 x, y使得 a'x + b'y = Gcd(a', b')
由于b' = a % b = a - a / b * b (注:这里的/是程序设计语言中的除法)
那么可以得到:
a'x + b'y = Gcd(a', b') ===>
bx + (a - a / b * b)y = Gcd(a', b') = Gcd(a, b) ===>
ay +b(x - a / b*y) = Gcd(a, b)
因此对于a和b而言,他们的相对应的p,q分别是 y和(x-a/b*y)

重点:关于使用扩展欧几里德算法解决不定方程的办法
对于不定整数方程ax+by=c;

判断是否有解:

若 c mod Gcd(a, b)=0,则该方程存在整数解,否则不存在整数解。
解不定方程ax+by=c;
解这个不定方程可以用扩展欧几里德算法。
对于不定方程ax+by=c的通解为:
x=x0*c/d+b/d*t
y=y0*c/d+a/d*t
当c%gcd(a,b)!=0时,不定方程无解.
剩下需要做的就是用x=x0*c/d+b/d*t
求出一个最小的正整数x,

 

得到的x0,y0有可能是负值,(如果你要求x0,y0的最小正值)这个时候,就需要用到上面的两条式子来求,用while来判断所得的x是否大于0就可以了。

上个用来求二元不定方程的extended gcd模板:

#include <cstdio>
using namespace std;
int extended_gcd(int a, int b, int &x, int &y)
{
    int ret, tmp;
    if (!b)
    {
        x = 1;
        y = 0;
        return a;
    }
    ret = extended_gcd(b, a % b, x, y);
    tmp = x;
    x = y;
    y = tmp - a / b * y;
    return ret;
}
int main()
{
    int a,b,x,y,cas;
    scanf("%d",&cas);
    while(cas--)
    {
        scanf("%d%d",&a,&b);
        int gc=extended_gcd(a,b,x,y);//这里的x,y在函数中是最为被引用的参数,所以函数结束,
        //x,y的值就改变了,就是x0,y0
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值