欧几里得算法(GCD)和扩展欧几里得算法(EXGCD)

一、欧几里得算法(GCD)

欧几里德算法又称辗转相除法,是指用于计算两个正整数a,b的最大公约数。


1、求最大公约数

给定平面上的两个格点P1(x1,y1),P2(x2,y2),在线段P1P2上,除P1、P2外,一共有多少个格点?(格点定义为横纵坐标都是整数的点


答案为|x1-x2|和|y1-y2|的最大公约数-1


GCD——辗转相除法介绍
设gcd(a,b)是计算自然数a,b最大公约数的函数,a除以b得到的商和余数分别为p和q。
因为a=b*p+q,
所以gcd(b,q)可以整除a和b也就可以整除gcd(a,b);
此时可看为a=p*gcd(b,q)*c+gcd(b,q)*d
因为q=a-b*p,
同理可知gcd(a,b)整除gcd(b,q);
因此gcd(a,b)=gcd(b,q)=gcd(b,a%b).
因此不断递归gcd(a,b)=gcd(b,a%b)可以得到gcd(a,b)=gcd(c,0)
此时c即为最大公约数.


code:

#include 
#include 
using namespace std;
long long gcd(long long a,long long b)
{
    return b==0?a:gcd(b,a%b);
}
int main()
{
    long long n1,n2;
    while(scanf("%lld%lld",&n1,&n2)!=EOF)
    {
        n1=abs(n1);
        n2=abs(n2);
        printf("%lld\n",gcd(n1,n2));
    }
    return 0;
}

二、扩展欧几里得算法(EXGCD)

现在我们知道了a和b的最大公约数,那么一定存在x,y使x*a+y*b=gcd(a,b)。我们假设已经有了一个特解x0和y0。那么我们用x0,y0来表示他们的通解:
x=x0+(b/gcd)*t,
y=y0-(a/gcd)*t.
这里或许会有疑问为什么不是
x=x0+b*t,
y=y0+a*t,
由于t取值范围是整数,那么当然取除了1以外的最小公约数做参数来获得更多的取值,所以此处b/gcd和a/gcd。这里推荐一本书《基础数论》(哈尔滨工业大学出版社)


那么我们该如何求出我们想要的特解呢?
这里我们可以从欧几里得算法中得到启示,可以注意到欧几里得算法的停止状态是
a=gcd,b=0。那么此时只需要a的系数x=1.即可得到等式x*a+y*b=gcd(a,b).(此处y的取值无所谓,因为此时b为0,0乘任何数还是0)
那么我们反推到最初状态
先写出上一状态:x1,x2使b和a%b满足等式b*x1 + (a%b)*y1 = gcd
我们把a提出来,
我们知道: a%b = a - (a/b)*b(这里的 “/” 指的是整除,例如 5/2=2)
所以可以得到 gcd = b*x1 + (a-(a/b)*b)*y1
= b*x1 + a*y1 – (a/b)*b*y1
= a*y1 + b*(x1 – a/b*y1)
此时再和我们的最终状态a*x + b*y = gcd比较就可以得到x,y与x1,y1的关系。
x=y1,
y=x1–a/b*y1。
我们用递推的方法来表示以上内容:

int exgcd(int a,int b,int x,int y)
{
    if(b==0)
    {
        x=1;
        y=0;//虽然说此时y的取值任意但还是返回0,现在还没有想出为什么
        return a;
    }
    int ans=exgcd(b,a%b,x,y);
    int temp=x;
    x=y;
    y=temp-a/b*y;
    return ans;
}
//此时ans返回的是a和b的最大公约数

而正常情况下我们并不需要这些通解,而是需要一些特解比如一个数对另一个数的乘法逆元(a*x=1mod(m))我们称 x 是 a 关于 m 的乘法逆元.可以等价于这样的表达式: a*x + m*y = 1.
由表达式可以看出当gcd(a , m) != 1 的时候是没有解的这也是 a*x + b*y = c 有解的充要条件: c % gcd(a,b) == 0
一般情况我们需要求的是最小解,其实我们用 x0 % m其实就得到了最小的解了。
为什么呢?
当c=1时
这时候通解的表达式为x=x0+m*t,所以有且只有一个最小的解在[0 , m]之间
但是,由于问题的特殊性,有时候我们得到的特解 x0 是一个负数,还有的时候我们的 m 也是一个负数这怎么办?
当 m 是负数的时候,我们取 m 的绝对值就行了,当 x0 是负数的时候,他模上 m 的结果仍然是负数(在计算机计算的结果上是这样的,虽然定义的时候不是这样的),这时候,我们仍然让 x0 对abs(m) 取模,然后结果再加上abs(m) 就行了,于是,我们不难写出下面的代码求解一个数 a 对于另一个数 m 的乘法逆元
(例:若ans=x0%abs(m),若x0=-9,m=3则ans=0,正好加了3个m使ans为最小非负解;若x0=-10,m=3,则ans=-1,再加m则ans=2,为最小非负解)

int cal(int a,int b)
{
    int x,y;
    int gcd=exgcd(a,m,,x,y);
    if(gcd!=1)
        return -1;
    m=abs(m);
    int ans=x%m;
    if(ans<=0)
        ans+=m;
    return ans;
}

ans返回的便是x0,再根据等式ax+my=1,即可求出y=(1-ans*a)/m

PROBLEM:

一、

HDU 2669 http://acm.hdu.edu.cn/showproblem.php?pid=2669 裸的扩展欧几里得

Romantic

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

Problem Description

The Sky is Sprite.
The Birds is Fly in the Sky.
The Wind is Wonderful.
Blew Throw the Trees
Trees are Shaking, Leaves are Falling.
Lovers Walk passing, and so are You.
…………………………..Write in English class by yifenfei

Girls are clever and bright. In HDU every girl like math. Every girl like to solve math problem!
Now tell you two nonnegative integer a and b. Find the nonnegative integer X and integer Y to satisfy X*a + Y*b = 1. If no such answer print “sorry” instead.
Input
The input contains multiple test cases.
Each case two nonnegative integer a,b (0

Output

output nonnegative integer X and integer Y, if there are more answers than the X smaller one will be choosed. If no answer put “sorry” instead.

Sample Input

77 51
10 44
34 79

Sample Output

2 -3
sorry
7 -3

Author
yifenfei

CODE:

#include 
#include 
using namespace std;
typedef long long LL;
LL exgcd(LL a,LL b,LL &x,LL &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    LL ans=exgcd(b,a%b,x,y);
    LL temp=x;
    x=y;
    y=temp-a/b*y;
    return ans;
}
void cal(LL a,LL b,LL c)
{
    LL x,y;
    LL gcd=exgcd(a,b,x,y);
    b=abs(b);//用g++提交,c++出现Compilation Error
    LL ans=x%b;
    if(ans<=0)
        ans+=b;
   if(gcd!=1)
        printf("sorry\n");
    else
        printf("%lld %lld\n",ans,(1-ans*a)/b);
}

int main()
{
    LL a,b;
    while(scanf("%lld%lld",&a,&b)!=EOF)
        cal(a,b,1);
    return 0;
}

你可能感兴趣的:(数论)