鸡兔同笼算法
中国古代经典问题 · O(1) 解析解 · 四语言对照实现
概述
鸡兔同笼问题最早记载于约公元 5 世纪的《孙子算经》: 「今有雉兔同笼,上有三十五头,下有九十四足。问雉、兔各几何?」
从代数视角看,它是一组二元一次方程;从算法视角看,它是 O(1) 时间内完成的常数级运算 —— 无需枚举、无需迭代,一次代入即可得解。
问题描述
已知笼中鸡、兔共有 h 个头、f 只脚,求鸡和兔各有多少只。
- 每只鸡有 1 个头、2 只脚
- 每只兔有 1 个头、4 只脚
- 所有动物均为整数只,且头与脚均为非负整数
头 = 35,脚 = 94 → 鸡 = 23,兔 = 12。验证:23 + 12 = 35,23 × 2 + 12 × 4 = 94 ✓
算法思路
设兔数为 r、鸡数为 c,列出方程组:
合法性校验
f − 2h必须为 非负偶数- 计算得到的
c与r必须 ≥ 0 - 任一条件不满足 → 该组输入无解
实现
以下给出四种主流语言的等价实现。所有版本遵循相同的契约:输入 (heads, feet),返回 (chickens, rabbits) 或表示「无解」的空值。
Python
def solve(heads: int, feet: int):
rabbits = (feet - 2 * heads) // 2
chickens = heads - rabbits
if rabbits < 0 or chickens < 0 or (feet - 2 * heads) % 2 != 0:
return None
return chickens, rabbits
if __name__ == "__main__":
h, f = 35, 94
result = solve(h, f)
if result:
print(f"头={h}, 脚={f} => 鸡={result[0]}, 兔={result[1]}")
else:
print("无解")
JavaScript
function solve(heads, feet) {
const diff = feet - 2 * heads;
if (diff < 0 || diff % 2 !== 0) return null;
const rabbits = diff / 2;
const chickens = heads - rabbits;
if (chickens < 0) return null;
return { chickens, rabbits };
}
const h = 35, f = 94;
const r = solve(h, f);
console.log(r ? `头=${h}, 脚=${f} => 鸡=${r.chickens}, 兔=${r.rabbits}` : "无解");
Go
package main
import "fmt"
func solve(heads, feet int) (int, int, bool) {
diff := feet - 2*heads
if diff < 0 || diff%2 != 0 {
return 0, 0, false
}
rabbits := diff / 2
chickens := heads - rabbits
if chickens < 0 {
return 0, 0, false
}
return chickens, rabbits, true
}
func main() {
h, f := 35, 94
if c, r, ok := solve(h, f); ok {
fmt.Printf("头=%d, 脚=%d => 鸡=%d, 兔=%d\n", h, f, c, r)
} else {
fmt.Println("无解")
}
}
Rust
fn solve(heads: i32, feet: i32) -> Option<(i32, i32)> {
let diff = feet - 2 * heads;
if diff < 0 || diff % 2 != 0 {
return None;
}
let rabbits = diff / 2;
let chickens = heads - rabbits;
if chickens < 0 {
return None;
}
Some((chickens, rabbits))
}
fn main() {
let (h, f) = (35, 94);
match solve(h, f) {
Some((c, r)) => println!("头={}, 脚={} => 鸡={}, 兔={}", h, f, c, r),
None => println!("无解"),
}
}
paseo × Claude Code 速度差异分析
同一模型、同一任务、同一中转平台 —— 为什么两条链路的体感差出一个数量级?
背景与现象
在 paseo(第三方手机端 agent 框架)与 Claude Code(官方 CLI)中,分别接入同一家 vibeapi 中转的同一个 Opus 4.7 模型,让它们执行完全相同的任务:基于一个已有的 chicken_rabbit.md,生成 Claude 官网风格的网页。
两条链路调用的名义模型一致、任务输入一致、中转平台一致,按直觉应当性能接近。但实际差异不是百分之几十,而是数量级。下面拆解原因。
两条链路示意
五大原因
按对延迟的贡献度,从大到小排列。
Prompt Caching 命中率最关键
Anthropic 官方 API 支持 prompt caching:系统提示、工具定义、对话历史这些大块前缀只要 5 分钟内复用,下一次请求输入 token 的计费降到 1/10、prefill 延迟也几乎归零。一个任务里有几十次工具调用,每一次都能复用同一份巨大的前缀。
Claude Code 走原厂直连,默认开启 cache_control,TTFT 极短。paseo → vibeapi 这条链路通常没有 caching —— 第三方中转要么不传 cache_control 字段,要么虽然透传但分流到不同后端实例,缓存键对不上、每次都是冷启动。冷启动意味着每次都要重算几万 token 的输入,光 prefill 就要十几秒。
Skill 加载把上下文炸大上下文膨胀
截图中 paseo 触发了 Skill frontend-design。这个 skill 会把一大批设计参考、组件模板、HTML/CSS 范本作为参考材料注入到上下文里,单次注入十几 k 到几十 k token 是常事。
Claude Code 这边因为 CLAUDE.md 要求「写内容之前先经过用户同意」,先用 AskUserQuestion 对齐方案,没有触发 frontend-design,上下文保持在很小的量级。膨胀的上下文 × 没有 caching = 双重惩罚。
多层中转的固有开销链路代价
paseo 链路有 4 跳,Claude Code 链路只有 2 跳。paseo 作为 agent 框架还会注入自己的系统提示、工具协议封装、会话状态管理。每多一层封装:网络 RTT 增加、可能有排队(第三方网关并发池容量有限)、可能有参数改写(破坏 cache key)。
「同一模型」不一定真同一模型路由
vibeapi 上挂着名字叫 Opus 4.7 1M 的模型,不保证后端真的路由到原厂 Opus 4.7。第三方平台常见做法:按账户/价格梯度路由到不同后端、高负载时悄悄降级到 Sonnet 但保留 Opus 展示名、把 1M 上下文请求拦截到慢速专用池。
输出节奏对体感的放大感知层
Opus 4.7 的稳态 output 速度约 50–80 tok/s,两边都一样。但 paseo 显示的「Worked for Xm Xs」是 TTFT + prefill + output 的总和。当前面的 prefill 卡住几十秒乃至几分钟,output 还没开始,整体就显得「数量级慢」。
贡献度估算
把每个因素对总延迟的相对贡献量化(估算值,非实测):
验证方法
结论需要可验证。最具操作性的方式是「重复请求触发缓存对比」:
T1。
T2。
T1 与 T2:
· 若
T2 ≪ T1(明显变快) → 是 caching 起作用,但首次冷启动慢;问题在首次成本。
· 若
T2 ≈ T1(两次一样慢) → 这条链路根本没开 caching,问题在中转层而非模型。
T1' / T2'。差值能直接看出两条链路的 caching 利用率差距。
结论
Claude Code 直连吃到了 prompt caching 的全部红利;paseo + vibeapi 这条链路在中转环节把它弄丢了,再叠加 Skill 注入造成的上下文膨胀,于是出现了同样一个 Opus 4.7 在两端「秒级 vs 分钟级」的对比。
实践建议
- 若必须使用第三方中转:优先选择透传
cache_control、不做请求改写的服务商。 - 对延迟敏感的工作流:能直连原厂就直连,caching 是工程优化的最大杠杆。
- 使用 agent 框架时关注它是否默认加载重 Skill —— skill 价值很高,但首轮成本也最高。
- 评估第三方模型时不要只看名字,跑一次 prompt cache 命中测试就能定性判断链路质量。