最大公约数与最小公倍数

来源:互联网 发布:down.php文件怎么打开 编辑:程序博客网 时间:2024/06/06 08:45
最大公约数与最小公倍数
         古希腊数学家欧几里德提出了求两个自然数的最大公约数的方法——辗转相除法,又称为欧几里德算法
    我们一般用(x1, x2, …, xn)表示n个正整数x1, x2, …, xn的最大公约数;用[x1, x2, …, xn]表示x1, x2, …, xn的最小公倍数。
求a,b的最大公约数和最小公倍数有以下4个常用的方法:

例如:     (24, 72, 108, 32)  [24, 72, 108, 32]   = (((24,72), 108), 32)= [[[24,72], 108], 32]   = ((24, 108), 32)= [[72, 108], 32]   = (12, 32)= [216, 32]   = 4= 864
【程序1】最大公约数
        求两个自然数m,n的最大公约数
由公式(1):  读入m,n;  while n>0 {      r ← m mod n;      m ← n;      n ← r;  }  输出m;


【程序2】n个数的最大公约数和最小公倍数 
        编程求n个(n<=100)正整数Ai(Ai<=30000,1<=i<=n)的最小公约数和最小公倍数。假设解一定在长整数范围之内。
样例输入
4
24 72 108 32
样例输出
4 864
由公式(3) 和 (4):             1) read x;              2) gcd ← (gcd, x);                3) lcm ← (lcm, x);                4) goto 1).C++ code:#include<iostream>using namespace std;int gcd1(int x,int y){if(y==0) return x;else return gcd1(y,x%y);}int main(){int x,y,r,n;cin >> n >> x;    int gcd=x,lcm=x;    for(int i=2;i<=n;i++){cin >> x;gcd=gcd1(x,gcd);  //(gcd,x)lcm=lcm/gcd1(lcm,x)*x;   //[lcm,x]   }        cout << gcd << endl << lcm << endl; }

扩展欧几里德算法
        扩展欧几里德定理
        对于不完全为 0 的非负整数 a,b,gcd(a,b)表示a,b的最大公约数,必然存在整数对x,y,使得
         gcd(a,b)= ax + by
        利用扩展欧几里得算法不进可以得出任意两个正整数a和b的最大公约数d,而且还能计算出满足下式的整系数x和y: d=gcd(a,b)=ax+by (x和y可能为0或负数)
C++ code:int exgcd(int a,int b,int &x,int &y){    if(b==0){ x=1; y=0; return a; }    int r=exgcd(b,a%b,x,y);    int t=x;    x=y; y=t-a/b*y;    return r;}
        
扩展欧几里德算法的应用主要有以下三方面:
(1)求解不定方程;
(2)求解模线性方程(线性同余方程);
(3)求解模的逆元;

(1)使用扩展欧几里德算法解决不定方程的办法:
    对于不定整数方程pa+qb=c,若 c mod Gcd(p, q)=0,则该方程存在整数解,否则不存在整数解。
    上面已经列出找一个整数解的方法,在找到p * a+q * b = Gcd(p, q)的一组解p0,q0后,p * a+q * b = Gcd(p, q)的其他整数解满足:
        p = p0 + b/Gcd(p, q) * t 
        q = q0 - a/Gcd(p, q) * t(其中t为任意整数)
    至于pa+qb=c的整数解,只需将p * a+q * b = Gcd(p, q)的每个解乘上 c/Gcd(p, q) 即可。
    在找到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的所有整数解。
