Python中数学问题1–lcm、gcd
一、GCD与LCM基础概念
1. 定义
GCD (Greatest Common Divisor):最大公约数,两个或多个整数共有约数中最大的一个。
例:gcd(12,18) = 6
LCM (Least Common Multiple):最小公倍数,两个或多个整数最小的公倍数。
例:lcm(4, 6) = 12
2. 核心公式
关系公式:
扩展公式:gcd(a,b) = gcd(b,a%b) (欧几里得算法)
二、算法实现与优化
1. GCD实现
递归实现:
def gcd(a, b):
return a if b == 0 else gcd(b, a % b)
迭代实现(避免递归栈溢出):
def gcd(a, b):
while b != 0:
a, b = b, a % b
return a
2. LCM实现
直接计算:
def lcm(a, b):
return a * b // gcd(a, b)
多数的LCM(扩展到多个数):
def lcm_list(nums):
res = 1
for num in nums:
res = res * num // gcd(res, num)
return res
3. Python内置函数
math.gcd
(注意:仅支持两个数,且Python 3.5+):
import math
print(math.gcd(12, 18)) # 6
负数处理:math.gcd
返回绝对值结果,如gcd(-12, 18) = 6
。
三、具体应用场景举例(都需要引用相关函数)
1. 分数化简
将分数 a/b
化为最简形式:
a, b = 18, 24
g = gcd(a, b)
print(f"{a//g}/{b//g}") # 3/4
2. 周期性问题
问题示例:两个事件分别每 a
天和 b
天发生一次,求同时发生的周期。
cycle = lcm(a, b)
3. 资源分配问题
问题示例:将长为 L
的木棍切成等长小段,要求每段能同时整除 a
和 b
,求最长小段长度。
max_length = gcd(a, b)
4. 数论问题
裴蜀定理:若 gcd(a, b) = d
,则存在整数 x, y
使得 ax + by = d
。
它又称贝祖定理,可以解释成:对于不全为零的任意整数 a 和 b,记二者的最大公约数为 d,则对于任意整数 x 和 y 都一定满足 ax+by 是 d 的倍数。特别地,一定存在整数 x 和 y 的解,使得 ax+by=gcd(a,b) 成立。
这里提到蓝桥杯中的一道题-裴蜀定理。
问题描述
这是裴蜀定理模板题。
给定正整数 nn、长度为 nn 的正整数数列 a1,a2…an 和一个正整数 b。
判断关于 x1,x2…xn 的方程 a1x1+a2x2+⋯+anxn=b 是否存在整数解。
如果有解则输出
YES
,无解则输出NO
。输入格式
输入第一行,包含一个正整数 n。
输入第二行,包含 nn 个正整数:a1,a2…an。
输入第三行,包含一个正整数 b。
输出格式
输出仅一行,包含一个字符串,如果有解则输出
YES
,无解则输出NO
。
通过前面的讲解我们可以很快给出这道题的答案:
# 导入 math 模块,该模块提供了许多数学相关的函数,这里使用其中的 gcd 函数
import math
# 从 functools 模块中导入 reduce 函数
from functools import reduce
# 获取用户输入的一个整数 n,可代表后续输入的列表 a 的长度
n = int(input())
# 获取用户输入的整数序列,使用 map 函数将输入的每个字符串转换为整数,
# 再将其转换为列表 a
a = list(map(int, input().split()))
# 获取用户输入的另一个整数 b,后续判断是否能被列表 a 中所有元素的最大公约数整除
b = int(input())
# 使用 reduce 函数和 math.gcd 函数计算列表 a 中所有元素的最大公约数
# reduce 函数的作用是将一个二元函数(这里是 math.gcd)累加到序列(这里是列表 a)的元素上,
# 具体过程是:首先将列表 a 的前两个元素作为参数传递给 math.gcd 函数,得到它们的最大公约数,
# 然后将这个最大公约数与列表 a 的第三个元素作为参数再次传递给 math.gcd 函数,
# 以此类推,直到遍历完列表 a 的所有元素,最终得到整个列表元素的最大公约数
# 例如,若 a = [4, 6, 8],则先计算 gcd(4, 6) = 2,再计算 gcd(2, 8) = 2,所以最终结果为 2
d = reduce(math.gcd, a)
# 判断整数 b 是否能被列表 a 中所有元素的最大公约数 d 整除
# 如果能整除,输出 "YES",否则输出 "NO"
print("YES" if b % d == 0 else "NO")
reduce
函数主要的功能体现在累加/递归上,它的原型是 reduce(function, iterable[, initializer])
,它接受一个二元函数(有两个参数的函数)和一个可迭代对象(如列表、元组等),可选地还可以接受一个初始值。它的工作原理是将可迭代对象中的元素依次应用二元函数进行累积计算,最终得到一个单一的结果。在这个代码中,math.gcd
作为二元函数,a
作为可迭代对象,通过 reduce
函数计算出列表 a
中所有元素的最大公约数。
四、真题训练
1. 基础题:求多个数的GCD
题目描述:给定 n
个正整数,计算它们的最大公约数。
输入:3 6 9 12
输出:3
代码实现:
from functools import reduce
import math
nums = list(map(int, input().split()))
result = reduce(math.gcd, nums)
print(result)
2. 中等题:最小公倍数求和
题目描述:给定 n
,求所有满足 1 ≤ i < j ≤ n
的数对的 lcm(i, j)
之和。
输入:n = 3
输出:lcm(1,2)+lcm(1,3)+lcm(2,3)=2+3+6=11
优化思路:预处理每个数的倍数贡献。
代码框架:
n = int(input())
total = 0
for i in range(1, n+1):
for j in range(i+1, n+1):
total += (i * j) // math.gcd(i, j)
print(total)
3. 进阶题:等差数列(2020年省赛真题)
题目描述:给定 n
个整数,找到最长能构成等差数列的子序列长度。等差数列的公差需为所有数对的GCD。
输入:5 [2, 6, 4, 10, 20]
输出:5
(公差为2,序列为2,4,6,10,20)
解题步骤:
-
对所有数排序。
-
计算所有相邻数的差值,求这些差值的GCD。
-
根据公差计算最大长度。
代码片段:
nums = sorted(list(map(int, input().split())))
diffs = [nums[i] - nums[i-1] for i in range(1, len(nums))]
d = reduce(math.gcd, diffs)
max_length = (nums[-1] - nums[0]) // d + 1
print(max_length)
五、高频考点与技巧
1. 复杂度优化
多数的GCD计算:使用reduce
结合math.gcd
。
大数处理:用迭代代替递归防止栈溢出。
2. 特殊边界处理
含0的情况:gcd(0, a) = |a|
,lcm(0, a)
无意义。
负数处理:计算前先取绝对值。
3. 结合质因数分解
质因数法求GCD/LCM:
GCD:取各质因数的最小指数。
LCM:取各质因数的最大指数。
示例:
六、扩展练习题库
-
蓝桥杯省赛题:核桃的数量
-
题目:将一堆核桃平分给三人,要求每人的数量是相同的且最小,求最少需要多少核桃。
-
关键:求三个数的最小公倍数。
-
输入:
3 5 7
-
输出:
105
-
代码:
a, b, c = 3, 5, 7 lcm_ab = a * b // math.gcd(a, b) result = lcm_ab * c // math.gcd(lcm_ab, c) print(result)
-
蓝桥杯国赛题:矩阵求和
-
题目:矩阵中每个元素是行号与列号的GCD,求矩阵所有元素之和。
-
输入:
n=3
-
输出:
和为12
-
优化:预处理欧拉函数减少重复计算。
作者:重生之我要成为代码大佬