GCD与LCM的综合应用
极难8GCD与LCM的综合应用
生活中的例子
我们已经知道,最大公约数和最小公倍数就像一对好朋友,经常一起出现。下面的例子帮你更好地理解它们:
-
分蛋糕:小明有长48厘米、宽36厘米的矩形蛋糕,想切成大小相等的正方形小块,要求不能浪费,且正方形边长是整数厘米。最大正方形边长是多少?能切成多少块?这里边长就是48和36的最大公约数12厘米。块数 = (48/12)*(36/12) = 4×3=12块。
-
铺地砖:小红家卫生间长240厘米、宽180厘米,要铺正方形地砖,地砖边长是整数厘米,不能切割,且要用整块砖铺满。最大能用边长多少厘米的砖?最少需要多少块?
- 边长是240和180的最大公约数60厘米。块数 = (240/60)×(180/60)=4×3=12块。
- 如果改问“用边长60厘米的砖铺需要多少块”,这是除法问题;但如果问“至少用多少块砖(边长最大)”,就是求gcd。
-
钟表问题:甲钟每12分钟响一次铃,乙钟每18分钟响一次铃,丙钟每20分钟响一次铃。它们同时响铃后,多久再次同时响铃?这是求12、18、20的最小公倍数,为180分钟=3小时。
-
分数化简:将分数48/36化为最简分数,需要用分子和分母同时除以最大公约数12,得到4/3。
-
队伍排列:士兵排成每行8人或每行12人都能正好排完,至少有多少人?这是求8和12的最小公倍数24。如果要求人数在100到200之间,则取24的倍数中在这个范围的(即120、144、168、192)。
-
分糖果:有24颗草莓糖和36颗巧克力糖,想把它们混合分给小朋友,每个小朋友分到的两种糖数量相同且刚好分完,最多可以分给几个小朋友?
- 小朋友人数就是24和36的最大公约数12。每个小朋友分到2颗草莓糖和3颗巧克力糖。
-
循环赛日程:两支球队分别每3天和每4天训练一次,今天两队同时训练,下一次同时训练是几天后?这是求3和4的最小公倍数12天后。
数学原理与公式回顾
- gcd(a,b) = d,则a和b可以表示为 a = d * m, b = d * n,且gcd(m,n)=1。
- lcm(a,b) = a * b / gcd(a,b) = d * m * n。
- 对于多个数,gcd和lcm都可以两两逐步计算,但注意gcd满足结合律和交换律。
应用题型1:已知两个数的积与最大公约数,求最小公倍数
例如,两个数的积是216,最大公约数是6,那么最小公倍数 = 积 / gcd = 216/6=36。
应用题型2:已知最大公约数和最小公倍数,求原数
设两个数为a和b,已知gcd(a,b)=g,lcm(a,b)=l。则有 a * b = g * l。且可以设 a = g * x, b = g * y,其中x,y互质。那么 l = g * x * y,所以 x*y = l/g。然后枚举互质的x,y即可求出a,b。
生活例子:小明和小红的年龄都是整数,年龄的最大公约数是6,最小公倍数是36,且小明比小红大。求两人的年龄。
- 设g=6, l=36,则 x*y = 36/6 = 6。互质的(x,y)对可能有(1,6)和(2,3)。若x=1,y=6则a=6,b=36;若x=2,y=3则a=12,b=18。因为小明年龄大,所以小明18岁,小红12岁。
应用题型3:求多个数的最大公约数和最小公倍数
比如求数组[12,18,24]的gcd和lcm。gcd(12,18)=6, gcd(6,24)=6;lcm(12,18)=36, lcm(36,24)=72,所以lcm=72。
注意:计算多个数的lcm时,不能直接abc/gcd(a,b,c),因为公式不直接推广。必须两两逐步计算。
应用题型4:分数运算与化简
在分数加减乘除中,常常需要通分和约分,通分需要求分母的最小公倍数,约分需要求分子分母的最大公约数。
常见错误与注意事项
-
混淆gcd和lcm的用途
- 分物、切割、分组等“分成最大块”的问题通常用gcd;
- 同时发生、周期相遇、找公倍数等“下一次同时”的问题通常用lcm。
- 例如“用边长多少的正方形地砖铺满”是gcd,“至少多少人能排成每行8人或12人”是lcm。
-
计算lcm时溢出
- 公式
lcm = a * b / gcd(a,b)中,如果a和b很大,a*b可能超过int范围。 - 应该先除后乘:
lcm = a / gcd(a,b) * b,这样中间结果更小。
- 公式
-
gcd函数参数为负数
- 分数可能有负分子或负分母,计算gcd时应先取绝对值。代码中已用
abs处理。
- 分数可能有负分子或负分母,计算gcd时应先取绝对值。代码中已用
-
多个数lcm不能直接相乘再除
- 例如求12、18、24的lcm,
12*18*24 / gcd(12,18,24)得到的结果不对(因为lcm不是这个公式)。必须两两计算。
- 例如求12、18、24的lcm,
-
忘记化简分数
- 分数加法后别忘了约分,否则结果不是最简形式。
C++完整代码实现
我们编写一个综合程序,包含以下功能:
- 求两个数的gcd和lcm
- 求数组的gcd和lcm
- 分数化简(用结构体表示分数)
#include <iostream>
#include <vector>
#include <cstdlib> // 为了abs函数
using namespace std;
// 最大公约数(欧几里得算法)
int gcd(int a, int b) {
while (b) {
int t = a % b; // 余数
a = b; // 更新a为原b
b = t; // 更新b为余数
}
return a;
}
// 最小公倍数:先除后乘防止溢出
int lcm(int a, int b) {
return a / gcd(a, b) * b;
}
// 数组的最大公约数
int gcd_array(const vector<int> &arr) {
int res = arr[0]; // 初始化为第一个元素
for (int i = 1; i < arr.size(); i++) {
res = gcd(res, arr[i]); // 依次两两计算
}
return res;
}
// 数组的最小公倍数
int lcm_array(const vector<int> &arr) {
int res = arr[0]; // 初始化为第一个元素
for (int i = 1; i < arr.size(); i++) {
res = lcm(res, arr[i]); // 依次两两计算
}
return res;
}
// 分数结构体
struct Fraction {
int num; // 分子
int den; // 分母
};
// 分数化简
Fraction reduce_fraction(Fraction f) {
int g = gcd(abs(f.num), abs(f.den)); // 取绝对值求gcd
f.num /= g; // 分子除以gcd
f.den /= g; // 分母除以gcd
// 通常分母为正
if (f.den < 0) {
f.num = -f.num;
f.den = -f.den;
}
return f;
}
// 分数加法
Fraction add_fraction(Fraction a, Fraction b) {
// 通分:分母为lcm
int common_den = lcm(a.den, b.den); // 公分母
int new_num = a.num * (common_den / a.den) + b.num * (common_den / b.den);
Fraction result = {new_num, common_den};
return reduce_fraction(result); // 最后约分
}
int main() {
// 1. 两个数的gcd和lcm
int x = 48; // 第一个数
int y = 36; // 第二个数
cout << "gcd(" << x << "," << y << ") = " << gcd(x, y) << endl;
cout << "lcm(" << x << "," << y << ") = " << lcm(x, y) << endl;
// 2. 数组的gcd和lcm
vector<int> numbers = {12, 18, 24}; // 三个数
cout << "数组[12,18,24]的gcd = " << gcd_array(numbers) << endl;
cout << "数组[12,18,24]的lcm = " << lcm_array(numbers) << endl;
// 3. 分数化简
Fraction frac = {48, 36}; // 待化简的分数
Fraction reduced = reduce_fraction(frac);
cout << "分数48/36化简为 " << reduced.num << "/" << reduced.den << endl;
// 4. 分数加法
Fraction a = {1, 6}; // 第一个分数 1/6
Fraction b = {1, 8}; // 第二个分数 1/8
Fraction sum = add_fraction(a, b);
cout << "1/6 + 1/8 = " << sum.num << "/" << sum.den << endl; // 7/24
return 0;
}
注意:在计算gcd时,我们使用了abs是因为分数可能有负数,但gcd函数通常用于非负整数,所以先取绝对值。
Python完整代码实现
Python同样实现这些功能,更加简洁。
import math
def gcd(a, b):
"""欧几里得算法求最大公约数"""
while b:
a, b = b, a % b
return a
def lcm(a, b):
"""最小公倍数(先除后乘)"""
return a // gcd(a, b) * b
def gcd_array(arr):
"""数组的gcd"""
res = arr[0] # 第一个数
for num in arr[1:]:
res = gcd(res, num) # 两两计算
return res
def lcm_array(arr):
"""数组的lcm"""
res = arr[0] # 第一个数
for num in arr[1:]:
res = lcm(res, num) # 两两计算
return res
def reduce_fraction(num, den):
"""化简分数,返回(分子,分母)"""
g = gcd(abs(num), abs(den)) # 取绝对值求gcd
num //= g # 分子除以gcd
den //= g # 分母除以gcd
if den < 0: # 分母调为正
num = -num
den = -den
return num, den
def add_fraction(a_num, a_den, b_num, b_den):
"""分数加法,a_num/a_den + b_num/b_den"""
common_den = lcm(a_den, b_den) # 公分母
new_num = a_num * (common_den // a_den) + b_num * (common_den // b_den)
return reduce_fraction(new_num, common_den)
# 测试
x, y = 48, 36 # 两个示例数
print(f"gcd({x},{y}) = {gcd(x, y)}")
print(f"lcm({x},{y}) = {lcm(x, y)}")
numbers = [12, 18, 24] # 数组
print(f"数组{numbers}的gcd = {gcd_array(numbers)}")
print(f"数组{numbers}的lcm = {lcm_array(numbers)}")
num, den = reduce_fraction(48, 36)
print(f"分数48/36化简为 {num}/{den}")
s_num, s_den = add_fraction(1, 6, 1, 8)
print(f"1/6 + 1/8 = {s_num}/{s_den}")
Python内置的math.gcd可以直接用,但这里手动实现以便理解。
总结要点
- 最大公约数和最小公倍数是解决很多实际问题的工具,如分物、铺地砖、周期事件、分数化简等。
- 核心公式:lcm(a,b) = a * b / gcd(a,b)。编程时应先除后乘防溢出。
- 多个数的gcd可以两两连续计算,lcm同理。
- 分数化简需要同时用到gcd;分数通分需要用到lcm。
- 在解题时,注意分析问题到底求gcd还是lcm,有时候需要两者结合。比如在已知两个数的积和gcd时,可以求出lcm;或者利用互质关系解未知数。
通过本文学习,你应该能够灵活运用最大公约数和最小公倍数解决实际编程问题,并为后续更复杂的数论算法打下坚实基础。
相关知识点指引
学完gcd和lcm后,可以继续探索以下内容:
- 扩展欧几里得算法:不仅能求gcd,还能解出方程ax+by=gcd(a,b)的一组整数解,用于模逆元、同余方程。
- 质因数分解:通过分解质因数可以直观计算gcd和lcm(取公共质因子的最小次数、最大次数)。
- 同余与中国剩余定理:解决“物品总数除以某数余几”的问题,其中会用到lcm求公共周期。
- 分数高精度运算:当分子分母非常大时,需要结合大整数和gcd来化简。
- 编程竞赛常见题型:如“两个数的gcd和lcm互推”、“区间内有多少个数与某数互质”(利用gcd性质)。
希望这些知识能成为你数论入门的第一块基石!
例题精讲
小明家客厅地面是长方形,长540厘米,宽360厘米。现在要铺上正方形地砖,要求地砖边长是整数厘米,且不能切割地砖,问最大边长是多少?
甲每3天去一次图书馆,乙每5天去一次,丙每7天去一次。他们某天在图书馆相遇,请问下一次相遇至少是多少天后?
对于任意两个正整数a和b,有GCD(a,b) × LCM(a,b) = a × b。
以下函数使用辗转相除法求两个正整数的最大公约数和最小公倍数。请补充代码使函数完整。
def gcd_lcm(a, b):
orig_a, orig_b = a, b
while b:
a, b = b, a % b
gcd = a
lcm = ___ # 计算最小公倍数
return gcd, lcm以下函数用于将分数化简为最简形式,即分子分母除以它们的最大公约数。请补充代码。
def gcd(x, y):
while y:
x, y = y, x % y
return x
def simplify(numerator, denominator):
g = ___ # 求最大公约数
return numerator // g, denominator // g