​求两个整数的最大公约数,最优算法?

求两个整数的最大公约数,要尽量优化算法的性能。

暴力枚举法 version_1

#include
#include
using namespace std;

int GreatestCommonDivisor(int num1, int num2) {
    if (num1 <= 0 || num2 <= 0) {
        return -1;
    }
    int big = num1 > num2 ? num1 : num2;
    int small = num1 < num2 ? num1 : num2;

    if (big % small == 0) {
        return small;
    }

    for (int i = small / 2; i > 0; --i)
    {
        if (big % i == 0 && small % i == 0) {
            return i;
        }
    }
    return 1;
}


int main(int argc, char const* argv[])
{
    assert(GreatestCommonDivisor(10, 5) == 5);
    assert(GreatestCommonDivisor(25, 10) == 5);
    assert(GreatestCommonDivisor(255, 3) == 3);
    assert(GreatestCommonDivisor(2, 3) == 1);
    assert(GreatestCommonDivisor(20000, 2) == 2);
    assert(GreatestCommonDivisor(100, 75) == 25);
    assert(GreatestCommonDivisor(99, 55) == 11);
    return 0;
}

暴力枚举,从较小整数的一半开始,试图找到一个合适的数,判断是否能被这两个数整除。

虽然这个方法实现了所有的功能,但是效率不太行啊。


辗转相除法,又名欧几里得算法(Euclidean algorithm),该算法的目的是求出两个正整数的最大公约数。它是已知最古老的算法,其产生时间可追溯至公元前300年。

a, b(a > b) 的最大公约数等于 (a % b = c), b 和 c 的最大公约数

辗转相除法 version_2

#include
#include
using namespace std;

int GreatestCommonDivisor(int num1, int num2) {
    if (num1 <= 0 || num2 <= 0) {
        return -1;
    }
    int big = num1 > num2 ? num1 : num2;
    int small = num1 < num2 ? num1 : num2;

    if (big % small == 0) {
        return small;
    }

    return GreatestCommonDivisor(big % small, small);
}


int main(int argc, char const *argv[])
{
    assert(GreatestCommonDivisor(10, 5) == 5);
    assert(GreatestCommonDivisor(25, 10) == 5);
    assert(GreatestCommonDivisor(255, 3) == 3);
    assert(GreatestCommonDivisor(2, 3) == 1);
    assert(GreatestCommonDivisor(20000, 2) == 2);
    assert(GreatestCommonDivisor(100, 75) == 25);
    assert(GreatestCommonDivisor(99, 55) == 11);
    return 0;
}

辗转相除法相对于暴力枚举已经好了很多了,还有一个问题,当整数较大时,求余的性能比较差。


更相减损术,出自中国古代的《九章算术》,也是一种求最大公约数的算法。

两个正整数a和b(a>b),它们的最大公约数等于a-b的差值c和b的最大公约数。

更相减损术 version_3

#include
#include
using namespace std;

int GreatestCommonDivisor(int num1, int num2) {
    if (num1 <= 0 || num2 <= 0) {
        return -1;
    }
    int big = num1 > num2 ? num1 : num2;
    int small = num1 < num2 ? num1 : num2;

    if (big - small == 0) {
        return small;
    }

    return GreatestCommonDivisor(big - small, small);
}


int main(int argc, char const* argv[])
{
    assert(GreatestCommonDivisor(10, 5) == 5);
    assert(GreatestCommonDivisor(25, 10) == 5);
    assert(GreatestCommonDivisor(255, 3) == 3);
    assert(GreatestCommonDivisor(2, 3) == 1);
    assert(GreatestCommonDivisor(20000, 2) == 2);
    assert(GreatestCommonDivisor(100, 75) == 25);
    assert(GreatestCommonDivisor(99, 55) == 11);
    return 0;
}

更相减损术,避免了大整数取模可能出现的性能问题,但是当两数相差很大时,递归次数也很大。


辗转相除法结合更相减损术 version_4

把辗转相除法和更相减损术的优势结合起来,在更相减损术的基础上使用移位运算。

#include
#include
using namespace std;

int GreatestCommonDivisor(int num1, int num2) {
    if (num1 <= 0 || num2 <= 0) {
        return -1;
    }
    int big = num1 > num2 ? num1 : num2;
    int small = num1 < num2 ? num1 : num2;

    if (big - small == 0) {
        return small;
    }

    if ( (big & 1) == 0 && (small & 1) == 0) {
     // 当两数均为偶数时
        return GreatestCommonDivisor(big >> 1, small >> 1) << 1;

    } else if ( (big & 1) != 0 && (small & 1) != 0) {
     // 当两数均为奇数时,两数相减必是偶数,运用更相减损术
        return GreatestCommonDivisor(big - small, small);

    } else if ( (big & 1) == 0) {
     // 当较大的数是偶数时
        return GreatestCommonDivisor(big >> 1, small);

    } else {
     // 当较小的数是偶数时
        return GreatestCommonDivisor(big, small >> 1);
    }

    return 1;
}


int main(int argc, char const *argv[])
{
    assert(GreatestCommonDivisor(10, 5) == 5);
    assert(GreatestCommonDivisor(25, 10) == 5);
    assert(GreatestCommonDivisor(255, 3) == 3);
    assert(GreatestCommonDivisor(2, 3) == 1);
    assert(GreatestCommonDivisor(20000, 2) == 2);
    assert(GreatestCommonDivisor(100, 75) == 25);
    assert(GreatestCommonDivisor(99, 55) == 11);
    return 0;
}

这种方式在两数都比较小时,可能看不出计算次数的优势;当两数越大时,计算次数的减少就会越明显。

展开阅读全文

页面更新:2024-06-02

标签:最大公约数   整数   算法   两个   差值   除法   偶数   暴力   次数   性能   优势   小时   方式   科技

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top