ACM数论----欧几里得算法与拓展欧几里得

一.首先介绍一下什么叫欧几里得算法

欧几里得算法又称为辗转相除法,用于求两个自然数数的最大公约数,若有负数,全变为正数再运算,这里直接给出代码:

(1)非递归版:

int gcd(int a,int b)
{
    //return b==0?a:gcd(b,a%b);
    if(a

(2)递归版:

int gcd(int a,int b)
{
    return b==0?a:gcd(b,a%b);
}

注:递归版虽然简洁,但是递归容易爆栈,非递归比递归还是要可靠一点虽然很长。

二.拓展欧几里得算法

1.拓展欧几里得是来干啥的:我们用来求不定方程:ax+by = c 的通解或者特解,那为什么叫拓展欧几里得呢?他跟欧几里得算法有什么关系呢?-->当然有关系了,ax+by = c是不一定有解的,但是由于拓展欧几里得定律:对于不完全为0的非负整数a,b,gcd(a, b)表示a, b的最大公约数,必定存在整数对x,y,满足a*x+b*y==gcd(a, b)。 这就是说至少c%gcd(a,b)==0 才能存在x,y解,这也就牵扯到了最大公约数的欧几里得。

2.推一下拓展欧几里得的通解(ax+by = c       a,b,c已知)

(1)首先我们从求 ax + by = gcd(a,b)的特解 开始:

      已知方程:ax1 + by1 = gcd(a,b)     +     bx2+(a%b)y2 = gcd(b,a%b);

      而我们知道: gcd(a,b) == gcd(b,a%b);

      由此得出: ax1 + by1 = bx2 + (a%b)y2;

      而:  a%b = a - (a/b )* b; 

      所以:  ax1 + by1 = bx2 + [ a - (a/b) * b] y2;

      合并得:ax1 + by1 = ay2 + b[ x2 - (a/b) y2];

      由恒等关系得: x1 = y2                y1 = x2 - a/b*y2

由此:我们得出了求x1,y1的递归方程,要求ax + by = gcd(a,b)的解x1,y1时,我们需要知道bx + (a%b)y = gcd(b,a%b)的解x2,y2.......依次推到底部,我们发现:

      当b==0时 : ax = gac(a,0) 即 ax = a,所以 底部的解为: x = 1,y = 0.然后由此结果回推x1,y1递归求解!

(2)假设上式中我们已经求出了 ax + by = gcd(a,b) 的特解 x1,y1,接下来我们来求 ax + by = c的特解:

   <1.>若c%gcd(a,b)! = 0:说明c不是gcd(a,b)的倍数,那么a,b再怎么凑也凑不到c,所以此时无解!

  <2.> 若c%gcd(a,b)==0:此时一定有解,而且有多个,所以可求通解!

     ax + by = c相当于 ax + by = gcd(a,b)两侧同时乘了一个 c/gcd(a,b)

    所以此条件下的特解x0 = x1 * c/gcd(a,b),  y0 = y1 * c/gcd(a,b);

(3)最后我们来求ax + by = c的通解(当特解存在时)

      我们知道当两项相加时,要想保持和不变,一项变大,另一项就得变小!而这里x总是变化a的倍数,y总是变化b的倍数,要想让他俩变化幅度相同,二者都要变化a,b的最小公倍数倍!

     所以:ax = ax + lcm(a,b)      by = by - lcm(a,b)

     而: lcm(a,b) = a*b/gcd(a,b)

     由此: ax = ax + a*b/gcd(a,b)

     约掉a得 : x = x + b/gcd(a,b) 即 通解 x = x0 + b/gcd(a,b) *k     (k = 0 ,+-1,+-2......)

    同理可得 : y = y  - a/gcd(a,b) 即 通解y = y0 - a/gcd(a,b) * k      (k = 0 ,+-1,+-2......)

3.做题过程

(1)找出题中关系方程:ax + by = c, 递归求解 ax + by = gcd(a,b) 特解x1,y1同时判断 c%gcd(a,b) ==0?继续:无解

(2)求ax + by = c特解 : x0 = x1*c/gcd(a,b),  y0 = y1*c/gcd(a,b) ;

(3)求ax + by = c 通解 : x = x0 + b/gcd(a,b)*k   y = y0 - b/gcd(a,b)*k ;(有可能找最小正整数解)

注:(1)关键-->找出题目中ax + by = c的关系方程!!!

(2)若题目a,b存在负数,则-ax-by = c ----> a*(-x) + b*(-y) = c化负为正再求解即可

4.代码:

#include 
#include
#include
using namespace std;
int ex_gcd(int aa,int bb,int &xx,int &yy)//递归
{
    if(bb==0){xx = 1; yy = 0;return aa;}
    int ans = ex_gcd(bb,aa%bb,xx,yy);
    int temp = xx;
    xx = yy;
    yy = temp - aa/bb*yy;
    return ans;
}
int main()
{
    int a,b,c;
    scanf("%d%d%d",&a,&b,&c);
    int x,y;//求第一个特解
    int res = ex_gcd(a,b,x,y);
    if(c%res)printf("Impossible\n");
    else{
        int x0 = x * c/res;//求第二个特解
        int y0 = y * c/res;
        int L = b/res;
        if(L<0)L = -L;
        int X = (x0%L + L)%L;//求x通解里的最小正整数
        int Y = (c - a*X)/b;
        printf("x最小正整数解时 :  x = %d,y = %d\n",X,Y);
    }
    return 0;
}

5.例题:

(1)Poj 1601 青蛙的约会

分析: 两只青蛙要想碰面,必须在某一时刻落在同一点: x + t*m = y + t*n + k*L

            合并得 : (x -y) = t*(n - m) + k*L   ---->a = n-m, x = t, b = L,y = k,c = x-y;求解该拓展欧几里得的最小正整数解即可

代码:

#include 
#include 
#include
using namespace std;
typedef long long int LL;
LL ex_gcd(LL a,LL b,LL &xx,LL &yy)
{
    if(b==0){xx = 1;yy = 0;return a;}
    LL d = ex_gcd(b,a%b,xx,yy);
    LL temp = xx;
    xx = yy;
    yy = temp - a/b*yy;
    return d;
}
int main()
{
    LL x,y,m,n,L;
    scanf("%lld%lld%lld%lld%lld",&x,&y,&m,&n,&L);
    LL X,Y;
    LL k = ex_gcd(n-m,L,X,Y);
    if((x-y)%k)printf("Impossible\n");
    else{
        LL fx = (x-y)/k*X;
        L/=k;
        if(L<0)L = -L;
        while(fx<0)fx+=L;
        printf("%lld\n",fx%L);
    }
    return 0;
}

(2)HDU 2669 (拓展欧几里得裸题)

#include 
#include
#include
#include
using namespace std;
typedef long long int LL;
LL ex_gcd(LL aa,LL bb,LL &xx,LL &yy)
{
    if(bb==0){xx = 1;yy = 0;return aa;}
    LL res = ex_gcd(bb,aa%bb,xx,yy);
    LL temp = xx;
    xx = yy;
    yy = temp - aa/bb*yy;
    return res;
}
int main()
{
    LL a,b;
    while(cin>>a>>b){
        LL x,y;
        LL ans = ex_gcd(a,b,x,y);
        if(ans!=1)printf("sorry\n");
        else{
            LL L = b;
            LL X = (x%b + b)%b;
            LL Y = (1 - a*X)/b;
            cout<

 

你可能感兴趣的:(====数学物理====,数论+几何)