最大公约数问题的基本求法是辗转相除法,辗转相除法的思想是这样的:首先两个数x,y,假设x>y,那么x = ky+b。x,y的公约数一定能够同时整除y,b。那么x,y的最大公约数一定就是y,b的最大公约数,如此不断的转换,直到b为0为止。
代码如下:
1 public int gcd(int x,int y){ 2 return (!y)?x:gcd(y,x%y); 3 }
由于取模运算是非常昂贵的开销,所以会成为程序的瓶颈,需要采用一些办法不使用取模操作。换一种思路,如果一个数能同时整除x,y,那么必然能够同时整除x-y和y,那么x,y的最大公约数一定就是x-y和y的最大公约数。如此循环,直到一方为0为止,为了防止出现x-y是负值的情况,所以要保证函数中的x一定大于y,如果x-y《y,那么就gcd(y,x-y)。具体代码如下:
1 public int gcd(int x,int y){ 2 if(x<y){ 3 return gcd(y,x) 4 } 5 if(y==0){ 6 return x; 7 }else{ 8 return gcd(x-y,y); 9 } 10 }
以上的代码用减法代替了除法,能够有更好的执行效率。但是迭代次数也增多了很多,在极端情况下,比如(10000000,1)这种情况,就相当糟糕了。
因此需要一种方式,同时减少除法运算和减法运算。
这就是解法三,不过我觉得一般情况下基本上是想不到解法三的做法的,假设k是素数,x=k*x1,y%k!=0,那么x,y的公约数中,肯定不含k,因此x/=k;如果两个都含k,那么x,y的公约数就等于k*gcd(x/=k,y/=k)。
2就是一个最简单的素数,同时在计算机中对2的操作可以转变为左移和右移的操作,非常的节省开销。
下面就针对不同的情况进行分析:
如果x能被2整除,y不能,那么gcd(x>>1,y)。x不能被2整除,y能被2整除同理。
如果x,y都能被2整除,则将这个2提取出来,2*gcd(x>>1,y>>1)。
如果x,y都不能被2整除,那么就用解法二的方法,算x和x-y的公约数,由于x,y为奇数,所以x-y一定是偶数,所以一定能变成上面的情况。具体代码如下:
1 public int gcd(int x,int y){ 2 if(x<y){ 3 return gcd(y,x); 4 } 5 if(y==0) 6 return x; 7 if(x%2!=0){ 8 if(y%2!=0){ 9 return gcd(x-y,y) 10 }else{ 11 return gcd(x,y>>1); 12 } 13 }else{ 14 if(y%2!=0){ 15 return gcd(x>>1,y); 16 }else{ 17 return gcd(x>>1,y>>1)<<1; 18 } 19 } 20 }
另外为了增加适用性,在gcd的参数中不一定是限定int类型,可以使BigInteger类型的。