【探索】糖果逻辑题

Last updated on August 9, 2026 pm

最近一道测试大模型的摸糖果逻辑题很有趣,网上围观的重点是它“怎么想”。这类题本质是最坏情况计数,我写了个脚本 ~/pi-test/candy_puzzle.py,能随机出一道新题并自动给出「题目 / 解答 / 四步证明」,且答案保证是真实最小界

题目形态

从 24 种口味里抽 3 种、10 种形状里抽 2 种,再随机生成 6 个数作为数量,拼出一道题。例如:

黑袋子里有苹果、桃子、西瓜三种口味,圆形/五角星形两种形状。苹果-圆 7、苹果-星 7;桃子-圆 9、桃子-星 6;西瓜-圆 8、西瓜-星 4。形状可摸出,口味需取出才知。求最少摸几个,能确保手里同时有“形状不同的苹果与桃子”?

“形状不同”是互斥条件:可接受 圆形苹果+五角星桃子圆形桃子+五角星苹果,二者任一即可。

求解:反推失败条件

设两形状各摸 r、q 个,目标口味 f1/f2,干扰 f3,数量映射为 a~f。保证成功等价于同时满足:

1
2
3
4
r ≥ e+1            # 否则可能全是干扰味形状A
q ≥ f+1 # 否则可能全是干扰味形状B
(r ≥ c+e+1q ≥ d+f+1) # 覆盖一组互斥组合
(r ≥ a+e+1qb+f+1) # 覆盖另一组互斥组合

在此约束下最小化 r+q,最优解必然落在四个角点之一:

策略含义
R1形状A摸全两目标味 + 形状B任一非干扰
R4形状B摸全两目标味 + 形状A任一非干扰
mA必含f2 + B必含f1(交叉配对)
nA必含f1 + B必含f2(交叉配对)

取四者最小值即真实最小界

