GCD欧几里得算法 & EXGCD扩展欧几里得算法

欧几里得算法
欧几里得算法 (Euclidean algorithm) 是用来解决最大公约数问题的,通常采用辗转相除法。


GCD欧几里得算法 & EXGCD扩展欧几里得算法_第1张图片
GCD

代码:

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

···············································································································
拓展欧几里得算法
拓展欧几里得算法(Extended Euclidean Algorithm)是基于欧几里得算法而来解一类特殊的线性丢番图方程。
解决问题:ax+by=1(即a,b互质),求解x,y。


GCD欧几里得算法 & EXGCD扩展欧几里得算法_第2张图片
扩展GCD

代码:

ll exgcd(ll a, ll b, ll &x, ll &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    ll r=exgcd(b, a%b, x, y);
    ll t=x;
    x=y;
    y=t-(a/b)*y;
    return r;
}

对于ax+by=gcd(a,b)我们可以得到一组解x和y
但往往我们会遇到ax+by=c这样的问题,那和前面的扩展gcd又有什么关系呢?
前提条件:若c mod gcd(a, b) =0,则该方程(ax+by=c)存在整数解,否则不存在整数解

对于ax+by=gcd(a,b)求出来的一组整数解x0和y0
对应ax+by=c的一组整数解x1和y1,有x1=x0(d/gcd(a,b)),y1=y0(d/gcd(a,b))
对于全部解,有x=x0+b/gcd(a,b)t,y=y0-a/gcd(a,b)t (其中t为任意常整数)
由于x0,y0,a,b,gcd(a,b)都是已经求出的数(可以看作常数),所以对于x,y我们可以看作是自变量为t,因变量为x,y的两个一次函数,所以可以知道,这2个函数是单调的

x = x0 + b/gcd(a,b)*t;
y = y0 - a/gcd(a,b)*t; 
(x0, y0 为方程的一组特解, t为整数)

证明是根据线性丢番图方程而来的。

GCD欧几里得算法 & EXGCD扩展欧几里得算法_第3张图片
扩展欧几里得算法

例题:青蛙的约会
POJ - 1061

#include
#include
using namespace std;
#define ll long long int

ll gcd(ll a, ll b)
{
    return b? gcd(b, a%b): a;
}

void exgcd(ll a, ll b, ll &x, ll &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return ;
    }
    exgcd(b, a%b, x, y);
    ll t=x;
    x=y;
    y=t-(a/b)*y;
    return ;
}


int main()
{
    ll x, y, m, n, L;
    ll T, P, d;
    ll a, b, c;
    while(~scanf("%lld%lld%lld%lld%lld", &x, &y, &m, &n, &L))
    {
        a=n-m;
        b=L;
        c=x-y;
        d=gcd(a, b);
        if(c%d) printf("Impossible\n");
        else
        {
            int r=d;
            a/=d;
            b/=d;
            c/=d;
            exgcd(a, b, T, P);
            T=T*c;
            T=(T%b+b)%b;
            printf("%lld\n", T);
        }
    }
    return 0;
}

例题:The Balance
POJ - 2142

#include
#include
using namespace std;
#define ll long long int
ll gcd(ll a, ll b)
{
    return b? gcd(b, a%b): a;
}

void exgcd(ll a, ll b, ll &x, ll &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return ;
    }
    exgcd(b, a%b, x, y);
    ll t=x;
    x=y;
    y=t-(a/b)*y;
    return ;
}

int main()
{
    ll a, b, c, x, y, vx, vy;
    while(~scanf("%lld%lld%lld", &a, &b, &c)&&(a||b||c))
    {
        ll r=gcd(a, b);
        a/=r;
        b/=r;
        c/=r;
        exgcd(a, b, x, y);

        vx=x*c;
        vx=(vx%b+b)%b;
        vy=abs((c-a*vx)/b);

        y=y*c;
        y=(y%a+a)%a;
        x=abs((c-b*y)/a);

        if(x+y>vx+vy)
        {
            x=vx;
            y=vy;
        }

        printf("%lld %lld\n", x, y);
    }
    return 0;
}

你可能感兴趣的:(GCD欧几里得算法 & EXGCD扩展欧几里得算法)