求两个整数的最小公倍数和最大公约数的算法及其C++实现

今天碰到一道C++练习题求两个整数的最小公倍数和最大公约数的算法及其C++实现,感觉可以做个整理笔记。

最大公约数
最大公约数(英语:Greatest Common Divisor,简写为G.C.D.;或Highest Common Factor,简写为H.C.F.),指某几个整数共有约数中最大的一个。
求两个整数最大公约数主要的方法:

  • 列举法:各自列出约数,再找出最大的公约数
  • 素因数分解法:两数各作素因数分解,然后取出共有的项相乘,所得的积就是这几个数的最大公约数。
  • 短除法:短除法求最大公约数,先用这几个数的公约数连续去除,一直除到所有的商互质为止,然后把所有的除数连乘起来,所得的积就是这几个数的最大公约数。
  • 辗转相除法:辗转相除法是求两个自然数的最大公约数的一种方法,也叫欧几里德算法。
    例如,求(319,377):
    ∵ 319÷377=0(余319)
    ∴(319,377)=(377,319);
    ∵ 377÷319=1(余58)
    ∴(377,319)=(319,58);
    ∵ 319÷58=5(余29),
    ∴ (319,58)=(58,29);
    ∵ 58÷29=2(余0),
    ∴ (58,29)= 29;
    ∴ (319,377)=29.
  • 扩展欧几里德算法:扩展欧几里得算法(又称扩充欧几里得算法)是用来解某一类特定的不定方程的一种方法,常用用来求解模线性方程及方程组。扩展的欧几里得算法可以用来计算模逆元,而模逆元在公钥密码学中占有举足轻重的地位。
    基本算法:对于不完全为 0 的非负整数 a,b,gcd(a,b)表示 a,b 的最大公约数,必然存在整数对 x,y ,使得 gcd(a,b)=ax+by。
    证明:设 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,所以递归可以结束。
  • 更相减损法:也叫更相减损术,是出自《九章算术》的一种求最大公约数的算法,它原本是为约分而设计的,但它适用于任何需要求最大公约数的场合。
    第一步:任意给定两个正整数;判断它们是否都是偶数。若是,则用2约简;若不是则执行第二步。
    第二步:以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数。继续这个操作,直到所得的减数和差相等为止。
    则第一步中约掉的若干个2与第二步中等数的乘积就是所求的最大公约数。
    其中所说的“等数”,就是最大公约数。求“等数”的办法是“更相减损”法。所以更相减损法也叫等值算法。
    例1、用更相减损术求98与63的最大公约数。
    解:由于63不是偶数,把98和63以大数减小数,并辗转相减:
    98-63=35
    63-35=28
    35-28=7
    28-7=21
    21-7=14
    14-7=7
    所以,98和63的最大公约数等于7。
    这个过程可以简单的写为:
    (98,63)=(35,63)=(35,28)=(7,28)=(7,21)=(7,14)=(7,7)=7.
    例2、用更相减损术求260和104的最大公约数。
    解:由于260和104均为偶数,首先用2约简得到130和52,再用2约简得到65和26。
    此时65是奇数而26不是奇数,故把65和26辗转相减:
    65-26=39
    39-26=13
    26-13=13
    所以,260与104的最大公约数等于13乘以第一步中约掉的两个2,即13*2*2=52。
    这个过程可以简单地写为:
    (260,104)(/2/2) =>(65,26)=(39,26)=(13,26)=(13,13)=13. (*2*2) => 52
  • Stein算法:Stein算法由J. Stein 1961年提出,这个方法也是计算两个数的最大公约数。和欧几里德算法算法不同的是,Stein算法只有整数的移位和加减法,这对于程序设计者是一个福音。
    设置A1=A、B1=B和C1=1
    1、如果An=0,Bn*Cn是最大公约数,算法结束
    2、如果Bn=0,An*Cn是最大公约数,算法结束
    3、如果An和Bn都是偶数,则An+1=An/2,Bn+1=Bn/2,Cn+1=Cn*2(注意,乘2只要把整数左移一位即可,除2只要把整数右移一位即可)
    4、如果An是偶数,Bn不是偶数,则An+1=An/2,Bn+1=Bn,Cn+1=Cn (很显然啦,2不是奇数的约数)
    5、如果Bn是偶数,An不是偶数,则Bn+1=Bn/2,An+1=An,Cn+1=Cn (很显然啦,2不是奇数的约数)
    6、如果An和Bn都不是偶数,则An+1=|An-Bn|,Bn+1=min(An,Bn),Cn+1=Cn
    7、n加1,转步骤1
    考虑欧几里德算法,最恶劣的情况是,每次迭代a=2b-1,这样,迭代后,r=b-1。如果a小于2N,这样大约需要4N次迭代。而考虑Stein算法,每次迭代后,显然A(n+1)B(n+1)≤AnBn/2,最大迭代次数也不超过4N次。也就是说,迭代次数几乎是相等的。但是,需要注意的是,对于大素数,试商法将使每次迭代都更复杂,因此对于大素数Stein将更有优势。
    以上内容整理自维基百科,百度百科

