现在的位置: 首页 > 综合 > 正文

扩展欧几里德算法

2013年02月07日 ⁄ 综合 ⁄ 共 4003字 ⁄ 字号 评论关闭

扩展欧几里德定理
 对于不完全为
0 的非负整数 a,b,gcd(a,b)表示 a,b 的最大公约数,必然存在整
数对 x,y ,使得 gcd(a,b)=ax+by
#include<stdio.h>
int x,y,q;
void extend_Eulid(int a,int b)
{
if(b == 0)
{
x = 1;y = 0;q = a;
}
else
{
//printf("AA: %d  BB: %d\n",a,b);
extend_Eulid(b,a%b);
int temp = x;
x = y;
y = temp - a/b*y;
//printf("A: %d B: %d X: %d Y:%d\n",a,b,x,y);
}
}
int main()
{
int a,b;
while(~scanf("%d%d",&a,&b))
{
extend_Eulid(a,b);
printf("%d=(%d)*%d+(%d)*%d\n",q,x,a,y,b);
}
return 0;
}

求解 x,y的方法的理解

设 a>b。
1,显然当 b=0,gcd(a,b)=a。此时 x=1,y=0;
2,ab!=0 时
设 ax1+by1=gcd(a,b);
bx2+(a mod b)y2=gcd(b,a mod b);
根据朴素的欧几里德原理有 gcd(a,b)=gcd(b,a mod b);
则:ax1+by1=bx2+(a mod b)y2;
即:ax1+by1=bx2+(a-[a/b]*b)y2=ay2+bx2-(a/b)*by2;
根据恒等定理得:x1=y2; y1=x2-[a/b]*y2;
这样我们就得到了求解 x1,y1 的方法:x1,y1 的值基于 x2,y2.
上面的思想是以递归定义的,因为 gcd 不断的递归求解一定会有个时候 b=0,所以递归可以结束。

使用扩展欧几里德算法解决不定方程的办法

