算法研究之最大公约数

公约数:假设有两个数a,b,所谓的公约数就是能把a,b整除的最大整数。

求最大公约数的方法有很多。

方法1:穷举法,即通过循环找到一个2个数都能整除的数

public class Divide {
	public int getMaxDivide(int a,int b){
		int value=0,min=0,max=0;
		if(a==b){
			return a;
		}
		if(a>b){
			max=a;
			min=b;
		}else {
			max=b;
			min=a;
		}
		for(int i=2;i<=min;i++){
			if(a%i==0&&b%i==0){
				value=i;
			}
		}
		return value;
	}
	public static void main(String[] args) {
		int a=60;
		int b=120;
		System.out.println(new Divide().getMaxDivide(a, b));
	}
}

方法2:欧几里德算法,公约数表示为f(a,b),并且有a>=b>0,欧几里德就给了我们一个很好的定理,f(a,b)=f(b,a%b)

我们来看下这个等式是怎么来的
设有 r=a/b ,q=a%b
所以就有 a=a/b*b+q ----(这里的a/b*b!=a ,原因就是我们用的是整数来计算的)
也就是a=r*b+q 变换一下有:q=a-r*b 设d=f(a,b),a/d=m,b/d=n;则 有q=dm-r*dn=d(m-rn)
所以q/d也为0;设d|q表示d是q的约数;以下相同;
又有d|b;所以有d|(b,q),设D是(b,q)的最大公约数,则会有d<=D=f(b,q);

再回到前面r=a/b,q=a%b这两个条件有
a=r*b+q,由于D|(b,q),所以D|a,所以有D|(a,b)
所以有D<=d=f(a,b),结合上部分就有d<=D <+d,及D=d;
所以得证;

通过递归的方法,我们很容易获得这样的算法:

public class Divide {
	public int getMaxDivide(int a,int b){
		if(a<b){
			int temp=a;
			a=b;
			b=temp;
		}
		if(b==0){
			return a;
		}
		return getMaxDivide(b, a%b);
	}
	public static void main(String[] args) {
		int a=60;
		int b=120;
		System.out.println(new Divide().getMaxDivide(a, b));
	}
}

方法3:对于大数来言,取模运算代价很大,因此我们可以利用f(x,y)=f(x-y,y)

public class Divide {
	public int getMaxDivide(int a,int b){
		if(a<b){
			int temp=a;
			a=b;
			b=temp;
		}
		if(b==0){
			return a;
		}
		return getMaxDivide(a-b, b);
	}
	public static void main(String[] args) {
		int a=60;
		int b=120;
		System.out.println(new Divide().getMaxDivide(a, b));
	}
}

这种方法和第二种方法相比,缺点在于递归迭代次数多。

方法3:

(1)如果y=k*y1,x=k*x1.那么有f(x,y)=k*f(x1,y1)
(2)如果x=p*x2,p为素数,并且y%p != 0,那么f(x,y) = f(p*x2,y) = f(x2,y)
于是我们得到下面的解决方法:
将p = 2,
若x,y均为偶数,f(x,y) = 2*f(x/2,y/2) = 2*f(x>>1,y>>1);
若x是偶数,y是奇数,f(x,y) = f(x/2,y) = f(x>>1,y);
若x是奇数,y是欧式,f(x,y) = f(x,y/2) = f(x,y>>1);
若x和y均为奇数,f(x,y) = f(y,x-y)。这时x-y一定是偶数,下一步一定会除以2。

public static int moveCombe(int a,int b){
                if(a<b){
                        return moveCombe(b, a);
                }
                if(0==b){
                        return a;
                }
                if(isTwo(a)){
                        if(isTwo(b)){
                                return moveCombe(a/2, b/2)*2;
                        }
                        return moveCombe(a/2, b);
                }else{
                        if(isTwo(b)){
                                return moveCombe(a, b/2);
                        }
                        return moveCombe(b, a-b);
                }
                         
                         
        }
        private static boolean isTwo(int a) {
                if(0==a%2){
                        return true;
                }else{
                        return false;
                }
        }





上一篇:spark rdd saveAsTextFile保存为文件


下一篇:日志服务数据加工最佳实践: 日期时间处理