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

    写一个程序,求两个正整数的最大公约数(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. 《编程之美》

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值