探索最大公约数与最小公倍数的求解之道
在数学的领域中,最大公约数(Greatest Common Divisor,简称GCD)和最小公倍数(Least Common Multiple,简称LCM)是两个重要的概念,它们在众多算法和数学问题里都有着关键的应用。今天,咱们就深入探讨一下这两个概念以及求解它们的多种方法。
一、概念解析
(一)最大公约数
最大公约数,也被称为greatest common divisor(缩写为gcd)或者highest common factor(缩写为hcf)。当自然数a能被自然数b整除时,a就是b的倍数,b则是a的约数。几个自然数共有的约数,就是它们的公约数,其中最大的那个就是最大公约数。就好比在2、4、6这几个数中,2能同时整除它们,所以2就是2、4、6的最大公约数 。
(二)最小公倍数
最小公倍数的英文是Least Common Multiple,常缩写为L.C.M.。它和最大公约数之间存在一个有趣的关系:最小公倍数等于两数的乘积除以它们的最大公约数 。例如,12和18,先算出它们的最大公约数是6,那么最小公倍数就是
12
×
18
÷
6
=
36
12×18÷6 = 36
12×18÷6=36 。
以下是对公式(a\times b = \text{GCD}(a, b)\times\text{LCM}(a, b))(其中(\text{GCD})表示最大公约数,(\text{LCM})表示最小公倍数)的推导过程:
设(a = p_{1}{a_{1}}p_{2}{a_{2}}\cdots p_{n}^{a_{n}}),(b = p_{1}{b_{1}}p_{2}{b_{2}}\cdots p_{n}^{b_{n}}),其中(p_{1},p_{2},\cdots,p_{n})是不同的质数,(a_{i})和(b_{i})是非负整数。
计算两者之间的关系a∗b=GCD∗LCM
-
计算最大公约数
(\text{GCD}(a,b)=p_{1}{\min(a_{1},b_{1})}p_{2}{\min(a_{2},b_{2})}\cdots p_{n}^{\min(a_{n},b_{n})}) -
计算最小公倍数
(\text{LCM}(a,b)=p_{1}{\max(a_{1},b_{1})}p_{2}{\max(a_{2},b_{2})}\cdots p_{n}^{\max(a_{n},b_{n})}) -
计算(\text{GCD}(a, b)\times\text{LCM}(a, b))
对于每一个质数(p_{i}),在(\text{GCD}(a, b)\times\text{LCM}(a, b))中的指数为(\min(a_{i},b_{i})+\max(a_{i},b_{i}))。
通过分析可得(\min(a_{i},b_{i})+\max(a_{i},b_{i})=a_{i}+b_{i})。
所以(\text{GCD}(a, b)\times\text{LCM}(a, b)=p_{1}{a_{1}+b_{1}}p_{2}{a_{2}+b_{2}}\cdots p_{n}^{a_{n}+b_{n}}=a\times b)。
综上,(a\times b=\text{GCD}(a, b)\times\text{LCM}(a, b))得证。
二、求解算法
(一)欧几里德算法(辗转相除法)
- 原理:欧几里德算法也叫辗转相除法,专门用来计算两个整数a、b的最大公约数。它的原理基于一个重要定理: g c d ( a , b ) = g c d ( b , a m o d b ) gcd(a, b) = gcd(b, a \mod b) gcd(a,b)=gcd(b,amodb) 。假设 a = k b + r a = kb + r a=kb+r(这里 r = a m o d b r = a \mod b r=amodb ),要是d是a和b的公约数,也就是 d ∣ a d|a d∣a且 d ∣ b d|b d∣b,由于 r = a − k b r = a - kb r=a−kb,所以 d ∣ r d|r d∣r,这就说明d也是 b b b和 a m o d b a \mod b amodb的公约数。反过来,要是d是 b b b和 a m o d b a \mod b amodb的公约数,同样能推出d是a和b的公约数。因此, ( a , b ) (a, b) (a,b)和 ( b , a m o d b ) (b, a \mod b) (b,amodb)的公约数是一样的,最大公约数自然也相等。
- 代码实现
- 递归版本(以Java为例):
public long gcd(long x, long y) {
if (y == 0) {
return x;
} else {
return gcd(y, x % y);
}
}
- **非递归版本(以Java为例)**:
public long gcd(int x, int y) {
int temp;
while (y != 0) {
temp = x % y;
x = y;
y = temp;
}
return x;
}
- 时间复杂度:这个算法的时间复杂度是 O ( log n ) O(\log n) O(logn) ,这里的n是a和b中较大数的长度。这意味着数据规模增大时,运算次数增长得比较慢,效率较高。
- 求最小公倍数:借助“最小公倍数 = 两数的乘积 / 最大公约数”这个公式,在算出最大公约数后,就能轻松得到最小公倍数。还是以Java为例:
public long lcm(int x, int y){
long gcd = gcd(x, y);
return (x * y) / gcd;
}
(二)Stein算法
- 原理:Stein算法同样用于计算两个数的最大公约数,它是J.Stein在1961年提出来的。和欧几里德算法不同,Stein算法只用到整数的移位和加减法,这在处理大整数时优势明显。它的正确性基于几个结论: g c d ( a , a ) = a gcd(a, a) = a gcd(a,a)=a ,也就是一个数和自身的最大公约数就是它自己; g c d ( k a , k b ) = k ∗ g c d ( a , b ) gcd(ka, kb) = k * gcd(a, b) gcd(ka,kb)=k∗gcd(a,b) ,最大公约数运算和倍乘运算可以交换,特别是当 k = 2 k = 2 k=2时,两个偶数的最大公约数能被2整除。
- 代码实现(以Python为例):
def gcd_Stein(a, b):
if a < b:
a, b = b, a
if (0 == b):
return a
if a % 2 == 0 and b % 2 == 0:
return 2 * gcd_Stein(a / 2, b / 2)
if a % 2 == 0:
return gcd_Stein(a / 2, b)
if b % 2 == 0:
return gcd_Stein(a, b / 2)
return gcd_Stein((a + b) / 2, (a - b) / 2)
- 优势:在处理超过64位的大整数时,欧几里德算法计算大整数的模会变得复杂,而Stein算法避免了除法和取模运算,减少了CPU的消耗,更适合现代密码算法这类需要处理大素数的场景。
(三)按定义求解
- 原理:从 i = 1 i = 1 i=1开始,每次加1,一直到x和y中较小的那个值,然后找出能同时整除x和y的i值,这些i值的乘积就是最大公约数。
- 代码实现(以Java为例):
- 最大公约数:
在这里插入代码片
public long gcd(int x, int y){
if(x > y){
int temp = x;
x = y;
y = temp;
}
int result = 1;
for(int i = 2; i <= x; i++){
if( (x % i == 0) && (y % i == 0) ){
result *= i;
x = x / i;
y = y / i;
}
}
return result;
}
- **最小公倍数**:最小公倍数等于最大公约数乘以最后的x和y值 。
public long lcm(int x, int y){
if(x > y){
int temp = x;
x = y;
y = temp;
}
int result = 1;
for(int i = 2; i <= x; i++){
if( (x % i == 0) && (y % i == 0) ){
result *= i;
x = x / i;
y = y / i;
}
}
return result * (x * y);
}
- 缺点:这种方法的时间复杂度比较高,在处理较大的数时效率较低,所以实际应用中不太常用。
三、总结与思考
在求解最大公约数和最小公倍数时,不同的算法各有优劣。欧几里德算法简单高效,时间复杂度低,适用于一般场景;Stein算法在处理大整数时优势突出;按定义求解虽然直观,但效率欠佳 。在实际编程中,我们要根据具体的需求和数据规模来选择合适的算法。希望通过今天的分享,大家对最大公约数和最小公倍数的求解有更深入的理解,在编程和数学学习中能够灵活运用这些知识。
在原博客内容基础上,为各类算法补充C++ 与Python 版本代码,增强代码语言多样性,方便不同语言背景读者理解运用,具体代码如下:
欧几里德算法(辗转相除法)
- 原理:依据定理 g c d ( a , b ) = g c d ( b , a m o d b ) gcd(a, b) = gcd(b, a \mod b) gcd(a,b)=gcd(b,amodb) 。若 a = k b + r a = kb + r a=kb+r( r = a m o d b r = a \mod b r=amodb ), d d d为 a a a和 b b b公约数时, d d d也是 b b b和 a m o d b a \mod b amodb公约数,反之亦然,所以两者最大公约数相等。
- 代码实现
- C++ 递归版本:
#include <iostream>
using namespace std;
long long gcd(long long x, long long y) {
if (y == 0) {
return x;
} else {
return gcd(y, x % y);
}
}
- **C++ 非递归版本**:
#include <iostream>
using namespace std;
long long gcd(int x, int y) {
int temp;
while (y != 0) {
temp = x % y;
x = y;
y = temp;
}
return x;
}
- **Python 递归版本**:
def gcd(x, y):
if y == 0:
return x
else:
return gcd(y, x % y)
- **Python 非递归版本**:
def gcd(x, y):
while y:
x, y = y, x % y
return x
- 时间复杂度: O ( log n ) O(\log n) O(logn) , n n n是 a a a和 b b b中较大数的长度,效率较高。
- 求最小公倍数
- C++ 版本:
#include <iostream>
using namespace std;
long long lcm(int x, int y) {
long long gcd_result = gcd(x, y);
return (x * y) / gcd_result;
}
- **Python 版本**:
def lcm(x, y):
gcd_result = gcd(x, y)
return (x * y) // gcd_result
Stein算法
- 原理:基于 g c d ( a , a ) = a gcd(a, a) = a gcd(a,a)=a 以及 g c d ( k a , k b ) = k ∗ g c d ( a , b ) gcd(ka, kb) = k * gcd(a, b) gcd(ka,kb)=k∗gcd(a,b)( k = 2 k = 2 k=2时,两个偶数的最大公约数能被2整除)等结论,通过整数移位和加减法计算最大公约数。
- 代码实现
- C++ 版本:
#include <iostream>
using namespace std;
long long gcd_Stein(long long a, long long b) {
if (a < b) {
swap(a, b);
}
if (b == 0) {
return a;
}
if ((a % 2 == 0) && (b % 2 == 0)) {
return 2 * gcd_Stein(a / 2, b / 2);
} else if (a % 2 == 0) {
return gcd_Stein(a / 2, b);
} else if (b % 2 == 0) {
return gcd_Stein(a, b / 2);
} else {
return gcd_Stein((a + b) / 2, (a - b) / 2);
}
}
- **Python 版本**:
def gcd_Stein(a, b):
if a < b:
a, b = b, a
if b == 0:
return a
if a % 2 == 0 and b % 2 == 0:
return 2 * gcd_Stein(a / 2, b / 2)
if a % 2 == 0:
return gcd_Stein(a / 2, b)
if b % 2 == 0:
return gcd_Stein(a, b / 2)
return gcd_Stein((a + b) / 2, (a - b) / 2)
- 优势:处理大整数时,避免复杂除法和取模运算,减少CPU消耗,适用于现代密码算法等场景。
按定义求解
- 原理:从 i = 1 i = 1 i=1递增到 x x x和 y y y中较小值,找出能同时整除 x x x和 y y y的 i i i值,其乘积为最大公约数。
- 代码实现
- C++ 最大公约数版本:
#include <iostream>
using namespace std;
long long gcd(int x, int y) {
if (x > y) {
swap(x, y);
}
long long result = 1;
for (int i = 2; i <= x; i++) {
if ((x % i == 0) && (y % i == 0)) {
result *= i;
x /= i;
y /= i;
i--;
}
}
return result;
}
- **C++ 最小公倍数版本**:
#include <iostream>
using namespace std;
long long lcm(int x, int y) {
if (x > y) {
swap(x, y);
}
long long result = 1;
for (int i = 2; i <= x; i++) {
if ((x % i == 0) && (y % i == 0)) {
result *= i;
x /= i;
y /= i;
i--;
}
}
return result * x * y;
}
- **Python 最大公约数版本**:
def gcd(x, y):
if x > y:
x, y = y, x
result = 1
i = 2
while i <= x:
if x % i == 0 and y % i == 0:
result *= i
x //= i
y //= i
i -= 1
i += 1
return result
- **Python 最小公倍数版本**:
def lcm(x, y):
if x > y:
x, y = y, x
result = 1
i = 2
while i <= x:
if x % i == 0 and y % i == 0:
result *= i
x //= i
y //= i
i -= 1
i += 1
return result * x * y
- 缺点:时间复杂度高,处理较大数时效率低,实际应用较少。