求助:如何运用GCD、LCM公式高效解决算法问题?
嘿,我明白你为啥头疼这个围圈发巧克力的问题了——模拟法虽然直观但效率低,公式又摸不着头脑对吧?咱一步步拆解,先把问题本质搞清楚,再把公式的来龙去脉讲透,保证你下次遇到直接套公式就行!
问题回顾
n个孩子围成一圈,每隔k个孩子发放巧克力,直到选中已获得巧克力的孩子为止。已知n和k,求未获得巧克力的孩子数量nr。示例:n=12,k=9时,nr=8。
解法1:直观模拟法(适合小n场景)
如果n的数值不大,用模拟法完全没问题,思路直白到一眼就能懂:
- 用布尔数组标记每个孩子是否已经拿到巧克力
- 从第一个孩子开始,每次往后数k个(因为是围成一圈,所以要对n取模来循环)
- 碰到没标记的孩子就标记为已获奖,碰到已经标记的就停止循环
- 最后统计数组里未标记的数量就是nr
举个Python代码的例子:
def calculate_nr_simulation(n, k): rewarded = [False] * n current_idx = 0 while True: if rewarded[current_idx]: break rewarded[current_idx] = True # 移动k步,取模n实现循环 current_idx = (current_idx + k) % n return n - sum(rewarded) # 测试示例 print(calculate_nr_simulation(12, 9)) # 输出8,符合预期
不过这种方法的短板也很明显:当n特别大(比如几万、几十万)时,循环次数会爆炸式增长,效率直接拉胯,这时候就需要高效的公式法出场了。
解法2:数学公式法(高效解决大n场景)
这个问题的本质其实和数论里的循环节有关:当你每隔k个孩子选一个时,你会形成一个固定的循环,这个循环里包含的孩子数量是有规律的。
核心公式推导
循环中能拿到巧克力的孩子数量 = n / gcd(n, k),其中gcd(n,k)是n和k的最大公约数。
那未获得巧克力的孩子数量nr就等于总人数减去循环内的人数:nr = n - n / gcd(n, k)
也可以简化成:nr = n * (gcd(n,k) - 1) / gcd(n,k)
为啥这个公式成立?
拿示例n=12,k=9验证:
gcd(12,9)=3,所以循环里的孩子数量是12/3=4,未获得的就是12-4=8,完全符合示例结果。
原理是:当你每隔k步移动一次时,在模n的规则下,你能走到的所有位置都是n和k最大公约数的倍数。比如gcd(12,9)=3,能走到的位置就是0、3、6、9(假设从0开始计数),正好4个,剩下的8个位置永远走不到,自然拿不到巧克力。
公式法代码实现
计算最大公约数用欧几里得算法就行,大部分编程语言都自带相关函数,比如Python的math.gcd:
import math def calculate_nr_formula(n, k): g = math.gcd(n, k) return n - n // g # 测试示例 print(calculate_nr_formula(12, 9)) # 输出8,正确
这个方法的计算速度是O(log(min(n,k))),不管n多大都能秒出结果,比模拟法高效太多!
总结
- 小n场景用模拟法,直观好理解,不用费脑子记公式
- 大n场景或者追求效率直接用公式法,核心就是求n和k的最大公约数,套公式就行
内容的提问来源于stack exchange,提问作者Snoky

