您的位置:首页 > 编程语言 > Java开发

最大公约数问题的优化解法

2016-07-23 15:20 489 查看
    写一个程序,求两个正整数的最大公约数(Greatest Common Divisor,GCD)。如果两个整数都很大,有什么简单的算法吗?

    (问题及思路来自于《编程之美》2.7节)

1. 辗转相除法

用辗转相除法确定两个正整数 a 和 b(a≥b) 的最大公因数gcd(a,b):

当a mod b=0 时gcd(a,b)=b,否则gcd(a,b) = gcd(b,a mod b)

2. 算法经典实现

/**
* 辗转相除法,经典实现
* @param x
* @param y
* @return
*/
public int gcd(int x, int y){
return (y == 0)?x:gcd(y, x % y);
}


3. 减少取模运算开销

    对于大整数而言,经典辗转相除法中的取模预算是非常昂贵的开销,将成为整个算法的瓶颈。

    分析:如果一个数能够同时整除x和y,则必能同时整除x-y和y;而能够同时整除x-y和y的数也必能同时整除x和y,即x和y的公约数与x-y和y的公约数是相同的,其最大公约数也是相同的,即f(x,y)=f(x-y,y)

/**
* 目的:减去取模运算的开销
* f(x,y) = f(x-y,y)
* @param x
* @param y
* @return
*/
public int gcd1(int x, int y){
if(x < y){
return gcd1(y,x);
}
if(y == 0){
return x;
}else{
return gcd1(x - y, y);
}
}

4. 减少迭代次数

    公约数特点:

1.如果y=k*y1,x=k*x1,那么有f(x,y)=k*f(x1,y1)

2.如果x=p*x1,假设p是素数,并且y%p!=0(即y不能被p整除),那么f(x,y)=f(p*x1,y)=f(x1,y)

    根据上述特点: 取p为2,此时最坏情况下的时间复杂度为O(log2(max(x,y)))

/**
* 目的:减法的迭代次数太多,需加速
* 公约数特点:
* 1.如果y=k*y1,x=k*x1,那么有f(x,y)=k*f(x1,y1)
* 2.如果x=p*x1,假设p是素数,并且y%p!=0(即y不能被p整除),
* 那么f(x,y)=f(p*x1,y)=f(x1,y)
* 根据上述特点:
* 取p为2,此时最坏情况下的时间复杂度为O(log2(max(x,y)))
* @param x
* @param y
* @return
*/
public int gcd2(int x, int y){
if(x < y){
return gcd2(y, x);
}
if(y == 0){
return x;
}else{
if(isEven(x)){ //分4种情况
if(isEven(y))
return (gcd2(x >> 1, y >> 1) << 1);
else
return gcd2(x >> 1, y);
}else{
if(isEven(y))
return gcd2(x, y >> 1);
else
return gcd2(y, x - y);

}
}
}
/**
* 判断x是否为偶数
* @param x
* @return
*/
private boolean isEven(int x) {
return x % 2 == 0;
}

参考资料:
1. 《编程之美》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息