「辗转相除法」和「更相减损术」求最大公约数

一、辗转相除法

举个例子,比如155和65
155=65*2+25
65=25*2+15
25=15*1+10
15=10*1+5
10=5*2+0
一直除到余数为0为止,所以最大公约数是5

public int gcd1(int a, int b) {
        int l = Math.max(a, b);
        int s = Math.min(a, b);
        while (s != 0) {
            int r = l % s;
            l = s;
            s = r;
        }
        return l;
    }

辗转相除法的最大问题在于,"%"这个操作因为是除法比较耗时,尤其是当l和s比较大的时候,会造成性能下降。

二、更相减损术

还是155和65
155-65=90
90-65=25
65-25=40
40-25=15
25-15=10
15-10=5
10-5=5
一直减到被减数和差相等为止,所以最大公约数是5

public int gcd2(int a, int b) {
        int l = Math.max(a, b);
        int s = Math.min(a, b);
        int r = l - s;
        while (s != r) {
            l = Math.max(s, r);
            s = Math.min(s, r);
            r = l - s;
        }
        return r;
    }

更相减损术的问题是遇到差别很大的数据,比如99999和1的时候要互减99998次,这样显然不行,所以我们做一个改进,把上面两个方法的优势结合起来。

三、两种方法的结合

结合的规律是这样的:
1.如果a、b都是偶数,则最大公约数 g c d ( a , b ) = 2 ∗ g c d ( a 2 , b 2 ) gcd(a,b)=2*gcd(\frac{a}{2},\frac{b}{2}) gcd(a,b)=2gcd(2a,2b)
比如gcd(100,50)=50=2*gcd(50,25)=2*25=50
2.如果a、b一个是偶数一个是奇数,则最大公约数 g c d ( a , b ) = g c d ( a 2 , b ) gcd(a,b)=gcd(\frac{a}{2},{b}) gcd(a,b)=gcd(2a,b)[这里假定a是偶数]
比如gcd(100,25)=25=gcd(50,25)=25
3.如果a、b都是奇数,则执行一次更相减损术,将其转化为1、2两种状态
比如a,b分别是49和21,执行一次相减,49-21=28,那a,b就变成了28和21,这就回到了状态2
注意每一次变换完都要减一下看看是不是已经结束了。
——————————————————————————————————
举个例子:
a=67870,b=23446,multi=1
a-b=44424!=b
a、b都是偶数,执行状态1,则a=33935,b=11723,multi=2
(因为都是偶数时要把2提出来,我们把这个倍数赋给multi,最后记得乘回来就行)
a-b=22212!=b
a、b都是奇数,执行状态3,则a=22212,b=11723
a-b=10489!=b
a是偶数,b是奇数,执行状态2,则a=11106,b=11723,交换一下让a是较大数,a=11723,b=11106
a-b=617!=b
a是奇数,b是偶数,执行状态2,则a=11723,b=5553
a-b=6170!=b
a、b都是奇数,执行状态3,则a=6170,b=5553
a-b=617!=b
a是偶数,b是奇数,执行状态2,则a=5553,b=3085
a-b=2468!=b
a、b都是奇数,执行状态3,则a=3085,b=2468
a-b=617!=b
a是奇数,b是偶数,执行状态2,则a=3085,b=1234
a-b=1851!=b
a是奇数,b是偶数,执行状态2,则a=3085,b=617
a-b=2468!=b
a、b都是奇数,执行状态3,则a=2468,b=617
a-b=1851!=b
a是偶数,b是奇数,执行状态2,则a=1234,b=617
a-b=617==b
所以最大公约数是617*multi=1234
———————————————————————————————————

public int gcd3(int a, int b) {
        int l = Math.max(a, b);
        int s = Math.min(a, b);
        int r = l - s;
        int multi = 1;
        while (s != r) {
            if ((l & 1) == 0 && (s & 1) == 0) {
                l = l >> 1;
                s = s >> 1;
                multi <<= 1;
            } else if ((l & 1) == 0 && (s & 1) != 0) {
                l = l >> 1;
                if (l < s) {
                    int p = l;
                    l = s;
                    s = p;
                }
            } else if ((l & 1) != 0 && (s & 1) == 0) {
                s = s >> 1;
            } else {
                l = Math.max(s, r);
                s = Math.min(s, r);
            }
            r = l - s;
        }
        return r * multi;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值