最大公约数,也称最大公因数、最大公因子,指两个或多个整数共有约数中最大的一个;
a,b的最大公约数记为(a,b),同样的,a,b,c的最大公约数记为(a,b,c),多个整数的最大公约数也有同样的记号;
求最大公约数有多种方法,常见的有质因数分解法、短除法、欧几里得算法(又称辗转相除法)、更相减损法。
质因数分解法&短除法较依赖计算者,灵活性也很大,步骤不固定,本文作者仅使用Java代码实现欧几里得算法(又称辗转相除法)&更相减损法,具体如下:
一、欧几里得算法(又称辗转相除法)
package algorithm.maxCommonDivisor;
/**
* 求两个数的最大公约数
* 欧几里得算法(又称辗转相除法)
*
* @author XUQIANG_DUAN
* @date 2019/7/29
* @time 9:21
*/
public class EuclideanAlgorithm {
/**
* 辗转相除法
*
* @param a
* @param b
* @return 最大公约数
*/
private static int euclidMethod(int a, int b) {
/**
* 大值&小值
*/
int maxI = a > b ? a : b;
int minI = a > b ? b : a;
/**
* 余数
*/
int remainder = maxI % minI;
// System.out.println("余数为:" + remainder);
/**
* 余数为零,小值为最大公约数
* 余数为一,一为最大公约数
* 大值&小值的最大公约数与小值与余数的最大公约数相同
*/
if (remainder == 0) {
return minI;
} else if (remainder == 1) {
return remainder;
} else {
return euclidMethod(minI, remainder);
}
}
/**
* 运行时间&结果记录
* currentTimeMillis 为毫秒时间戳
* nanoTime 为纳秒时间戳
* 1 秒 = 10^3 毫秒
* 1 秒 = 10^9 纳秒
*
* @param a
* @param b
*/
private static void timeConsume(int a, int b) {
// long start = System.currentTimeMillis();
long nstart = System.nanoTime();
System.out.println("(" + a + ", " + b + ") | result = " + euclidMethod(a, b));
// long end = System.currentTimeMillis();
long nend = System.nanoTime();
// System.out.println("共耗时(单位:ms):" + (end - start) + " | 结束点:" + end + " | 开始点:" + start);
System.out.println("共耗时(单位:ns):" + (nend - nstart) + " | 结束点:" + nend + " | 开始点:" + nstart);
}
public static void main(String[] args) {
timeConsume(55, 55);
timeConsume(66, 55);
timeConsume(10000, 10001);
timeConsume(10000, 10003);
timeConsume(1350, 1266);
timeConsume(1000000, 1000003);
}
}
代码运行结果如下:
...(55, 55) | result = 55
共耗时(单位:ns):438614 | 结束点:89819135569674 | 开始点:89819135131060
(66, 55) | result = 11
共耗时(单位:ns):21049 | 结束点:89819135643629 | 开始点:89819135622580
(10000, 10001) | result = 1
共耗时(单位:ns):17636 | 结束点:89819135685727 | 开始点:89819135668091
(10000, 10003) | result = 1
共耗时(单位:ns):23325 | 结束点:89819135732376 | 开始点:89819135709051
(1350, 1266) | result = 6
共耗时(单位:ns):31858 | 结束点:89819135789265 | 开始点:89819135757407
(1000000, 1000003) | result = 1
共耗时(单位:ns):27876 | 结束点:89819135849567 | 开始点:89819135821691Process finished with exit code 0
二、更相减损法
package algorithm.maxCommonDivisor;
/**
* 求两个数的最大公约数
* 更相减损法
*
* @author XUQIANG_DUAN
* @date 2019/7/29
* @time 10:24
*/
public class Subtraction {
private static int subMethon(int a, int b) {
int result = 1;
/**
* 大值&小值
*/
int maxI = a > b ? a : b;
int minI = a > b ? b : a;
/**
* 大值&小值都为偶数,先简约
*/
while (maxI % 2 == 0 && minI % 2 == 0) {
result *= 2;
maxI /= 2;
minI /= 2;
}
/**
* 差值
*/
int sub = maxI - minI;
// System.out.println("差值为:" + sub + " | 大值为:" + maxI + " | 小值为:" + minI + " | 结果为:" + result);
if (sub == 0) {
result *= minI;
} else if (sub == 1) {
result *= 1;
} else {
result *= subMethon(minI, sub);
}
return result;
}
/**
* 运行时间&结果记录
* currentTimeMillis 为毫秒时间戳
* nanoTime 为纳秒时间戳
* 1 秒 = 10^3 毫秒
* 1 秒 = 10^9 纳秒
*
* @param a
* @param b
*/
private static void timeConsume(int a, int b) {
// long start = System.currentTimeMillis();
long nstart = System.nanoTime();
System.out.println("(" + a + ", " + b + ") | result = " + subMethon(a, b));
// long end = System.currentTimeMillis();
long nend = System.nanoTime();
// System.out.println("共耗时(单位:ms):" + (end - start) + " | 结束点:" + end + " | 开始点:" + start);
System.out.println("共耗时(单位:ns):" + (nend - nstart) + " | 结束点:" + nend + " | 开始点:" + nstart);
}
public static void main(String[] args) {
timeConsume(5,55);
timeConsume(66,55);
timeConsume(10000,10001);
timeConsume(10000,10003);
timeConsume(1350, 1266);
timeConsume(1000000, 1000003);
}
}
代码运行如下:
...
(5, 55) | result = 5
共耗时(单位:ns):445440 | 结束点:90158027966585 | 开始点:90158027521145
(66, 55) | result = 11
共耗时(单位:ns):43235 | 结束点:90158028074105 | 开始点:90158028030870
(10000, 10001) | result = 1
共耗时(单位:ns):31289 | 结束点:90158028144648 | 开始点:90158028113359
(10000, 10003) | result = 1
共耗时(单位:ns):753209 | 结束点:90158028942799 | 开始点:90158028189590
(1350, 1266) | result = 6
共耗时(单位:ns):39253 | 结束点:90158029022443 | 开始点:90158028983190
Exception in thread "main" java.lang.StackOverflowError 【栈内存溢出异常】
at algorithm.maxCommonDivisor.Subtraction.subMethon(Subtraction.java:40)...
Disconnected from the target VM, address: '127.0.0.1:54677', transport: 'socket'
Process finished with exit code 1
结束语
更相减损法较辗转相除法时间&空间复杂度都较高,也极易发生栈内存溢出异常,原因也不难理解,乘除为加减的高级运算同时效率也倍增。