求最大公约数之四部曲

解法一:

欧几里得算法 ( 又称辗转相除法 )

        题:给定两个正整数 mn ,求它们的最大公约子(即能得到同时整除 mn 的最大正整数)

        解:

          E1.[ 求余数 ]nm 并令 r 为所得余数。(我们将有 0<<r<n

          E2.[ 余数为 0]r=0 ,算法结束, n 即为答案

          E3.[ 减少 ]m<-nn<-r ,并返回步骤 E1

 

算法示意图:


  

public int commonValue(int max,int min){

        if(max<0|| min<0){

           System.out.println("输入的两个参数必须为正整数。");

           return -1;

       }

       if(max<min){

            int temp = min;

            min = max;

            max = temp;

       }

       int remainder = max%min;

       while(remainder!=0){

           max = min;

           min = remainder;

           remainder = max%min;

       }
       return min;

    }
 

 

 

引申题:

   求两个线段长度的“最大公共量度”   ,其中心思想就是求最大公约数

 

 

解法二:

解法一中,用到了取模运算。但对于大整数而言,取模运算(其中用到除法)是非常昂贵的开销,有没有办法能够不用取模运算呢?

  分析:如果一个数能够同时整除 xy(x > y) ,则必能同时整除 x-yy;xy 的公约数与 x-yy 的公约数是相同的,其最大公约数也是相同的,即 f(x,y)=f(x-y,y) ,那么就可以不再需要进行大整数的取模运算,而转换成简单得多的大整数的减法。

示例:

  f(42,30)=f(30,12)=f(18,12)=f(12,6)=f(6,6)=f(6,0) = 6

 

public int cv(int max,int min){

        if(max<min)

            return cv(min,max);

        if(min == 0)

            return max; 

        else

            return cv(max-min,min);

    }

 

   优点: 用减法代替除法,可以提高算法的效率。

   缺点: 如果遇到 f(10 000 000 000 000,1) 这类情况(即两数值相差很大),本算法就不如算法一效率高。

 

 

解法三:

更相减损术: 是出自《九章算术》的一种求 最大公约数 算法 ,它原本是为 约分 而设计的,但它适用于任何需要求最大公约数的场合。

第一步:任意给定两个正整数;判断它们是否都是偶数。若是,则用2 约简;若不是则执行第二步。

第二步:以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数。继续这个操作,直到所得的减数和差相等为止。

  则第一步中约掉的若干个2 与第二步中等数的乘积就是所求的最大公约数。


该算法的思想是用 2 约简,该算法相比算法二效率有所提高, 那能不能进一步改进算法,想一想该算法只是在两个整数都为偶数的时候用 2 约简,当一个为偶数,一个有奇数时能约简吗?

 

 

private int count = 0;   //计数器,标记用2约简的次数
public int cv2(int max,int min){
		 if(max<min)
			 return cv2(min,max);
		 if(isEven(max)&&isEven(min)){
			 count++;
			 return cv2(max>>1,min>>1);
		 }
		 if(min == max-min){
			 while(count-->0){
			    min = min<<1;
			 }
			 return min;
		 }
		 else
			 return cv2(max-min,min);
	}
	//函数--判断是否是偶数
	public boolean isEven(int i){
		if((i&1) == 0)
			return true;
		return false;
	}
 

解法四:

对于 yx 来说,如果 y = k*y, x = k*x1 。那么有 f(y,x)=k*f(y1 ,x1 ) 。另外,如果 x = p*x1 , 假设 p 是素数,并且 y%p!=0(y 不能被 p 整除 ) ,那么 f(x,y)=f(p*x1, y)=f(x1, y)

同样的为了便于提高运算效率,采用 2 这个素数(乘除运算便于转化成位移运算)。

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(y,x-y)

那么在 f(x,y)=f(y,x-y) 之后, (x-y) 是一个偶数,下一步定会有除以 2 的操作(即两奇数相减得偶数)。

最坏情况下的时间复杂度是 O(log2 (max(x,y)))

 

f(42,30)=f(42/2,30/2)

          =2 * f(21,15)

          =2 * f(15,21-15)

          =2 * f(15,6)

          =2 * f(15,6/2)

          =2 * f(15,3)

          =2 * f(15-3,3)

          =2 * f(12,3)

          =2 * f(12/2,3)

          =2 * f(6,3)

          =2 * f(6/2,3)

          =2 * f(3,3)

          =2 * f(3,0)

          =2*3

          =6

 

public int cv(int max,int min){

        if(max<min)

            return cv(min,max);

        if(min==0){

            return max;

        }else{

            if(isEven(max)){

               if(isEven(min))

                   return (cv(max>>1,min>>1)<<1);

               else

                   return cv(max>>1,min);

            }else{

               if(isEven(min))

                   return cv(max,min>>1);

               else

                   return cv(min,max-min);

            }

        }

        

    }//函数--判断是否是偶数

public boolean isEven(int i){

if((i&1) == 0)

return true;

return false;

}
 


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值