对于不定整数方程pa+qb=c,若 c mod Gcd(a, b)=0,则该方程存在整数解,否则不存在整数解。
上面已经列出找一个整数解的方法,在找到p * a+q * b = Gcd(a, b)的一组解p0,q0后, /*p * a+q * b = Gcd(a, b)的其他整数解满足:
p = p0 + b/Gcd(a, b) * t
q = q0 - a/Gcd(a, b) * t(其中t为任意整数)
至于pa+qb=c的整数解,只需将p * a+q * b = Gcd(a, b)的每个解乘上 c/Gcd(a, b) 即可
在找到p * a+q * b = Gcd(a, b)的一组解p0,q0后,应该是
得到p * a+q * b = c的一组解p1 = p0*(c/Gcd(a,b)),q1 = q0*(c/Gcd(a,b)),p * a+q * b = c的其他整数解满足:
p = p1 + b/Gcd(a, b) * t
q = q1 - a/Gcd(a, b) * t(其中t为任意整数)
p 、q就是p * a+q * b = c的所有整数解。

运用扩展欧几里德算法的题目:

简单的扩展欧几里德应用
#include<stdio.h>
#include<iostream>
int x,y,p;
void extend_Eulid(int a,int b)
{
    if(b==0)
    {
        x=1;
        y=0;
        p=a;
    }
    else
    {
        extend_Eulid(b,a%b);
        int temp;
        temp=x;
        x=y;
        y=temp-a/b*y;
    }
}
int main()
{
int a,b,i;
 while(~scanf("%d%d",&a,&b))
 {
     extend_Eulid(a,b);
     if(p!=1)
       printf("sorry\n");
     else
     {
         while(x<0)
         {
             x=x+b;
             y=y-a;
         }
         printf("%d %d\n",x,y);
     }

 }
 return 0;
}

hdu 1576 A/B

n=A%9973,则n=A-A/9973*9973。又A/B=x,则A=Bx。所以Bx-A/9973*9973=n。令y=A/9973,即Bx-9973y=n;
只要能够求出x的值,就能得出(A/B)%9973的值,即x%9973;
利用扩展欧几里德算法可求出gcd(B,9973)=Bx1+9973y1=1的x1,等式两边同乘以n,得B(nx1)-9973(-ny1)=n。可知nx1就是Bx-9973y=n的解,即x=nx1。
若x的值求出来是负数,要先转化为正数
#include<stdio.h>
#include<iostream>
void exgcd(int a,int b,__int64 &x,__int64 &y)
{
    if(b==0)
    {
        x=1;
        y=0;
    }
    else
    {
        exgcd(b,a%b,x,y);
        int temp;
        temp=x;
        x=y;
        y=temp-a/b*y;
    }
}
int main()
{
    int m;
    __int64 x,y;
    __int64 n,b;
    scanf("%d",&m);
    while(m--)
    {
        scanf("%I64d%I64d",&n,&b);
        exgcd(b,9973,x,y);
        while(x<0)
           x+=9973;
        x*=n;
        printf("%d\n",x%9973);
    }
    return 0;
}

另一种方法

思路:设A = k * 9973 + n  ,A/ B = C, C = P * 9973 + x,x即为我们所求的答案。易知,A = k* 9973 + n =B * P * 9973
+ B * x,化简后得k * 9973 = B * P * 9973 + B * x - n,因此(B * x - n)%9973 = 0,n的值知道,B的值知道,又因为x的取值范围是0到9972,因此枚举x的值即可
#include<stdio.h>
#define M 9973
int main()
{
    int i,scase;
    __int64 n,b;
    scanf("%d",&scase);
    while(scase--)
    {
        scanf("%I64d%I64d",&n,&b);
        for(i=0;i<M;i++)
        {
            if((b*i-n)%M==0)
            {
                printf("%d\n",i);
                break;
            }
        }
    }
    return 0;
}

poj 1061 青蛙的约会

思路:假设青蛙跳的次数是k,当(x+k*m)%L=(y+k*n)%L成立时,能够碰面
(x+k*m)-(y+k*n)=L*p(p是未知数),可得出不定方程k(n-m)+L*p=x-y,当(x-y)%gcd(k,L)=0时,两只青蛙可以碰面,
根据扩展欧几里德算法解决不定方程的方法即可求出k
#include<stdio.h>
__int64 q;   //最大共约数
void exgcd(__int64 a,__int64 b,__int64 &k,__int64 &p)
{
    if(b==0)
    {
        k=1;
        p=0;
        q=a;
    }
    else
    {
        exgcd(b,a%b,k,p);
        __int64 temp=k;
        k=p;
        p=temp-a/b*p;
    }
}
int main()
{
    __int64 x,y,m,n,l,k,p;
    while(~scanf("%I64d%I64d%I64d%I64d%I64d",&x,&y,&m,&n,&l))
    {
        exgcd(n-m,l,k,p);
       // printf("%d   %d\n",x1,y1);
        if((x-y)%q==0)
        {
            k*=(x-y)/q;
            k=(k%(l/q)+l/q)%(l/q);//当k为负数的时候转化为正数
            printf("%I64d\n",k);
        }
        else
        {
            printf("Impossible\n");
        }
    }
    return 0;
}

poj 2115 C Looooops

#include<stdio.h>
__int64 q;   //最大共约数
void exgcd(__int64 a,__int64 b,__int64 &x,__int64 &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        q=a;
    }
    else
    {
        exgcd(b,a%b,x,y);
        __int64 temp=x;
        x=y;
        y=temp-a/b*y;
    }
}
int main()
{
    __int64 x,y,a,b,c,k,p,m=1;
    while(~scanf("%I64d%I64d%I64d%I64d",&a,&b,&c,&k))
    {
        if(a==0&&b==0&&c==0&&k==0) break;
        p=1LL<<k;//k在移位时必须写为1LL<<k,否则因为移位默认按int操作,会出现问题
        exgcd(c,p,x,y);
        if((b-a)%q==0)
        {
            x*=(b-a)/q;
            x=(x%(p/q)+p/q)%(p/q);//当x为负数的时候转化为正数
            printf("%I64d\n",x);
        }
        else
        {
            printf("FOREVER\n");
        }
    }
    return 0;
}


poj 2142 The Balance

分别求出最小正整数x时的|x|+|y|和最小正整数y时|x|+|y|,然后输出最小|x|+|y|时的|x|和|y|;

#include<stdio.h>
#include<iostream>
#include<cmath>
using namespace std;
int q;   //最大共约数
void exgcd(int a,int b,int &x,int &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        q=a;
    }
    else
    {
        exgcd(b,a%b,x,y);
        int temp;
        temp=x;
        x=y;
        y=temp-a/b*y;
    }
}
int main()
{
    int x,y,a,b,c,x1,y1,x2,y2,max1,max2;
    while(~scanf("%d%d%d",&a,&b,&c))
    {
        if(a==0&&b==0&&c==0) break;
        exgcd(a,b,x,y);
        x*=c/q;
        y*=c/q;
		x1=(x%(b/q)+(b/q))%(b/q);
		y1=(c-a*x1)/b;
        max1=abs(x1)+abs(y1);
        y2=(y%(a/q)+(a/q))%(a/q);
		x2=(c-b*y2)/a;
		max1=abs(x2)+abs(y2);
		if(max1<max2)
           printf("%d %d\n",abs(x1),abs(y1));
		else
		   printf("%d %d\n",abs(x2),abs(y2));

    }
    return 0;
}

抱歉!评论已关闭.