最大公约数问题的优化解法
2016-07-23 15:20
489 查看
写一个程序,求两个正整数的最大公约数(Greatest Common Divisor,GCD)。如果两个整数都很大,有什么简单的算法吗?
(问题及思路来自于《编程之美》2.7节)
当a mod b=0 时gcd(a,b)=b,否则gcd(a,b) = gcd(b,a mod b)
分析:如果一个数能够同时整除x和y,则必能同时整除x-y和y;而能够同时整除x-y和y的数也必能同时整除x和y,即x和y的公约数与x-y和y的公约数是相同的,其最大公约数也是相同的,即f(x,y)=f(x-y,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)))
/**
* 目的:减法的迭代次数太多,需加速
* 公约数特点:
* 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. 《编程之美》
(问题及思路来自于《编程之美》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. 《编程之美》
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树