哈哈,这个题和 Python 本身没什么关系哈,最主要的解题思路,用什么来实现倒是其次。我们可以看看,从非天才的普通人的视角来讲,这种题需要怎么解。
题目
题目大体是这样:你有一个不带刻度的天平,还有 9 个外观完全一致的小球(其中一个小球的重量比其他 8 个小球要轻)。问最少多少次称重可以找到这个重量不一样的小球?称重方案是什么?
分析
首先,有一个天平,虽然没有刻度(类似于尺规作图,所以有没有刻度不是最重要的),但是我们知道,天平的两边可以放东西,通过天平是否平衡可以知道两边物体的轻重。
还有就是要让称重次数最少,既然要称重次数最少,那最好对九个小球进行分组,然后进行称重。
分组特点
根据分析,我们可以总结出小球分组的特点。分组要尽量考虑已有唯一工具天平的特性,所以要使用天平进行称重的两组小球个数必须一致(尽量均分)。试想,如果称重的时候天平两边的小球个数不一致,称重结果也就没什么参考价值。
分组分球
1组(平凡分组):分成 1 组的话,天平就失效了(天平至少要两组才可以称重),所以用处不大,舍弃。
2组:如果分成 2 组,因为小球个数是奇数,则这 2 组小球的个数必然不可能一样,舍弃。
4组:在保持均分特点的情况下,分成 4 组的分发有 [2, 2, 2, 3] 和 [1, 1, 1, 6],对于其中的 [2, 2, 2] 和 [1, 1, 1] 分组的小球,至少要称重 2 次才能确定较轻的小球一定不在其中,再加上对剩下那组小球的二次分组和称重,称重次数肯定至少是 3 次。
5组:在保持均分特点的情况下,分成 5 组的分发有 [2, 2, 2, 2, 1] 和 [1, 1, 1, 1, 5],因为天平一次只能对其中的两组小球进行称重,包括涉及到二次分组的极限情况下,也至少要称重 3 次才能找到较轻的小球。
6组及以上:对于 6 组及以上的情况,因为分组的数目太多,也至少要对分组的小球称重 3 次及以上才可以一定找到较轻的小球。
3组:如果小球分成 3 组,按照均分的原则,分法就是 [3, 3, 3]。我们对其中两组小球进行称重。
第一次称重: 如果天平保持平衡,则说明较轻的小球在剩下那组;如果天平不平衡,则称重的两组小球,其中较轻的那组小球必然在天平中翘起。但是不管哪种情况,我们都可以将较轻的小球的范围限定在3组小球中的其中一组。
第二次称重: 第一次称重将较轻的小球限定在了其中一组中,也即将范围限定在3个小球以内。我们对较轻的那组小球,按照均分原则进行第二次分组。只能是分成 [1, 1, 1]。再对分组后的 3 个小球进行第二次称重。同上,如果天平保持平衡,则说明较轻的小球就是剩下那个;如果天平不平衡,则称重的两个小球,其中较轻的那个小球必然在天平中翘起。但是不管哪种情况,我们都可以在第二次称重将较轻的小球找到。
总的看来,分成三组的这种方式,可以最多称重两次,即可将较轻的那个小球找到。
实现
查找实现
前一段时间刚好学了下 Python 的新语法 match case,这次正好学以致用,熟悉一下。
python
import random
# 生成初始的 9 个小球
def gen_init_balls():
init_balls = [10 for i in range(8)] # 初始的8个质量相同的球(假设重量为 10)
light_ball = 8 # 较轻的那个球(假设重量为 8)
random_index = random.randint(0, 9)
init_balls.insert(random_index, light_ball) # 随机放到 8 个球中
return tuple(init_balls)
# 对分组的两组小球进行称重
def weigh_balls(group1, group2):
print("开始对分组小球进程称重")
sum1 = sum(group1)
sum2 = sum(group2)
diff = sum1 - sum2
match diff:
case _ if diff > 0:
return 1
case _ if diff == 0:
return 0
case _ if diff < 0:
return -1
if __name__ == '__main__':
# init_balls = (10, 10, 10, 8, 10, 10, 10, 10, 10)
init_balls = gen_init_balls()
print(init_balls)
# 小球第一次分组
group1 = init_balls[0:3]
group2 = init_balls[3:6]
group3 = init_balls[6:9]
# 对分组后的其中两组进行称重
weigh_result = weigh_balls(group1, group2)
match weigh_result:
# 如果 group1 和 group2 重量相等
case 0:
# 对 group3 进行二次分组
index = 2
group4 = group3[0:1]
group5 = group3[1:2]
group6 = group3[2:3]
# 如果 group1 更重
case 1:
# 对 group2 进行二次分组
index = 1
group4 = group2[0:1]
group5 = group2[1:2]
group6 = group2[2:3]
# 如果 group2 更重
case _:
# 对 group1 进行二次分组
index = 0
group4 = group1[0:1]
group5 = group1[1:2]
group6 = group1[2:3]
# 对二次分组后的其中两组进行称重
weigh_result = weigh_balls(group4, group5)
match weigh_result:
case 0:
print(f"最轻的小球是第 {3 * index + 3} 个")
case 1:
print(f"最轻的小球是第 {3 * index + 2} 个")
case _:
print(f"最轻的小球是第 {3 * index + 1} 个")
运行示例
因每次生成的随机数不一样,所以每次的运行结果也会不一样。可以看出,查找总共对小球进行了 固定 2 次的称重。
python
(10, 8, 10, 10, 10, 10, 10, 10, 10)
开始对分组小球进程称重
开始对分组小球进程称重
最轻的小球是第 2 个