问题:Java获取两个数的最大公约数
1:暴力法
暴力法指的是什么呢?
很简单,就是从两数较小值开始遍历,当然最好是small/2.
代码较简单。
/**
* @param a
* @param b
* 暴力法:for循环
* 如果是10000和10001时,需要循环4999次。太麻烦。
*/
public static int getValues(int a,int b){
int small=Math.min(a,b);
int big=Math.max(a,b);
if (big%small==0){
return small;
}
for (int i=big/2;i>1;i--){
if (small%i==0&&big%i==0){
return i;
}
}
return 1;
}
这种方法上面的代码中也有提到,如果是10000和10001时,需要循环4999次,十分麻烦,这种方法的时间复杂度O(min(a,b));
2:辗转相除法(欧几里得算法)
辗转相除法基于一个定理,即:两个正整数a和b(a>b)的最大公约数与a除以b的余数与b的最大公约数相等。
具体做法:用a除以b得到余数c,将其转化为求b和c的最大公约数,然后b继续除以c获得d,求d与c的最大公约数。。。直到余数为0时停止。
看下代码实现:
/**
* @param a
* @param b
* @return
* 辗转相除法(欧几里得算法)
* a和b的最大公约数(a>b)等于a%b的余数与b的最大公约数
* 问题:a和b的值过大时,取模运算性能太差
*/
public static int getValues2(int a,int b){
int small=Math.min(a,b);
int big=Math.max(a,b);
if (big%small==0){
return small;
}
return getValues2(big%small,small);
}
时间复杂度不太好计算。可以近似看成O(Log(max(a,b))).
3:更相减损法
更相减损法依据的定律:两个正整数a和b(a>b)的最大公约数与a-b的差与b的最大公约数相等。
所以具体的做法可以参照辗转相除法,利用递归获取。
/**
* @param a
* @param b
* @return
* 更相减损法
* a>b ,a-b=c和b的最大公约数等于a,b的最大公约数
*
* 问题:如果是10000和1的最大公约数,这时,递归的次数就有9999次,
*/
public static int getValues3(int a,int b){
int small=Math.min(a,b);
int big=Math.max(a,b);
if (small==big){
return small;
}
return getValues3(big-small,small);
}
相对于站桩相除法,减少了取模运算,但性能不太稳定,最坏时间复杂度为
O(max(a,b)。
4:优化的更新减损法
/**
* @param a
* @param b
* @return
* 优化方法:对更相减损法进行位移操作。
* 如果:a,b均为偶数。则gcd代表方法名,gcd(a,b)=2*gcd(a/2,b/2)=2*gcd(a>>1,b>>1);
* 若a为奇数,b为偶数,则:gcd(a,b)=gcd(a,b>>1)
* 若b为奇数,a为偶数,则:gcd(a,b)=gcd(a>>1,b)
*/
public static int getValues4(int a,int b){
if (a==b){
return a;
}
if ((a&1)==0&&(b&1)==0){
return getValues4(a>>1,b>>1)<<1;
}else if ((a&1)==0&&(b&1)!=0){
return getValues4(a>>1,b);
}else if ((a&1)!=0&&(b&1)==0){
return getValues4(a,b>>1);
}else{
int big=Math.max(a,b);
int small=Math.min(a,b);
return getValues4(big-small,small);
}
}
时间复杂度为O(Log(max(a,b)))。