---
description: "两端点燃 = 剩余时间减半，级联三次 $4+2+1=7$；本质是 $7/8$ 的二进制展开"
---

# 3 根绳子烧出 7 分钟

## 题

3 根不均匀的绳子，每根完整烧完恰好 8 分钟——但因为粗细不均，烧一半长度不等于烧 4 分钟。只有火柴，没有表。如何精确计时 7 分钟？

## 一句话

两端同时点燃能把一根绳的剩余燃烧时间精确减半；三根绳级联三次减半，$4 + 2 + 1 = 7$，恰好是 $8 \times \left(\frac{1}{2} + \frac{1}{4} + \frac{1}{8}\right)$。

## 关键技巧

绳子不均匀意味着**长度信息全部作废**：不能对折找中点，不能按刻度剪。可靠的只剩两样——"某根绳烧完"这个**事件**，和"在事件发生的瞬间点火"这个**操作**。整道题就是用这两样原料搭一条事件链。

核心引理：**无论多不均匀，两端同时点燃的绳，烧完耗时 = 剩余燃烧时长的一半。** 证明只需换一个坐标系：不按长度、按"燃烧时间"给绳子标刻度（第 $s$ 分钟烧到哪就标 $s$）。每个火头每分钟恰好消耗 1 分钟的刻度，两个火头合计每分钟消耗 2 分钟，剩余量 $R$ 自然在 $R/2$ 后烧尽——两火头相遇的位置不确定，相遇的**时刻**却分毫不差。

于是"在另一端补一把火"就是一个精确操作：**把这根绳当前的剩余时间除以 2**。想测 $7 = 8 - 1$，就要让最后一根绳恰好剩 1 分钟再补火，倒推出二进制展开 $\frac{7}{8} = 0.111_2$——每一位 1 对应一次减半。

## 解

$t=0$ 时三根绳一起点，但点法不同：

1. **$t=0$**：绳 A 点**两端**，绳 B、绳 C 各点**一端**。
2. **$t=4$**：绳 A 烧完（$8/2 = 4$）。此刻点燃绳 B 的另一端——B 已单端烧了 4 分钟，剩 4 分钟量，减半后还需 2 分钟。
3. **$t=6$**：绳 B 烧完。此刻点燃绳 C 的另一端——C 已单端烧了 6 分钟，剩 2 分钟量，减半后还需 1 分钟。
4. **$t=7$**：绳 C 烧完，计时结束。

```echarts
{
  "height": 260,
  "grid": {"left": 60, "right": 40, "top": 40, "bottom": 30},
  "legend": {"top": 0},
  "xAxis": {"type": "value", "max": 8, "interval": 1, "name": "分钟"},
  "yAxis": {"type": "category", "data": ["绳 C", "绳 B", "绳 A"]},
  "series": [
    {
      "name": "单端燃烧", "type": "bar", "stack": "t", "barWidth": 22,
      "itemStyle": {"color": "#f28e2c"}, "data": [6, 4, 0]
    },
    {
      "name": "双端燃烧", "type": "bar", "stack": "t", "barWidth": 22,
      "itemStyle": {"color": "#4e79a7"}, "data": [1, 2, 4],
      "markLine": {
        "symbol": "none",
        "label": {"formatter": "7 分钟", "position": "insideEndTop"},
        "lineStyle": {"color": "#e15759", "width": 2},
        "data": [{"xAxis": 7}]
      }
    }
  ]
}
```

每根绳烧完的瞬间就是给下一根补火的信号，误差不会累积——每一步都由"烧完"这个物理事件对齐，而不是靠人估时间。

## 延伸：除了 7，还能计哪些时间

从最简单的往上爬：

- **一根绳**：单端点燃得 **8**，双端点燃得 **4**。就这两个。
- **两根绳**：级联一次得 **6** $= 4 + 2$；也可以往上接力——A 双端烧完再单端点 B 得 **12** $= 4 + 8$，纯接力得 **16** $= 8 + 8$。面试经典原版"两根 60 分钟绳测 45 分钟"就是级联的 $60 \times \frac{3}{4}$。级联通式：$n$ 根 $T$ 分钟的绳可测 $T\left(1 - 2^{-n}\right)$，本题的 7 就是 $8 \times \frac{7}{8}$。
- **三根绳**：除了 7，还有两个不那么显然的——
    - **9 分钟** $= 4 + 2 + 3$：A、B 照原方案烧出事件 4 和 6，但 C 的单端改在 $t=4$ 才点、$t=6$ 补火。补火时 C 只烧了 2 分钟，剩 6 减半得 3，$t=9$ 烧完。
    - **10 分钟** $= 4 + 4 + 2$：A 双端给出事件 4，B 纯单端给出事件 8；C 在 $t=4$ 点单端、$t=8$ 补火，剩 4 减半得 2，$t=10$ 烧完。
