举个例子,比如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)=2∗gcd(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;
}