C++实现

  • 辗转相除法:
#include
int main()
{
    int a,b,c;
    scanf("%d %d",&a,&b);
    if(a!=0&&b!=0)   //先判断输入整数中有无零值,有零值的话无法求公约数返回error;
    {
        do{
        c=a%b;
        a=b;
        b=c;
    }while(c);
    }
    else
        printf("error\n");
    printf("%d\n",a);
    return 0;
}
  • 更相减损法:
#include
#include
int main()
{
    int a,b,n=0;
    scanf("%d %d",&a,&b);
    if(a!=0&&b!=0)   //先判断输入整数中有无零值,有零值的话无法求公约数返回error;
    {
        while(a%2==0&&b%2==0)
        {
            a/=2;
            b/=2;
            n++;
        }
        while(a!=b)
        {
            (a>b)?a-=b:b-=a;
        }
        a=a*pow(2,n);
        printf("%d\n",a);
    }
    else
        printf("error\n");
    return 0;
}
  • Stein算法:
#include
int gcd(int n,int m)      //适用于计算很大的数的最大公约数;
{
    if(n==m)
        return n;
    else if(n==0)      //不明白这个算法为什么有零值还能求公约数,后头再查查
            return m;
    else if(m==0)
            return n;
    else if(m>n)
        return gcd(m,n);
    if(n%2==0)
    {
        if(m%2==0)
            return gcd(n>>1,m>>1)<<1;
        else
            return gcd(n>>1,m);
    }
    else
    {
        if(m%2==0)
            return gcd(n,m>>1);
        else
            return gcd((n-m)>>1,m);
    }
}
int main()
{
    int a,b;
    scanf("%d %d",&a,&b);
    printf("%d\n",gcd(a,b));
    return 0;
}

最小公倍数
最小公倍数是数论中的一个概念。若有一个数X,可以被另外两个数A、B整除,且X大于(或等于)A和B,则X为A和B的公倍数。A和B的公倍数有无限个,而所有的公倍数中,最小的公倍数就叫做最小公倍数。两个整数公有的倍数称为它们的公倍数,其中最小的一个正整数称为它们两个的最小公倍数。同样地,若干个整数公有的倍数中最小的正整数称为它们的最小公倍数。n整数 a1,a2,…,an的最小公倍数一般记作: [a1,a2,…,an],或者参照英文记法记作 lcm(a1,a2,…,an),其中lcm是英语中“最小公倍数”一词(lowest common multiple)的首字母缩写。
例如,十天干和十二地支的混合称为一个阴历年,干支循环回归同一名称的所需时间,就是12和10的最小公倍数,即是60──一个“甲子”。
(摘自维基百科)
C++算法实现

根据最大公约数来求最小公倍数,以辗转相除法为例:

#include
int main()
{
    int a,b,c,n,m;
    scanf("%d %d",&n,&m);
    if(n!=0&&m!=0)   //先判断输入整数中有无零值,有零值的话无法求公约数返回error;
    {
        a=n;
        b=m;
        do{
            c=a%b;
            a=b;
            b=c;
        }while(c);
    }
    else
        printf("error\n");
    printf("%d\n",n*m/a);
    return 0;
}

你可能感兴趣的:(C++)