完整脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
糖果逻辑题生成器
以"真实最小界"为核心,每次随机生成一道具体的糖果题,并输出 题目 / 解答 / 证明。
"""
import random

# 足够大的口味池(干扰集合)
FLAVORS = [
"苹果", "桃子", "西瓜", "草莓", "葡萄", "蓝莓", "柠檬", "橙子",
"芒果", "荔枝", "菠萝", "樱桃", "香蕉", "椰子", "榴莲", "猕猴桃",
"杨梅", "枇杷", "山楂", "桑葚", "石榴", "柚子", "蜜瓜", "杏仁",
]
# 足够大的形状池
SHAPES = [
"圆形", "五角星形", "三角形", "爱心形", "正方形", "六边形",
"菱形", "月牙形", "水滴形", "花朵形",
]

def pick_flavors(n=3):
return random.sample(FLAVORS, n) # 抽 n 个不同口味

def pick_shapes(n=2):
return random.sample(SHAPES, n) # 抽 2 个不同形状

def rand_counts():
return [random.randint(1, 9) for _ in range(6)] # a,b,c,d,e,f

# ---- 真实最小界 ----
def true_answer(a, b, c, d, e, f):
m = (a + e + 1) + (d + f + 1) # F2(形状A) + F1(形状B)
n = (c + e + 1) + (b + f + 1) # F1(形状A) + F2(形状B)
R1 = max(a, c) + e + f + 2 # 形状A 摸全两种目标味 + 形状B 任一非干扰
R4 = max(b, d) + e + f + 2 # 形状B 摸全两种目标味 + 形状A 任一非干扰
return min(R1, R4, m, n), (R1, R4, m, n)

def best_strategy(a, b, c, d, e, f):
"""返回 (cost, 描述, 两个形状各自摸几个) 中最优的那套"""
R1 = (max(a, c) + e + f + 2, "R1", (max(a, c) + e + 1, f + 1))
R4 = (max(b, d) + e + f + 2, "R4", (e + 1, max(b, d) + f + 1))
m = ((a + e + 1) + (d + f + 1), "m", (a + e + 1, d + f + 1))
n = ((c + e + 1) + (b + f + 1), "n", (c + e + 1, b + f + 1))
return min([R1, R4, m, n], key=lambda t: t[0])

# ---- 生成题目 ----
def generate_puzzle():
f1, f2, f3 = pick_flavors() # f1,f2 为目标口味;f3 为干扰
s1, s2 = pick_shapes()
a, b, c, d, e, f = rand_counts()
return dict(f1=f1, f2=f2, f3=f3, s1=s1, s2=s2,
a=a, b=b, c=c, d=d, e=e, f=f)

def render(p):
f1, f2, f3 = p["f1"], p["f2"], p["f3"]
s1, s2 = p["s1"], p["s2"]
a, b, c, d, e, f = p["a"], p["b"], p["c"], p["d"], p["e"], p["f"]
ans, _ = true_answer(a, b, c, d, e, f)
cost, tag, (r1, r2) = best_strategy(a, b, c, d, e, f)

lines = []
lines.append("=" * 62)
lines.append("【题目】")
para = (f"想象一个黑袋子,里面装着三种口味的糖果:{f1}味、{f2}味和{f3}味。"
f"每种糖果又有两种形状:{s1}{s2}。"
f"最关键的设定是,形状不同,手感能摸出来,也就是我们可以通过触觉区分是{s1}还是{s2},"
f"但尝出来是什么味得拿出来后或者吃了才知道,光靠摸是永远猜不出口味的。"
f"具体的数量是硬指标,{f1}味的{s1}{a}个、{s2}{b}个;"
f"{f2}味的{s1}{c}个、{s2}{d}个;"
f"{f3}味的{s1}{e}个、{s2}{f}个。"
f"为了百分之百保证,你最少需要从袋子里摸出多少个糖果,"
f"才能确保手里同时拥有“不同形状的{f1}味和{f2}味的糖”?")
lines.append(para)
lines.append("")

lines.append("【解答】")
lines.append(f"最少摸出 **{ans}** 个糖果。")
lines.append(f"具体做法(最优策略 {tag}):摸 {r1}{s1} + 摸 {r2}{s2},共 {cost} 个。")
lines.append("")

lines.append("【证明】")
lines += proof(p)
lines.append("=" * 62)
return "\n".join(lines)

def proof(p):
f1, f2, f3 = p["f1"], p["f2"], p["f3"]
s1, s2 = p["s1"], p["s2"]
a, b, c, d, e, f = p["a"], p["b"], p["c"], p["d"], p["e"], p["f"]
ans, (R1, R4, m, n) = true_answer(a, b, c, d, e, f)
cost, tag, (r1, r2) = best_strategy(a, b, c, d, e, f)

L = []
L.append(f"设摸 {r1}{s1}、q 个{s2}。目标是保证“{f1}味与{f2}味、形状不同”,")
L.append(f"即至少出现以下两种组合之一:")
L.append(f" ① {s1}{f1}味 与 {s2}{f2}味;")
L.append(f" ② {s2}{f1}味 与 {s1}{f2}味。")
L.append("")
L.append(f"【第一步:安全条件】若摸了 r 个{s1}、q 个{s2},反过来思考“什么时候会失败”。")
L.append(f"失败当且仅当存在某种取法,使得 (摸不到{f1}{s1} 或 摸不到{f2}{s2}) 且 "
f"(摸不到{f1}{s2} 或 摸不到{f2}{s1})。")
L.append(f"逐项排除后,保证成功的充要条件是同时满足:")
L.append(f" r ≥ e+1 = {e+1}(否则可能全是{f3}{s1});")
L.append(f" q ≥ f+1 = {f+1}(否则可能全是{f3}{s2});")
L.append(f" ( r ≥ c+e+1 = {c+e+1} 或 q ≥ d+f+1 = {d+f+1} );")
L.append(f" ( r ≥ a+e+1 = {a+e+1} 或 q ≥ b+f+1 = {b+f+1} )。")
L.append("")
L.append(f"【第二步:最优值】目标为最小化 r+q,最优解必然落在下列四个角点之一:")
L.append(f" R1:r=max(a,c)+e+1={max(a,c)+e+1}、q=f+1={f+1},花费 {R1};")
L.append(f" R4:r=e+1={e+1}、q=max(b,d)+f+1={max(b,d)+f+1},花费 {R4};")
L.append(f" m :r=a+e+1={a+e+1}、q=d+f+1={d+f+1},花费 {m};")
L.append(f" n :r=c+e+1={c+e+1}、q=b+f+1={b+f+1},花费 {n}。")
L.append(f"取最小:min(R1,R4,m,n) = min({R1},{R4},{m},{n}) = **{ans}**,由策略 {tag} 达到。")
L.append("")
L.append(f"【第三步:可行性验证(以 {tag} 为例)】")
t, rr, qq = tag, r1, r2
if t == "R1":
L.append(f"摸 {rr}{s1}{s1}{f3}味仅 {e} 个,非{f1}味的只有 {c+e} 个、非{f2}味的只有 {a+e} 个,")
L.append(f" 摸 {max(a,c)+e+1} 个必然同时摸到{f1}味与{f2}味的{s1};")
L.append(f"摸 {qq}{s2}{s2}{f3}味仅 {f} 个,摸 {f+1} 个必含{f1}味或{f2}味;")
L.append(f"把该{f1}味/{f2}{s2}与相反的{s1}目标味配对,即得形状不同的{f1}+{f2}。")
elif t == "R4":
L.append(f"摸 {rr}{s1}必含{f1}味或{f2}味;")
L.append(f"摸 {qq}{s2}{s2}{f3}味仅 {f} 个,非{f1}味只有 {d+f} 个、非{f2}味只有 {b+f} 个,")
L.append(f" 摸 {max(b,d)+f+1} 个必然同时摸到{f1}味与{f2}味的{s2};")
L.append(f"把两形状的目标味交叉配对,即得形状不同的{f1}+{f2}。")
elif t == "m":
L.append(f"摸 {rr}{s1}必含{f2}味(非{f2}味仅 {a+e} 个);")
L.append(f"摸 {qq}{s2}必含{f1}味(非{f1}味仅 {d+f} 个);")
L.append(f"于是必有 {s2}{f1}味 + {s1}{f2}味,形状不同。")
else:
L.append(f"摸 {rr}{s1}必含{f1}味(非{f1}味仅 {c+e} 个);")
L.append(f"摸 {qq}{s2}必含{f2}味(非{f2}味仅 {b+f} 个);")
L.append(f"于是必有 {s1}{f1}味 + {s2}{f2}味,形状不同。")
L.append("")
L.append(f"【第四步:为什么更少不行】若总数小于 {ans},r+q≤{ans-1},")
L.append(f"则四个角点条件必有至少一个被破坏,存在可构造的失败取法,故无法百分之百保证。")
return L

if __name__ == "__main__":
random.seed()
print(render(generate_puzzle()))

核心只有两个函数:true_answer 算四个角点并取最小界;best_strategy 返回最优策略及具体摸法(r1 个形状A + r2 个形状B)。render 组织题目/解答文本,proof 按四步渲染证明:安全条件 → 最优值 → 可行性(针对选中策略)→ “更少不行”。

用法

1
python3 ~/pi-test/candy_puzzle.py

每次运行随机出新题。改口味池/形状池/数量范围即可换“食材”,适合批量生成训练或测试集。

小结

脚本的价值不在算对一道题,而在把“最坏情况计数”这类题抽象成可随机出题、可自动证明的流水线,答案锚定在数学上可验证的真实最小界——而这正是 AI 常翻车的地方。


【探索】糖果逻辑题
https://hexo.limour.top/candy-logic-puzzle
Author
Limour
Posted on
August 9, 2026
Updated on
August 9, 2026
Licensed under