- **完整清单**：只允许在事件时刻点绳头的话，三根绳从点火起能测的**恰好是 4、6、7、8、9、10、12、14、16、20、24 分钟**（穷举见下方折叠块）。注意 8 分钟以内只有 4、6、7、8——想要 5 分钟，从划火柴那刻起是测不出来的。
- **允许计时起点延迟呢**：如果只要求"从事件 X 到事件 Y 恰好 $k$ 分钟"，1 到 8 的**每个整数**都能测。比如 5 分钟就藏在 9 分钟方案里：从 $t=4$（A 烧完）到 $t=9$（C 烧完）。1 分钟则是原方案里 $t=6$ 到 $t=7$ 的那一段。
- **一般化到 $n$ 根 $T$ 时间的绳**：可测时刻数 $f(n) = 2, 5, 11, 23, 48, 101, 218, 473, \ldots$，即 OEIS A283075[^a283075]（出自 FiveThirtyEight 的 Riddler 栏目），**没有已知闭式**，只能穷举，经验增长率约 $2.19^n$。前 4 项恰好等于 $3 \cdot 2^{n-1} - 1$，很容易让人以为找到了规律——但 $n=5$ 时公式给 47，实际是 48，简单倍增的直觉在这里破裂。不过两条结构性规律不随 $n$ 变复杂：$[0, T]$ 内可测的**恰好只有级联家族** $T\left(1-2^{-k}\right)$（$k = 0, \ldots, n$，共 $n+1$ 个）；最大可测 $nT$（纯接力），且一切可测值都是 $T$ 的二进分数、分母至多 $2^n$。
- **为什么不能对折**：对折找的是长度中点，不均匀时长度中点 $\ne$ 时间中点。两端点燃有效，靠的是燃烧时间的守恒（两火头合计消耗速率恒定），与几何位置无关。
- **理论上限**：每个新事件时刻都由"减半"和"相加"生成，所以一切可测时长都是 $T$ 的二进分数（$k/2^m$ 倍）——$8/3$ 分钟这样的目标加多少根绳都测不出来。
- **允许点绳子中间呢**：在任意内点点火会产生两个新火头，相当于把绳拆成两段"双端燃烧"。若某段烧完就立刻在剩余部分再点一个内点，始终维持 4 个火头，一根绳就能烧出 $8/4 = 2$ 分钟——代价是要求"瞬间补火"这种理想化操作，通常只当趣味延伸。

??? note "穷举可测时刻"

    规则形式化：绳头只能在"事件时刻"（$t=0$ 或某根绳烧完）点燃。一根绳若 $t_1$ 先点一端、$t_2$ 补另一端，烧完时刻为 $\frac{t_1 + t_2 + 8}{2}$；永不补火则为 $t_1 + 8$。递归穷举所有策略：

    ```python
    from fractions import Fraction as F

    T, N = F(8), 3
    from_zero, intervals, seen = set(), set(), set()

    def rec(events, ropes_left):
        if (events, ropes_left) in seen:                     # 记忆化，N>=5 时必需
            return
        seen.add((events, ropes_left))
        from_zero.update(events)
        intervals.update(b - a for a in events for b in events if b > a)
        if ropes_left == 0:
            return
        for t1 in sorted(events):
            rec(events | {t1 + T}, ropes_left - 1)          # 只点一端
            for t2 in sorted(events):
                if t1 <= t2 < t1 + T:                        # 在事件 t2 补火
                    rec(events | {(t1 + t2 + T) / 2}, ropes_left - 1)

    rec(frozenset({F(0)}), N)
    print(sorted(x for x in from_zero if x > 0))
    # [4, 6, 7, 8, 9, 10, 12, 14, 16, 20, 24]
    print(sorted(x for x in intervals if x <= 8))
    # [1, 2, 3, 4, 5, 6, 7, 8]
    ```

    把 `N` 换成一般值即可复现 OEIS A283075[^a283075]：

    | $n$ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
    | --- | --- | --- | --- | --- | --- | --- | --- | --- |
    | 可测时刻数 $f(n)$ | 2 | 5 | 11 | 23 | 48 | 101 | 218 | 473 |

    前 6 项是本机穷举验证过的，7、8 两项取自 OEIS。OEIS 的评注还指出：如果允许"提前烧几根绳再开始计时"（即计时起点延迟，对应上文的区间玩法），可测集合会进一步变大。

??? note "数值验证"

    把绳子建模成一串随机长短的小段（每段有自己的燃烧秒数，总和 8 分钟），模拟火头逐段推进，验证无论怎么不均匀，三个事件时刻恒为 4、6、7：

    ```python
    import random

    def both_ends_time(segs):
        """两端同时点燃，返回烧完耗时。segs 是每小段的燃烧时长。"""
        segs, i, j, t = segs[:], 0, len(segs) - 1, 0.0
        while i < j:
            d = min(segs[i], segs[j])
            t += d
            segs[i] -= d; segs[j] -= d
            if segs[i] == 0: i += 1
            if segs[j] == 0: j -= 1
        return t + segs[i] / 2 if i == j else t

    def after_one_end(segs, t):
        """单端烧 t 分钟后剩下的段。"""
        out = segs[:]
        while t > 0 and out:
            d = min(out[0], t)
            out[0] -= d; t -= d
            if out[0] == 0: out.pop(0)
        return out

    def rand_rope(n=12, total=8):
        w = [random.random() for _ in range(n)]
        return [x / sum(w) * total for x in w]

    for _ in range(5):
        A, B, C = rand_rope(), rand_rope(), rand_rope()
        tA = both_ends_time(A)
        tB = tA + both_ends_time(after_one_end(B, tA))
        tC = tB + both_ends_time(after_one_end(C, tB))
        print(round(tA, 9), round(tB, 9), round(tC, 9))  # 恒为 4.0 6.0 7.0
    ```

[^a283075]: [OEIS A283075](https://oeis.org/A283075)，2026-07-28 读。