bool linear_equation(int a,int b,int c,int &x,int &y){    int d=exgcd(a,b,x,y);    if(c%d) return false;    int k=c/d;    x*=k; y*=k;    //求得的只是其中一组解    return true;}
        
(2) 用扩展欧几里德算法求解模线性方程的方法:
    同余方程 ax≡b (mod n)对于未知数 x 有解,当且仅当 gcd(a,n) | b。且方程有解时,方程有 gcd(a,n) 个解。
    求解方程 ax≡b (mod n) 相当于求解方程 ax+ ny= b, (x, y为整数)
    设 d= gcd(a,n),假如整数 x 和 y,满足 d= ax+ ny(用扩展欧几里德得出)。如果 d| b,则方程a*x0 + n*y0 = d, 方程两边乘以 b/d,(因为 d|b,所以能够整除),得到 a * x0 * b/d + n * y0 * b/d=b。
    所以 x= x0* b/ d,y= y0* b/ d 为 ax+ ny= b 的一个解,所以 x= x0* b/ d 为 ax= b (mod n ) 的解。
    ax≡b (mod n)的一个解为 x0= x* (b/ d ) mod n,且方程的 d 个解分别为 xi= (x0+ i* (n/ d ))mod n {i= 0... d-1}。
    设ans=x*(b/d),s=n/d;
    方程ax≡b (mod n)的最小整数解为:(ans%s+s)%s;

相关证明:     证明方程有一解是: x0 = x'(b/d) mod n;
    由 a*x0 = a*x'(b/d) (mod n)
        a*x0 = d (b/d) (mod n) (由于 ax' = d (mod n))
            = b (mod n)
    证明方程有d个解: xi = x0 + i*(n/d) (mod n);
    由 a*xi (mod n) = a * (x0 + i*(n/d)) (mod n)
            = (a*x0+a*i*(n/d)) (mod n)
            = a * x0 (mod n) (由于 d | a)
            = b

首先看一个简单的例子:
    5x=4 (mod 3)
    解得x = 2,5,8,11,14.......
    由此可以发现一个规律,就是解的间隔是3.
    那么这个解的间隔是怎么决定的呢?
    如果可以设法找到第一个解,并且求出解之间的间隔,那么就可以求出模的线性方程的解集了。
    我们设解之间的间隔为dx.

那么有
    a*x = b(mod n);     a*(x+dx) = b(mod n); 两式相减,得到:
    a*dx(mod n)= 0;
    也就是说a*dx就是a的倍数,同时也是n的倍数,即a*dx是a 和 n的公倍数.为了求出dx,我们应该求出a 和 n的最小公倍数,此时对应的dx是最小的.
    设a 和 n的最大公约数为d,那么a 和 n 的最小公倍数为(a*n)/d.
    即a*dx = a*n/d;
    所以dx = n/d.
因此解之间的间隔就求出来了.
bool modular_linear_equation(int a,int b,int n){    int x,y,x0,i;    int d=exgcd(a,n,x,y);    if(b%d) return false;    x0=x*(b/d)%n;   //特解    for(i=1;i<d;i++)        printf("%d\n",(x0+i*(n/d))%n);    return true;}
        
(3)用欧几里德算法求模的逆元:
    同余方程ax≡b (mod n),如果 gcd(a,n)== 1,则方程只有唯一解。
    在这种情况下,如果 b== 1,同余方程就是 ax=1 (mod n ),gcd(a,n)= 1。
    这时称求出的 x 为 a 的对模 n 乘法的逆元。
    对于同余方程 ax= 1(mod n ), gcd(a,n)= 1 的求解就是求解方程
    ax + ny= 1,x, y 为整数。这个可用扩展欧几里德算法求出,原同余方程的唯一解就是用扩展欧几里德算法得出的 x 。


【程序3】Romantic 
         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


    女孩子们是聪明而可爱的。在layz的每一个女孩都喜欢数学,每一个女孩都喜欢解决数学问题。
    现在告诉你两个非负整数a和b,找到非负整数x和y满足 x*a +y*b = 1。如果没有这样的答案存在输出“sorry”。
输入: 一行,两个非负整数a,b (0<a, b<=2^31)
输出: 一行,非负整数x和y,用空格隔开。如果有多组解,输出最小的x的那组解。如果无解,输出“sorry”。
样例输入:
77 51
10 44
34 79
样例输出:
2 -3
sorry
7 -3

【分析】朴素的扩展欧几里德算法,求线性方程的解,x最小。
C++ code:#include<iostream>using namespace std;//返回d=gcd(a,b);和对应于等式ax+by=d中的x,ylong exgcd(long a,long b,long &x,long &y){    if(a==0&&b==0)  return -1;   //无最大公约数    if(b==0){x=1;y=0;return a;}    long d=exgcd(b,a%b,y,x);    y-=a/b*x;    return d;}int main(){    long a, b, x, y, d;    cin >> a >> b;    d = exgcd(a, b, x, y);    if(d == 1 ){            while(x<0){   //output nonnegative integer X and integer Y                //a * x + b * y = 1                   //->  a*(x+b) + b*(y-a) = a*x + a*b + b*y - a*b = 1                x = x+b;                y = y-a;            }            cout << x << ' ' << y << endl;        }        else { cout << "sorry\n";}    return 0;}
0 1