目标 73,二分策略六次猜中:50, 75, 62, 68, 71, 73。
一千局下来随机猜平均 7.58 次、最坏 17 次;
二分平均 5.83 次、最坏 7 次——正好等于理论上界。
上一节练排版,这一节用循环加条件写一个小游戏。 它看着是玩,实际上讲的是「一次提问能排除多少可能」。
一、游戏规则和循环骨架
规则:心里想一个 1 到 100 的整数,对方每次猜一个,你只回答「大了」「小了」或「中了」。
代码骨架是一个 while True:猜、比、缩小范围、猜中就 break。
范围用两个变量 lo 和 hi 表示,猜小了就 lo = g + 1,猜大了就 hi = g - 1。
+1 和 -1 很关键:猜过的那个数已经排除了,再留在范围里就是浪费。
漏掉这个 +1,最坏情况会多猜一次。
Demo 跑了一局并打印全部过程:目标 73,猜的是 50, 75, 62, 68, 71, 73,六次命中。
二、二分:每次砍一半
二分策略就是每次猜范围的中间值:(lo + hi) // 2。
注意这里必须用整除,用 / 会得到浮点,没法当下标也没法比较。
每猜一次,候选范围至少减半。100 个数最多需要 log₂100 上取整,也就是 7 次。
Demo 一千局的最坏结果正好是 7 次——理论和实测对上了。
平均 5.83 次,比 7 小,因为有的数运气好一次就中,大多数落在 6 到 7 次。
三、随机猜:也在缩范围,但会白猜
随机策略每次在 [lo, hi] 里随便挑一个。它也在缩范围——因为猜完还是按大小调整边界。
但它可能连着猜同一片区域,等于白问。所以平均 7.58 次、最坏 17 次,都明显差于二分。
差距有多大?平均多 1.75 次,最坏多 10 次。在 100 个数的小游戏里,这只是多点几下鼠标;
换成「问专家一个问题要 5 块钱」,就是成本差一倍。
把范围放大到 1 到 1000,差距更明显:随机平均 12.06 次、最坏 23 次;
二分平均 8.82 次、最坏 10 次,正好等于 log₂1000 上取整。
四、这件事跟数据标注的关系
猜数字是「二十问」游戏的最小版本:每次提问,你要让剩下的可能性尽量少。 标注规范里那些「请选择最接近的一项」,本质上也是在设计提问。 选项切得粗,一个问题排除不了多少可能,标注员就得反复问;选项切得细,一个问题顶三个。 所以「二分」这个思路在标注里到处都是:置信度分档、难例分层抽检、 按类别逐层细分——都是让每一步砍掉一半。 这一节的小游戏,练的就是这个直觉:好问题的标准是「能排除多少」,不是「问得多详细」。
五、动手 Demo:随机策略与二分策略各玩一千局
先跑一局二分策略并打印每一次猜的数; 再让随机策略与二分策略在 1 到 100 上各玩 1000 局,统计平均、最坏、最好次数; 然后把范围放大到 1 到 1000,各玩 300 局再对比一次,并与理论上界对照。
def play(guess_fn, target):
lo, hi, n, trail = 1, 100, 0, []
while True:
g = guess_fn(lo, hi)
n += 1
trail.append(g)
if g == target:
return n, trail
if g < target:
lo = g + 1
else:
hi = g - 1
def half_g(lo, hi):
return (lo + hi) // 2 # 必须整除
实跑输出:
--------------------------------------------------------------------
实例 5:猜数字,随机策略与二分策略各玩一千局
--------------------------------------------------------------------
目标数(先告诉你看得清楚) 73
二分策略的每一猜 [50, 75, 62, 68, 71, 73]
猜中用了 6 次
策略 平均次数 最坏 最好
------------------------------
随机猜 7.58 17 1
二分 5.83 7 1
二分理论上界 7 次(2^7 = 128 ≥ 100)
随机策略也在缩范围,但它可能连着猜同一片,白猜。
二分每猜一次,候选范围必砍一半;随机可能连着猜中同一片区域。
这就是「有信息量的提问」和「瞎问」的差别——
数据标注里问专家选哪类,也是同一个道理。
换个范围再验一次(1~1000):
策略(1~1000) 平均次数 最坏
-------------------------------
随机猜 12.06 23
二分 8.82 10
二分理论上界 10 次(2^10 = 1024 ≥ 1000)
一局记录很清楚:猜 50(小了)、75(大了)、62、68、71、73,六次命中。
每一次都把范围砍掉一半。
一千局统计:随机 7.58 平均、17 最坏、1 最好;
二分 5.83 平均、7 最坏、1 最好。
二分的最坏 7 次正好等于 log₂100 上取整——理论上界就是这么来的,实测一模一样。
放大到 1 到 1000:随机 12.06 平均、23 最坏;
二分 8.82 平均、10 最坏,等于 log₂1000 上取整。
30 秒实验
- 把范围改成 1 到 10000,先算出二分的最坏次数,再跑一遍验证。
- 加第三种策略:每次猜
lo + (hi - lo) // 3,看平均次数比二分的多还是少。 - 想一想:为什么随机策略的最坏次数会比二分多出一倍以上?
常见误区
- 「随机猜和二分差不多」——平均多 1.75 次,最坏多 10 次。
- 「范围调整不用 +1」——猜过的数已经排除,留着就多猜一次。
- 「问题问得越细越好」——好问题的标准是能排除多少可能,不是多详细。
自测三问(点开对答案)
1. 1 到 100 用二分最多猜几次?为什么? —— 7 次;2 的 7 次方是 128,已经覆盖 100 个数。 2. 为什么边界要写成 `lo = g + 1`? —— 猜过的那个数已经排除,留在范围里会多猜一次。 3. 这个游戏跟标注规范有什么关系? —— 都在设计「提问」,好问题能一次排除掉大部分可能性。第三章的语法和实例到此结束,下一章进机器学习算法——从「模型怎么学」开始。
- ☐ (本章完,下一节 4.1.1 机器学习与人工智能:共筑智能未来)