Python 之通过一个天平找出9个小球中唯一重量较轻的小球

哈哈,这个题和 Python 本身没什么关系哈,最主要的解题思路,用什么来实现倒是其次。我们可以看看,从非天才的普通人的视角来讲,这种题需要怎么解。

题目

题目大体是这样:你有一个不带刻度的天平,还有 9 个外观完全一致的小球(其中一个小球的重量比其他 8 个小球要轻)。问最少多少次称重可以找到这个重量不一样的小球?称重方案是什么?

分析

首先,有一个天平,虽然没有刻度(类似于尺规作图,所以有没有刻度不是最重要的),但是我们知道,天平的两边可以放东西,通过天平是否平衡可以知道两边物体的轻重。

还有就是要让称重次数最少,既然要称重次数最少,那最好对九个小球进行分组,然后进行称重。

分组特点

根据分析,我们可以总结出小球分组的特点。分组要尽量考虑已有唯一工具天平的特性,所以要使用天平进行称重的两组小球个数必须一致(尽量均分)。试想,如果称重的时候天平两边的小球个数不一致,称重结果也就没什么参考价值。

分组分球

1组(平凡分组):分成 1 组的话,天平就失效了(天平至少要两组才可以称重),所以用处不大,舍弃。

2组:如果分成 2 组,因为小球个数是奇数,则这 2 组小球的个数必然不可能一样,舍弃。

4组:在保持均分特点的情况下,分成 4 组的分发有 2, 2, 2, 31, 1, 1, 6,对于其中的 2, 2, 21, 1, 1 分组的小球,至少要称重 2 次才能确定较轻的小球一定不在其中,再加上对剩下那组小球的二次分组和称重,称重次数肯定至少是 3 次。

5组:在保持均分特点的情况下,分成 5 组的分发有 2, 2, 2, 2, 11, 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 个
相关推荐
ZhengEnCi3 小时前
P2M-Matplotlib折线图完全指南-从数据可视化到趋势分析的Python绘图利器
python·matlab·数据可视化
ZhengEnCi5 小时前
P2L-Matplotlib饼图完全指南-从数据可视化到图表定制的Python绘图利器
python·matlab
曲幽5 小时前
你的REST接口还在“过度投喂”数据吗?——FastAPI + GraphQL实战避坑指南
python·fastapi·web·graphql·route·cors·rest·strawberry
用户8358086187916 小时前
基于 Self-RAG 与列表级重排序的进阶 RAG 系统设计与实现
python
To_OC16 小时前
LC 207 课程表:刚学图论那会儿,我连这是拓扑排序都没看出来
javascript·算法·leetcode
To_OC16 小时前
LC 208 实现 Trie 前缀树:曾被名字劝退,写完发现是送分题
javascript·算法·leetcode
BadBadBad__AK18 小时前
线段树维护区间 k 次方和
c++·数学·算法·stl
Warson_L1 天前
Python `Annotated` 与 LangGraph Reducer 学习笔记
python
韩师傅1 天前
海天线算法的前世今生
python·计算机视觉
韩师傅1 天前
当你的甲方设备过烂,要如何快速出效果?
python·计算机视觉