普通的欧几里得算法

  这大概是最为人熟知的数论算法了吧,作用是求出gcd(a,b),代码如下: 递归版本:

循环版本:

  另外,求最大公约数还可以用库函数__gcd(),这是GNU里的库函数,不是标准库函数,头文件为 < algorithm >

拓展欧几里得算法

  而拓展欧几里得算法则是在算出a,b的最大公约数的同时,顺带计算贝祖公式的一组解,即ax+by=gcd(a,b)的解( 贝祖等式 )

  那么,如果要手算ax+by=gcd(a,b),我们要怎么算呢?

  首先,因为gcd(a,b)=gcd(b,a%b),

  所以a x + b y = gcd ( a,b ) = gcd ( b,a%b )= c x' + d y' ( 其中,c = b,d = a % b )

  又因为a % b = a - (a/b) * b

  所以有a y' + b ( x' - (a/b) * y) = a x + b y

  比较两式,可得x = y' , y = x' - ( a / b ) * y’

  又因为当b = 0时,解为x = 1 , y = 0,所以只要递归求解至b = 0即可

代码如下:

另一种写法为:

  拓展欧几里得的一个作用,是求出满足形如ax+by=c的方程的解。根据贝祖等式可以看出,若c % gcd( a,b ) != 0,则方程无解。代码如下:

ll solve(ll a,ll b,ll b)
{
    ll x,y;
    ll g=exgcd(a,b,x,y);
    if(c%g)
        return -1;
    x*=c/gcd;
    b/=gcd;
    if(b<0)
        b=-b;        //如果b < 0,就取其绝对值
    ll ans=x%b;        //如果求出的解小于0,就将其加上b
    if(ans<=0)
        ans+=b;
    return ans; 
}

  这个算法的另一个作用是求最小逆元。根据a x = 1 ( mod p) ==> ax = py + 1 ==> ax + by = 1,不难看出,求最小逆元其实就是求ax + by = c的解的特殊情况。因此,利用拓展欧几里得求最小逆元的代码如下:

一道求ax + by = c 的模板题: 青蛙约会

我的代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
typedef long long ll;
void exgcd(ll a,ll b,ll& d,ll& x,ll& y)
{
    if(!b){
        d=a;x=1;y=0;
    }
    else{
        exgcd(b,a%b,d,y,x);
        y-=(a/b)*x;
    }
}
ll solve(ll a,ll b,ll c)
{
    ll x;ll y;ll g;
    exgcd(a,b,g,x,y);
    if(c%g)        return -1;
    x=(x*c)/g;
    b/=g;
    if(b<0)        b=-b;
    ll ans=x%b;
    if(ans<=0)        ans+=b;
    return ans;
}
int main()
{
    ll x,y,m,n,L;
    scanf("%lld %lld %lld %lld %lld",&x,&y,&m,&n,&L);
    ll ans=solve(n-m,L,x-y);
    if(ans==-1)
        printf("Impossible\n");
    else
        printf("%lld\n",ans);
    return 0;
}

为什么求ax + by = c的代码可以这样写呢?我也不知道啊OTZ(发出了数学太差的声音)