最大公约数问题

解法一

早在公元前300年,欧几里德就在《几何原本》中给出了高效的辗转相除法。欧几里得辗转相除法是现在算法的鼻祖。

算法思路(伪代码)

function gcd (a, b)

    while  b ≠ 0

        t  = b

        b = a mod b  //取余

        a = t

     return a

算法证明
1. 两个数a、b,用a除以b,得 a = bq + r(q是商   r是余数)。若 r等于0,则b为最大公约数,否则,接着往下走

2. 证明一点:任何a和b的公约数都是r的公约数。

   证明:假设d为a、b的公约数,则a = md  b = nd.  

            r = a - bq = md - ndb = (m - nb)d. 得证

3. 对于所有的d,这都是正确的,因此a、b的公约数也即b、r的公约数。a、b的最大公约数也即b、r的最大公约数。

这样一直重复2 3过程,直到r=0。则b、r的最大公约数为b。这样得求。

例子说明

12 和 8的最大公约数:

b != 0

a = 12, b = 8 tmp = b = 8 b = a % b = 12 % 8 = 4 a = tmp = 8
b
!= 0 tmp = b = 4 b = a % b = 0 a = tmp = 4 最大公约数为4

程序实现

 迭代思路

#include <stdio.h>

int gcd(int a, int b)

{

    int tmp;

    while (b != 0)

    {

        tmp = b;

        b = a % b;

        a = tmp;

    }



    return a;

}

int main()

{

    printf("%d\n", gcd(12, 8));

    

    return 0;

}

 

 递归思路

#include <stdio.h>

int gcd(int a, int b)

{

    return (!y) ? x : gcd(b, a % b);

}

int main()

{

    printf("%d\n", gcd(12, 8));

    

    return 0;

}

瑕疵:用到了取模运算,对于大整数而言,取模运算是非常昂贵的开销,成为了该算法的瓶颈。

 

解法二

类似于欧几里德辗转相除法,两个数的公约数必然是两者之差的公约数(a = md, b = nd, a - b = (m-n)*d),因此f(a,b) = f(b, a-b),这里注意前者大于后者,否则,把两者换过来。

示例

f(42,30)=f(30,12)=f(12,18)=f(18,12)=f(12,6)=f(6,6)=f(6,0), 结果为6

代码

#include <iostream>

using namespace std;



int gcd(int m, int n)

{

    if(n == 0)

        return m;

    if(m < n)

        return gcd(n, m);

    else

        return gcd(n, m-n);

}



int main()

{

    cout << "42, 30:" << gcd(42, 30) << endl;

    return 0;

}

结果

瑕疵:该方法免去了大整数除法的繁琐,但是新的不足之处在于:遇到一大一小,相差悬殊的情况(例如:(200000000, 1))

 

解法三

分析公约数特点:对于x和y来说(k为素数)

如果x = k * x1, y = k * y1,那么gcd(x,y)=gcd(x1,y1)*k

如果x = k * x1, y不能被k整除,那么gcd(x,y)=gcd(x1,y)

如果x不能被k整除 y = k * y1,那么gcd(x,y)=gcd(x,y1)

代码

#include <iostream>

using namespace std;



int gcd(int m, int n)

{

    cout << "m, n:" << m << " " << n << endl;

    if(n == 0)

        return m;

    if(m < n)

        return gcd(n, m);

    else

    {

        if(m % 2 == 0)

        {

            if(n % 2 == 0)

                return (gcd(m >> 1, n >> 1) << 2);

            else

                return gcd(m >> 1, n);

        }

        else

        {

            if(n % 2 == 0)

                return gcd(m , n >> 1);

            else

                return gcd(n, m - n);

        }

    }

}



int main()

{

    cout << "20000, 1000:" << gcd(20000, 1000) << endl;

    return 0;

}

结果

最大公约数问题

评述:解法一瓶颈在于计算大整数除法,解法二瓶颈在于有时迭代次数太多。解法三充分综合了一二的优点。另外解法三选取2作为素数,通过移位方便的进行乘除,避免了一定的大整数乘除法。

 

参考:《编程之美》2.7

你可能感兴趣的:(问题)