• 最大公约数问题


    解法一

    早在公元前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

  • 相关阅读:
    Linux system basic 2 + add kernel for Jupyter
    Linux package installation: deb and rpm
    classification tips 01: npy file
    how to activate XMind8 to pro version.
    Linux system 初步
    try_except_finally
    Postgresql Json Sql
    python package install error and little code bugs
    小程序用户操作事件
    套数据操作步骤
  • 原文地址:https://www.cnblogs.com/kaituorensheng/p/3021273.html
Copyright © 2020-2023  润新知