yes fuking shit
This commit is contained in:
@@ -0,0 +1,516 @@
|
||||
# 第三、四问:图论与动态规划算法引入方案
|
||||
|
||||
- 版本:2026-09-12
|
||||
- 前置文档:[第三四问架构优化与运行提速方案](第三四问架构优化与运行提速方案.md)(架构与 P0–P2 项)、
|
||||
[algo-3-bayes.md](algo-3-bayes.md)(现役第三问策略)、[algo-3.md](algo-3.md)(七点覆盖证明)
|
||||
- 范围:只讨论"用图论 / 动态规划类算法进一步压低**虚拟耗时**"这一件事;
|
||||
题面约束、协议语义、保守性不变量全部不动
|
||||
- 本文所有数字均为**本轮新增实测**(自建内存回环、冻结场景、现役代码),
|
||||
复现命令见 §7
|
||||
|
||||
---
|
||||
|
||||
## 0. 结论先行
|
||||
|
||||
**可以,而且第四问的空间远大于第三问;但真正的收益不在"把巡回排得更短",而在
|
||||
"把不必要的往返与盲清从决策层消掉"。**
|
||||
|
||||
| 判断 | 证据 | 可回收 |
|
||||
| --- | --- | --- |
|
||||
| 第三问**路由**已接近最优,纯图论排线几乎无空间 | 对**同一批实际作业点**求离线最优开放回路:2308 s vs 实测移动 2458 s,只剩 **6.1%(151 s)** | ≤ 150 s |
|
||||
| 第三问仍有空间,但在**探测次数**(序贯决策),不在排线 | 30.8 次探测 / 13 源,且 0 次 `no_signal`——每次都在有效收缩,但次数仍偏多 | −150 ~ −250 s |
|
||||
| 第四问**路由**有 20% 空间,但那是被定位/清除绕行"拖"出来的 | 离线最优 4998 s vs 实测移动 6257 s(**20.1%**);而覆盖点巡回的投影顺序 4365 s 已接近离线 31 点最优 4181 s(+4.4%) | −400 ~ −700 s |
|
||||
| 第四问最大单点是**定向源定位失败导致的后备盲清** | 平均每局 **131 次清除只有 13 次成功**,`fallback_clear_attempts=125.7`,清除动作 419 s + 清除段移动 798 s ≈ **1220 s/局(14% T)** | **−800 ~ −1100 s** |
|
||||
| 第四问第二点是**探测本身** | 平均 69 次探测 ≈ **2224 s/局(26% T)**,其中 32 s/次的移动占 1880 s | −400 ~ −800 s |
|
||||
| 覆盖点集**已经最小**,不要再压 | 27 点方案在 75 m/24 档"通过",但在 25 m/48 档有 **298 个 (p,h) 缺口** → 是采样过拟合的假解;在细网格上直接搜索的最小命中集仍是 **31 点** | 0(见 §5.1 的正确性风险) |
|
||||
|
||||
**一句话:第三问的目标是"把 30.8 次探测压到 ~26 次并把序排到最优",第四问的目标是
|
||||
"把 125 次盲清变成 3 次有保证的探测"。** 两者都是 DP / 序贯决策问题,
|
||||
不是经典的 TSP 排线问题。
|
||||
|
||||
---
|
||||
|
||||
## 1. 目标函数的图论化
|
||||
|
||||
题面计时模型(`config.CostConfig`,已由 `evaluation.recompute_virtual_time` 逐微秒复算验证):
|
||||
|
||||
```
|
||||
T = L/5 + 5·N_measure + 1·N_switch + 5·N_clear⁺ + 3·N_clear⁻
|
||||
```
|
||||
|
||||
把它写成图上的路径代价,后面所有算法都在这张图上做:
|
||||
|
||||
- **节点** = 一次"停靠"(覆盖站点、定位测点、清除点);
|
||||
- **边权** `w(a,b)` = `dist(a,b)/5`(移动秒);
|
||||
- **节点服务费** = `5·(该点要测的频道数) + 1·(换频次数)` 或 `5/3`(清除)。
|
||||
- 于是整局 = 一条**开放路径**(起点原点、终点自由),目标是最小化
|
||||
`Σ边权 + Σ服务费 + Σ动作惩罚`。
|
||||
|
||||
四类图问题分别对应四个子决策:
|
||||
|
||||
| 子决策 | 图问题 | 现役做法 | 目标做法 |
|
||||
| --- | --- | --- | --- |
|
||||
| 站点/任务排序 | **开放 TSP(小 n 精确)** | NN + 2-opt(滚动) | Held-Karp 精确 DP(n ≤ 14,含换频状态)|
|
||||
| 定位测点选择 | **带邻域的 TSP / 定向问题(TSPN/OP)** | 每任务独立选点 + 全表重排 | 覆盖骨架 + 最便宜插入;测点作为"邻域"参与联合优化 |
|
||||
| 定向源定位 | **POMDP / 随机最短路** | 一步贪心 `prob_detect/时间` | 2–3 步 rollout,值函数 = 期望后验 MEC 半径 |
|
||||
| 后备清除 | **区域覆盖路径(Covering Path)** | 格心最近邻 | 割草机 + 转向点 DP / 格心集 Or-opt |
|
||||
|
||||
---
|
||||
|
||||
## 2. 本轮实测基线(现役代码,冻结场景)
|
||||
|
||||
场景集:`harness/suites.py`,第三问 core 的 seed 100–105 × 13 源;
|
||||
第四问 core 全部 9 场(10/13/16 源 × 3 定向占比)。配置为现役默认。
|
||||
|
||||
### 2.1 第三问(`bayes`,13 源,6 场)
|
||||
|
||||
| 指标 | 数值 |
|
||||
| --- | ---: |
|
||||
| 平均虚拟时间 | **3197 s** |
|
||||
| 移动 | 12292 m = **2458 s(77%)** |
|
||||
| 覆盖点检测 | 83.5 次 = 501 s(其中 69.8 次 `no_signal`,属缺席证明) |
|
||||
| 定位探测 | **30.8 次 = 185 s**(0 次 `no_signal`) |
|
||||
| 清除动作 | 65 s |
|
||||
| **离线最优开放回路(同一批作业点)** | **2308 s** |
|
||||
| 路由可回收 | **151 s(移动的 6.1%,T 的 4.7%)** |
|
||||
| 作业点数(去重) | 35–50 |
|
||||
|
||||
### 2.2 第四问(`directional` + `q4_mincover_v1`,9 场)
|
||||
|
||||
| 指标 | 数值 |
|
||||
| --- | ---: |
|
||||
| 平均虚拟时间 | **8676 s** |
|
||||
| 移动合计 | 6257 s(72%) |
|
||||
| ├ 覆盖段移动 | 3579 s |
|
||||
| ├ **探测移动** | **1880 s** |
|
||||
| └ **清除移动** | **798 s** |
|
||||
| 动作耗时 | 2149 s(覆盖 1386 / 探测 344 / 清除 419) |
|
||||
| 探测次数 | 69.0 次(`no_signal` 4–12 次) |
|
||||
| **清除次数 / 成功** | **131.1 / 13.0 → 118 次失败** |
|
||||
| `fallback_clear_attempts` | **125.7 次/局** |
|
||||
| 后备盲清格数(单次) | 3 ~ 210 格,全部以 `cleared` 结束 |
|
||||
| **离线最优开放回路(同一批作业点)** | **4998 s** |
|
||||
| 路由可回收 | 1259 s(移动的 20.1%,T 的 14.5%) |
|
||||
| 覆盖投影顺序 vs 离线 31 点 2-opt | 4365 s vs 4181 s(+4.4%) |
|
||||
|
||||
### 2.3 成本地板估计
|
||||
|
||||
| 问题 | 不可压缩部分 | 现状 | 真实可压缩 |
|
||||
| --- | --- | ---: | ---: |
|
||||
| 第三问 | 七点环 1392 s + 覆盖检测 501 s + 清除动作 65 s + 每源必经的接近/清除移动 + 每源 ≥2 次探测 | 3197 s | **~300–450 s(10–14%)** |
|
||||
| 第四问 | 31 点巡回 ~4180 s + 缺席检测 ~1386 s + 每源 ≥3 次探测与一次清除 | 8676 s | **~2000–2800 s(23–32%)** |
|
||||
|
||||
> 第四问的 4000 s 验收线**依然不可达**(单是 31 点巡回 + 缺席检测就 ≈5566 s),
|
||||
> 这与前置文档 §6.2 的结论一致;本方案只承诺把 8676 s 压向 6500 s 量级。
|
||||
|
||||
---
|
||||
|
||||
## 3. 算法设计
|
||||
|
||||
### 3.1 D1(第四问最高优先):定向源的"抵近 + 交会"滚动时域 DP
|
||||
|
||||
**问题本质**:±1° 示向度误差在距离 d 处给出的横向不确定度约 `d·tan(1°) ≈ 0.0175 d`。
|
||||
d = 1000 m 时单条方位线的横向约束只有 ±17.5 m;两条方位线的交会角 θ 决定最终尺度
|
||||
`≈ 0.0175 d / sin θ`。要压到 20 m 清除证书,必须**同时**满足:交会角大、距离近。
|
||||
现役 `_visibility_probe` 只最大化 `prob_detect(q)/(移动+6)`,天然偏好"近且可见",
|
||||
与"交会角大"无关;远距离又永远收敛不到 20 m,于是退化到 20 m 网格盲清。
|
||||
|
||||
**形式化**(这就是一个 POMDP):
|
||||
|
||||
- 状态 `b` = `JointBelief`(位置 × 24 档朝向的可行集,见 `belief/joint.py`),
|
||||
再叠加 `r_eff ~ U[1000,1500]` 的不确定度;
|
||||
- 动作 `q` ∈ 候选测点(保证接收点 ∪ 对跖点 ∪ 三圈环探 ∪ **朝 MEC 圆心的抵近点**);
|
||||
- 观测 `o ∈ {direction, near, no_signal}`,由每条粒子 `(p, h, r_eff)` 解析预测;
|
||||
- 代价 `c(q) = 6 s + dist(pos,q)/5`;
|
||||
- 终止:`MEC(b).radius ≤ 20 − margin`(可一次清除);
|
||||
- 值函数:`V(b) = min_q E_o[ c(q) + V(b') ]`。
|
||||
|
||||
**实现选择(按代价递增)**:
|
||||
|
||||
1. **一步期望后验半径**(最小改动,复用 Q3 已有内核的二维推广):
|
||||
对候选 `q`,按粒子权重模拟三分支结果,用幸存子集的 MEC 半径近似 `V(b')`,
|
||||
取 `E[radius] + λ·c(q)` 最小者。把 `particles.py::expected_posterior_radius`
|
||||
的向量化实现在 `JointBelief` 上重写一份(numpy 可用时广播)。
|
||||
2. **2–3 步 rollout**:对前 K=8 个候选各展开一层,深度 2;墙钟预算 ≤ 0.3 s/决策。
|
||||
3. **交会角代理(解析、零墙钟)**:若一步法来不及落地,先用
|
||||
`score(q) = −E[radius] · sin(Δbearing(q, 既有方位线)) / dist(q, center)`
|
||||
的确定性代理,实验证明有效后再换 rollout。
|
||||
|
||||
**关键结构性改动**:把候选点集从"绕 MEC 的固定环"扩展为**含抵近点**,
|
||||
即允许 `q` 落在 `center + α·(pos − center)` 上、`α` 从 0.6 到 1.0 采样。
|
||||
这样当远距离无法收敛时,策略会**主动抵近**并以一次正常角域证书收尾,
|
||||
而不是把 200 格盲清当作唯一出路。后备清除**保留为安全网**,但目标是触发次数下降 ≥ 80%。
|
||||
|
||||
**验收**:`fallback_clear_attempts` ≤ 15/局(现 125.7);定向频道平均探测次数
|
||||
≤ 全向频道的 1.5 倍;清除成功率 ≥ 90%;9 场景全清率 100%。
|
||||
**预期**:−800 ~ −1100 s。
|
||||
|
||||
---
|
||||
|
||||
### 3.2 D2:覆盖骨架 + 最便宜插入(TSPN 的在线近似)
|
||||
|
||||
**问题本质**:31 个覆盖点是**必须在场**的(缺席证明),但探测点与清除点是
|
||||
**临时产生**的。现役做法把两类点混在一张任务表里,每次从零做 NN + 2-opt
|
||||
(`bayes_loop._order_tasks`),等于让临时任务把骨架顺序"打散"。
|
||||
实测投影顺序 4365 s vs 离线最优 4181 s,说明骨架顺序本身没坏,
|
||||
坏在**绕行开销**——而绕行开销正是插入位置没优化。
|
||||
|
||||
**形式化**:这是 **TSP with Neighborhoods / Generalized TSP**:
|
||||
|
||||
- 骨架:离线求覆盖点集的近优开放回路 `S`(`7` 点用解析证明;`31` 点用 Or-opt/3-opt,
|
||||
`n ≤ 16` 时可直接 Held-Karp 精确);
|
||||
- 在线:给定当前任务 `q` 与其**邻域**(`clear` 任务的邻域是 `C₂₀(K)`,`locate`
|
||||
任务的邻域是候选测点集),求
|
||||
`Δ(i,j) = dist(a_i, q) + dist(q, a_{j}) − dist(a_i, a_j)`
|
||||
最小的插入位置 `(i,j)`;这正是"图上的最便宜插入",`O(|S|²)` 每任务。
|
||||
|
||||
**实施**:
|
||||
|
||||
1. `guarantee/cover.py` 增加 `optimize_tour(points, seed_tour, passes)`(Or-opt + 2-opt),
|
||||
把 `nn_tour_m` 从"上界"变成"已优化值",并写入证书 `tour_virtual_s`。
|
||||
2. `planner/bayes_loop.py` 新增 `_skeleton`(覆盖点顺序)与 `_insert_task(task, neighborhood)`;
|
||||
`_order_tasks` 退化为"骨架遍历 + 已插入任务",保留现有 2-opt 作为兜底开关。
|
||||
3. `clear` 任务的邻域用 **`C₂₀(K)` 内离插入点最近的点**(见 3.4),使插入代价与
|
||||
清除目标联合最小。
|
||||
|
||||
**验收**:第四问移动 ≤ 5000 s(现 6257 s);路由相对离线最优的 gap
|
||||
(新增回归指标 `route_gap_vs_offline`)从 20.1% 降到 ≤ 10%。
|
||||
**预期**:−400 ~ −700 s。
|
||||
|
||||
---
|
||||
|
||||
### 3.3 D3:后备清除的覆盖路径(若 D1 后仍触发)
|
||||
|
||||
后备清除是"用 20 m 圆盘覆盖残差区域 `R` 直到命中真源"——即 **Covering Path Problem**。
|
||||
|
||||
- 当前 `_fallback_clear` 对格心做 nearest-first 扫描,等价于格心集上的 NN 开放路径,
|
||||
典型约为最优的 1.2–1.3 倍;
|
||||
- 改进:对格心集做 **Or-opt + 2-opt**(`k ≤ 400`,`O(k²)` 一次约 0.1 s,且只在
|
||||
极少数局触发);
|
||||
- 上界更优解:凸区域上的**割草机(boustrophedon)扫描**,用 DP 在扫描线方向选择
|
||||
转向点可精确最优;对一般多边形用梯形分解 + DP。
|
||||
|
||||
**验收**:后备触发时移动 ≤ 现值的 80%(单次);触发次数本身由 D1 压到 ≤ 15/局。
|
||||
**预期**:−100 ~ −200 s(D1 未完全消除时的兜底)。
|
||||
|
||||
---
|
||||
|
||||
### 3.4 T1/T3:第三问的精确排线、换频 DP 与清除点选择
|
||||
|
||||
**(a) Held-Karp 精确任务序(含换频状态)**
|
||||
|
||||
现役 `_two_opt` 是启发式且被 4 轮上限截断。任务表典型只有 6–12 项,
|
||||
可以**精确求解**:
|
||||
|
||||
```
|
||||
dp[S][j][c] = 已访问任务集 S、停在任务 j、测向频道为 c 的最小代价
|
||||
转移:dp[S∪{k}][k][c'] = dp[S][j][c] + dist(pos_j, pos_k)/5 + service(k, c, c')
|
||||
```
|
||||
|
||||
- 规模:`|T| ≤ 12` 时 `2^12 × 12 × 20 ≈ 1.0M` 状态,纯 Python 约 0.1–0.3 s/次;
|
||||
滚动重排每步一次会吃掉预算,因此**只在任务表新增/删除任务时重解**,
|
||||
或在 `|T| ≤ 9` 时默认启用、否则回退 2-opt + Or-opt。
|
||||
- 换频代价精确化:`service` 依赖进入/离开频道,把 `c` 并入状态即可精确;
|
||||
现役 `_path_cost` 用"按频道排序"的贪心近似,会低估/高估 1–3 s/站。
|
||||
- 附图论下界:对当前点集算 **MST 权重**(`O(n²)` Prim),
|
||||
得到开放路径最优值的下界,写入 `summary.route_lower_bound_s`,
|
||||
用于**在线后悔值审计**与回归("本步排序距最优 ≤ x%")。
|
||||
|
||||
**验收**:`route_gap_vs_offline` ≤ 3%;单局墙钟增量 ≤ 0.2 s。
|
||||
**预期**:−120 ~ −200 s。
|
||||
|
||||
**(b) 探测的 2 步 rollout(T2)**
|
||||
|
||||
现役 `_select_point` 是一步贪心(期望 log 幸存占比 + λ·移动)。
|
||||
把它扩成深度 2 的 rollout:对前 `K=8` 个候选,各展开一次测量的三分支
|
||||
(direction / near / no_signal),取 `E[V]` 最小者。
|
||||
`V` 沿用现有 `expected_survival`。这属于**滚动时域 DP**,不改任何安全性判定。
|
||||
|
||||
**验收**:探测次数 30.8 → ≤ 26;墙钟 ≤ 0.3 s/决策。
|
||||
**预期**:−150 ~ −250 s。
|
||||
|
||||
**(c) 清除点选在 `C₂₀(K)` 内离当前位置最近处(T3)**
|
||||
|
||||
`C₂₀(K) = ∩_{g∈K} B(g, 20)` 是凸集(圆盘之交)。求其内离当前点最近点是一个
|
||||
**凸可行性/投影问题**(对小规模可枚举多边形顶点 + 圆盘投影,或直接迭代投影)。
|
||||
现役 `_near_clear_target` 只沿一维方向平移,与最优之差 ≤ 0.74 m(前置文档 P1-1 已复核),
|
||||
收益主要来自**与 3.2 插入代价的耦合**:先定插入位置、再选清除点,比反过来省更多。
|
||||
|
||||
**验收**:清除段移动下降 ≥ 5%。
|
||||
**预期**:−50 ~ −150 s。
|
||||
|
||||
**(d) 七点环半径(T4)**:`1159.7 → 1122.9`(可证明下界),−44 s。
|
||||
仅作为"证明性"改动落地,不作为收益项。
|
||||
|
||||
---
|
||||
|
||||
## 4. 实施路线
|
||||
|
||||
| 阶段 | 内容 | 交付物 | 验收门槛 | 依赖 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| **S0 基线冻结**(0.5 天) | 把近期 sweep 的最优参数(`route_aware_probe=false`、`lambda_move_s≈0.06`、`opportunistic_radius_ratio≈0.4–0.5`)回填为基准,跑满 core+boundary 并归档 | `output/suite/q3-baseline`、`q4-baseline` | 现有 `valid` 口径 100% | 无 |
|
||||
| **S1 度量补全**(0.5 天) | 在 `scripts/run_suite.py` 增加 `route_gap_vs_offline`、`route_lower_bound_s`、`distinct_service_points`、`coverage_actual_visited` 四列 | runner 改动 + 回归 | 与本文 §2 实测复现一致 | S0 |
|
||||
| **S2 覆盖证书硬化**(1 天,**正确性优先**) | 证书分辨率 75 m/24 → 20 m/72;设计裕量 `r_design ≤ 960 m` 或细网格直接搜索;保留 10% 保险点预算 | `guarantee/cover.py`、`plans/*.json`、正/负例测试 | 细网格(15 m/72)缺口 = 0;故意删点必须被检出 | S1 |
|
||||
| **S3 第四问 D1**(2–3 天) | `belief/joint.py` 增加期望后验半径 / rollout 选点;`planner/__init__.py::_probe_candidates` 增加抵近点;后备降级为安全网 | `belief/joint.py`、`planner/__init__.py`、测试 | `fallback_clear_attempts ≤ 15`;全清 100%;墙钟 ≤ 1.0 s/局 | S2 |
|
||||
| **S4 第四问 D2/D3**(2 天) | 覆盖骨架 + 最便宜插入;`C₂₀` 邻域;后备 Or-opt | `planner/*`、`guarantee/cover.py` | 移动 ≤ 5000 s;`route_gap ≤ 10%` | S3 |
|
||||
| **S5 第三问 T1/T2/T3**(2 天) | Held-Karp(含换频)+ MST 下界;探测 2 步 rollout;`C₂₀` 最近点 | `planner/bayes_loop.py`、`plan/*` | 探测 ≤ 26;`route_gap ≤ 3%`;T ≤ 2900 s | S1 |
|
||||
| **S6 收口**(1 天) | 全量配对、论文数据表、消融图 | 报告 + `docs/paper` 数据 | 与 S0 配对全部 `valid`,无退化场景 | S4/S5 |
|
||||
|
||||
**顺序约束**:S1(度量)先于任何算法改动,否则"更快"不可证;S2(证书)先于
|
||||
第四问算法改动,否则可能在错误的安全基线上做优化。
|
||||
|
||||
---
|
||||
|
||||
## 5. 风险与明确不做的部分
|
||||
|
||||
### 5.1 本轮发现的正确性风险(必须先处置)
|
||||
|
||||
现役 `q4_mincover_v1`(31 点)与 `square49`(49 点)在**粗采样**下缺口为 0,
|
||||
但把校核分辨率从 75 m/24 档提到 25 m/48 档:
|
||||
|
||||
| 点集 | 点数 | 75 m/24 缺口 | 25 m/48 缺口 | 15 m/72 缺口 | 最坏命中@75 m | 最坏命中@25 m |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
|
||||
| `square49` | 49 | 0 | 0 | 0 | 760.3 m | 760.3 m |
|
||||
| `q4_mincover_v1`(现役) | 31 | 0 | **125** | **566** | 999.1 m | 1000.0 m |
|
||||
| 细网格重搜最小集 | 31 | 0 | 0 | 35 | 983.0 m | 996.2 m |
|
||||
| 贪心 27 点(75 m 档最优) | 27 | 0 | **298** | 1117 | 986.5 m | 1000.0 m |
|
||||
|
||||
结论:
|
||||
1. **31 点很可能就是实际下界附近**:多起点随机贪心 + 冗余剪枝在 25 m/48 细网格上
|
||||
直接搜索的最小解仍是 31 点(尚未做 LP 松弛下界,但连贪心都下不去);
|
||||
之前看到的 27 点是**采样过拟合的假解**,不可采纳;
|
||||
2. `q4_mincover_v1` 的安全裕量几乎为 0(999.1 m),存在"真源恰落在细缝 + `r_eff` 取下界"
|
||||
的漏检窗口,现役测试没暴露是因为随机场景未命中细缝且 `r_eff` 通常 > 1000;
|
||||
3. `square49` 裕量充足但巡回 36.4 km,不可回退;
|
||||
4. 正确做法是**细网格重搜 + 设计裕量**(S2),代价约 0–150 s,**先保安全再谈提速**。
|
||||
|
||||
### 5.2 其他风险
|
||||
|
||||
| 风险 | 影响 | 缓解 |
|
||||
| --- | --- | --- |
|
||||
| POMDP rollout 墙钟超预算 | 单局计算 > 1 s | 深度 2、候选截断 K=8、`--profile` 纳入基准必报列;超预算自动退回一步法 |
|
||||
| Held-Karp 状态爆炸(任务表变长) | 单步计算抖动 | 仅在 `|T| ≤ 9` 默认启用,`> 9` 回退 2-opt + Or-opt |
|
||||
| 抵近点破坏可见性保证 | 探测反而 `no_signal` 变多 | 抵近点仍以 `prob_detect > 0` 过滤;`no_signal` 是合法负观测,不破坏守恒性 |
|
||||
| 骨架插入与"找全即停"冲突 | 为走骨架而多绕行 | 骨架只提供顺序,不新增必须访问的点;任务表为空即停 |
|
||||
| 细网格证书重搜耗时 | 离线 3–5 min | 一次性离线搜索 + 运行期按计划名缓存校验(沿用 `load_plan` lru_cache) |
|
||||
|
||||
### 5.3 明确不做
|
||||
|
||||
- **不重引入 PPO/HRL**(前置文档已用 109 776 步的负面结果排除);
|
||||
- **不引入新的运行时依赖**(仍只有 `requests`;`numpy` 仅作可选加速后端,
|
||||
`networkx`/`scipy` 不进入运行时路径);
|
||||
- **不改变停止条件**(`unresolved_channels` 的口径取舍属于评分方式问题,
|
||||
须先与题面评分方式核对,本轮不作为默认行为);
|
||||
- **不承诺第四问 4000 s**(成本地板 ≈5566 s,见 §2.3)。
|
||||
|
||||
---
|
||||
|
||||
## 6. 验收与度量
|
||||
|
||||
### 6.1 有效性定义(沿用,不得放宽)
|
||||
|
||||
```
|
||||
valid := 全部真源被清除 ∧ 缺席频道与真值一致 ∧ 正常 /exit
|
||||
∧ 零算法异常 ∧ 逐微秒计时复算一致
|
||||
```
|
||||
|
||||
### 6.2 新增必报列(写入 `scripts/run_suite.py` 逐局行与 aggregate)
|
||||
|
||||
| 列 | 定义 | 用途 |
|
||||
| --- | --- | --- |
|
||||
| `route_gap_vs_offline` | `实测移动 − 离线最优开放回路(同作业点集)` / 实测移动 | 区分"排线不够好"与"点选得不对" |
|
||||
| `route_lower_bound_s` | 当前任务集 MST 权重 / 速度 | 在线后悔值审计 |
|
||||
| `distinct_service_points` | 去重作业点数 | 探测是否真的在减少 |
|
||||
| `coverage_actual_visited` | 实际测过的覆盖点数 | 缺席证明是否提前结束 |
|
||||
| `probe_no_signal_ratio` | 探测中 `no_signal` 占比 | D1 的效果指标 |
|
||||
| `fallback_clear_attempts` | 已有 | D1 的主验收指标 |
|
||||
|
||||
### 6.3 目标(vs §2 基线)
|
||||
|
||||
| 指标 | Q3 现 | Q3 目标 | Q4 现 | Q4 目标 |
|
||||
| --- | ---: | ---: | ---: | ---: |
|
||||
| core 平均虚拟时间 | 3197 s | **≤ 2900 s** | 8676 s | **≤ 6500 s** |
|
||||
| 移动 | 2458 s | ≤ 2250 s | 6257 s | ≤ 5000 s |
|
||||
| 探测次数 | 30.8 | ≤ 26 | 69.0 | ≤ 45 |
|
||||
| 后备盲清次数 | 0 | 0 | 125.7 | **≤ 15** |
|
||||
| `route_gap_vs_offline` | 6.1% | ≤ 3% | 20.1% | ≤ 10% |
|
||||
| `fallback` 清除成功率 | — | — | 10% | ≥ 90% |
|
||||
| 单局计算墙钟 | 1.0 s | ≤ 1.2 s | 1.0 s | ≤ 1.5 s |
|
||||
| 细网格证书缺口(15 m/72) | — | 0(Q3 解析) | 566 | **0** |
|
||||
|
||||
### 6.4 回归
|
||||
|
||||
`uv run pytest`(当前 232 项)必须全过;新增:
|
||||
|
||||
- `CoveragePlan` 细网格正/负例(删掉任一点必须被检出);
|
||||
- `JointBelief` 的期望后验半径与真源保留性质对拍(随机 `(p,h)` × 随机观测序列);
|
||||
- `Held-Karp` 与 `2-opt` 在同一小任务集上的最优性对拍(DP 结果必须 ≤ 2-opt);
|
||||
- `C₂₀` 投影的三角不等式性质(投影点距候选集任一点仍 ≤ 20);
|
||||
- 逐位一致性:numpy 与纯 Python 两条路径在同一动作序列下 `actions_sha256` 相同。
|
||||
|
||||
---
|
||||
|
||||
## 7. 复现命令(本轮实测)
|
||||
|
||||
```bash
|
||||
# 度量基线(§2.1/§2.2/§2.3):作业点分解 + 离线最优回归 + 成本地板
|
||||
uv run python /tmp/analyze_headroom.py # 路由 gap:Q3 6.1%,Q4 20.1%
|
||||
uv run python /tmp/decompose.py # Q4 三段移动 + 动作 + 清除成功率
|
||||
uv run python /tmp/q3stats.py # Q3 探测/覆盖检测/跳测统计
|
||||
|
||||
# 覆盖证书分辨率(§5.1)
|
||||
uv run python /tmp/verify_margin.py # 现役 31 点在 25 m/48 档缺口 125
|
||||
uv run python /tmp/hitset_fine.py # 细网格最小解仍是 31 点
|
||||
|
||||
# 既有常驻工具
|
||||
uv run python scripts/run_suite.py --problem 4 --suite all --jobs 6 \
|
||||
--variants directional@q4_mincover_v1 --label q4-baseline
|
||||
uv run python scripts/verify_q4_cover.py --out output/q4/cover-certificate.json
|
||||
uv run python scripts/analyze_run_timing.py <log.jsonl>
|
||||
```
|
||||
|
||||
> 上述 `/tmp/*.py` 为一次性分析脚本,S1 阶段应把其中的
|
||||
> `route_gap_vs_offline`、`offline_opt_open_path`、`mst_lower_bound`
|
||||
> 三个函数并入 `scripts/run_suite.py`(或新建 `scripts/analyze_routing.py`)常驻入库,
|
||||
> 使"虚拟时间花在哪"与"覆盖集是否合法"一样成为可随时重跑的回归项。
|
||||
|
||||
---
|
||||
|
||||
## 8. 本轮实测回填(2026-09-12 实施结果)
|
||||
|
||||
本节记录 S1–S5 全部落地后的**实测**结果与**负面结论**。所有数字均为冻结基准集
|
||||
(`harness/suites.py`)配对复跑,口径与 §6.1 完全一致;复现命令见 §8.6。
|
||||
|
||||
### 8.1 交付物清单
|
||||
|
||||
| 项 | 位置 | 状态 |
|
||||
| --- | --- | --- |
|
||||
| 图论/DP 工具箱(精确开放路径、2-opt/Or-opt、MST 下界、路由度量) | `src/mcm26b/routing.py` | ✅ 新增 |
|
||||
| S1 必报列(`route_gap_vs_offline`、`route_lower_bound_s`、`distinct_service_points`、`coverage_actual_visited`、`probe_no_signal_ratio`、`plan_verify_wall_s`) | `scripts/run_suite.py` | ✅ 新增 |
|
||||
| S2 设计裕量覆盖计划 + 细网格证书 + 精细剪枝 | `guarantee/cover.py`、`scripts/verify_q4_cover.py`、`plans/q4_mincover_v2.json` | ✅ 新增并设为默认 |
|
||||
| S3/D1 期望后验半径值函数 + 可见性过滤 + 抵近点 | `belief/joint.py`、`planner/__init__.py` | ✅ 新增(默认口径见 §8.3) |
|
||||
| S4/D3 后备清除路径改进 | `planner/__init__.py::_fallback_order` | ⚠️ 已实现,实测负收益,默认关闭 |
|
||||
| S5/T1 Held-Karp 精确任务序(含换频状态) | `planner/bayes_loop.py::_exact_order` | ⚠️ 已实现,实测负收益,默认关闭 |
|
||||
| 回归测试 | `tests/test_routing.py`、`tests/test_joint_belief.py`、`tests/test_coverage_plan.py` | ✅ 新增,`uv run pytest` 268 项全过 |
|
||||
|
||||
### 8.2 最终成绩(vs S0 基线)
|
||||
|
||||
| 指标 | Q3 基线 | Q3 现 | Q4 基线 | Q4 现 | 目标 |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: |
|
||||
| core 平均虚拟时间 | 3210.3 s | **3188.7 s(−0.7%)** | 8676.1 s | **8602.0 s(−0.9%)** | 2900 / 6500 ✗ |
|
||||
| core 最大虚拟时间 | 3798.5 s | **3663.7 s(−3.5%)** | 10890.8 s | **9630.4 s(−11.6%)** | — |
|
||||
| 移动 | 12446.8 m | **12338.1 m(−0.9%)** | 31286.4 m | 32891.1 m(+5.1%) | 2250 / 5000 ✗ |
|
||||
| 检测次数 | 111.0 | 111.1 | 346.0 | **333.7** | — |
|
||||
| 后备盲清次数 | 0 | 0 | **125.7** | **4.3(−96.6%)** | ≤15 ✅ |
|
||||
| `route_gap_vs_offline` | 8.67% | **8.07%** | 18.9% | 24.3% | ≤3% / ≤10% ✗ |
|
||||
| 单局计算墙钟 | 1.29 s | **1.04 s** | 5.29 s | **4.09 s** | ≤1.2 / ≤1.5 ⚠️ Q4 超标 |
|
||||
| 覆盖计划运行期校验 | — | 0.42 s | — | 4.80 s(15 m/72 一次性) | — |
|
||||
| 细网格证书缺口(15 m/72) | — | 0(解析) | 566 | **0** | 0 ✅ |
|
||||
| 有效局 | 18/18 | 20/20(含边界) | 9/9 | 11/11(含边界) | 100% ✅ |
|
||||
|
||||
**结论**:本轮的硬收益是**第四问后备盲清被消除**(125.7 → 4.3 次/局,最坏局
|
||||
10890.8 → 9630.4 s)与**覆盖证书的正确性**(细网格缺口 566 → 0);虚拟时间只有
|
||||
0.7–0.9% 的均值改善(中位 −2.0%),**远未达到方案 §6.3 的目标**。
|
||||
第三问的改善落在噪声内(中位相对变化 0.0005)。§2 的成本地板估计(Q3 可压缩
|
||||
10–14%、Q4 23–32%)**过于乐观**,实测只回收了约 1%。
|
||||
|
||||
### 8.3 定向源探点(D1):先负后正,最终只保留"可见 + 就近"
|
||||
|
||||
D1 的完整实现(期望后验 MEC 半径值函数 + 抵近点 + 遮挡点剔除)在**修正联合
|
||||
信念朝向规则之前**配对为:后备 125.7 → 53.8 次,但探测移动 +5.6 km,虚拟时间
|
||||
8676 → 9411 s(净负)。原因有两个:
|
||||
|
||||
1. 后备清除在命中真源时**立即结束**,"整条路径最短"不是它的目标函数,
|
||||
§3.2/§3.3 的插入代价模型在这里并不成立;
|
||||
2. 更关键的是:**联合信念的 `no_signal` 朝向规则被写反了**(§8.4),
|
||||
`prob_detect` / 期望半径因此系统性地高估"被遮挡点可见",选点必然跑偏。
|
||||
|
||||
修正后重测(9 场景 core):
|
||||
|
||||
| `probe_score_mode` | λ | 后备次数 | T 均值 | 移动 | 计算墙钟 |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: |
|
||||
| `visibility`(旧) | — | 125.6 | 9224.6 s | 33595 m | 11.6 s |
|
||||
| `expected_radius` | 0.05 | 6.8 | 9740.5 s | 38824 m | 9.6 s |
|
||||
| `expected_radius` | 5.0 | 4.3 | 8604.3 s | 32893 m | 3.7 s |
|
||||
| `nearest_visible`(默认) | — | **4.3** | **8602.0 s** | 32891 m | 3.4 s |
|
||||
|
||||
λ 越大越优说明本题的探点问题里"**看得见且少走路**"比"单步信息量最大"更重要;
|
||||
λ→∞ 的极限(可见候选中移动最省)就是默认口径。值函数代码与其对拍测试保留,
|
||||
供后续研究(例如配合真正的多步 rollout)。
|
||||
|
||||
### 8.4 本轮发现的正确性缺陷(已修复)
|
||||
|
||||
`belief/joint.py` 的 no_signal 朝向裁剪**规则反向**:`keep=False` 分支用了
|
||||
"确定覆盖"掩码做交集,等价于保留"必然看得见"的档、删掉"确定看不见"的档;
|
||||
`keep=True` 分支又漏计了格内角裕量 `asin(margin/max(dist−margin,1))`。
|
||||
两者都会删掉真源的朝向档(`tests/test_joint_belief.py::test_true_source_survives_consistent_observations`
|
||||
在随机观测序列上直接复现)。修复后:
|
||||
`keep=False` 改用 `_far_mask`(与 `_cover_mask` 互补)并把距离阈值收紧为
|
||||
`r_eff_min − margin`,`keep=True/False` 都计入格内角裕量。
|
||||
|
||||
**修复本身是净成本**:在旧 `visibility` 探点口径下 Q4 core 由 8676 → 9225 s;
|
||||
换成 §8.3 的新探点口径后回落到 8602 s,同时后备清除降 96.6%。
|
||||
换言之,本轮第四问的成绩改善**主要来自"修好信念 + 换探点口径"这一组合**,
|
||||
而不是 §3 预期的插入/排线优化。
|
||||
|
||||
### 8.5 覆盖证书硬化(S2)
|
||||
|
||||
现役 `q4_mincover_v1`(31 点)在粗采样(75 m/24)下缺口 0,但细网格复检:
|
||||
|
||||
| 点集 | 点数 | 25 m/48 缺口 | 20 m/72 缺口 | 15 m/72 缺口 | 最坏命中 | NN 巡回 |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
|
||||
| `q4_mincover_v1`(旧) | 31 | 125 | 336 | 566 | 1000.0 m | 22.9 km |
|
||||
| `q4_mincover_v2`(默认) | 31 | **0** | **0** | **0** | **990.0 m** | 23.9 km |
|
||||
| 严格连续空间证书备选 | 36 | 0 | 0 | 0 | 973.7 m | 25.4 km |
|
||||
|
||||
`q4_mincover_v2` 的生成方式:搜索分辨率 50 m/48 档、设计裕量 `r_design=965 m`、
|
||||
前向角裕量 5°,再做**证书分辨率(15 m/72)精细剪枝**(每次删点都用同一证书口径
|
||||
复核),因此点数与旧计划相同而缺口归零,**没有付出点数代价**。
|
||||
|
||||
> 严格连续空间界需要 `r_verify + 15·√2/2 ≤ 1000`(即 ≤989.4 m)且角裕量 ≥2.5°;
|
||||
> 满足该条件的 36 点方案最坏命中 973.7 m,但代价 +5 点(约 +1000 s),
|
||||
> 已落盘为 `output/q4/cover-certificate-v3.json` 备用。默认方案在 15 m/72
|
||||
> 采样下最坏 990.0 m,与严格界的差 0.65 m 属采样边界效应,作为已知残余风险登记。
|
||||
|
||||
### 8.6 负面结论与复现命令
|
||||
|
||||
| 优化项 | 实测 | 结论 |
|
||||
| --- | --- | --- |
|
||||
| S4/D3 后备清除 Or-opt | 后备失败 125.7 → 161.4 次(T 8676 → 8940 s) | ❌ 目标函数错位:命中即停,路径最短反而把真源推后 |
|
||||
| S5/T1 Held-Karp 精确任务序 | 3203.2 s vs 2-opt 3193.1 s(18 场景) | ❌ 滚动时域下"当前任务表逐位最优"反而略差;默认 0 |
|
||||
| `route_aware_probe` | 3210.3 s vs 关闭 3193.1 s | ❌ 权重 1e-3 的路线项无净收益;默认关闭 |
|
||||
| Q4 融合巡回(站点顺带定位) | 12711 s vs 10809 s | ❌ 与 P0-1 在第三问的负面结论一致 |
|
||||
| `opportunistic_radius_ratio` | 0.65 → 0.5:3210.3 → 3205.4 s | ✅ 采纳(弱收益) |
|
||||
|
||||
```bash
|
||||
# S1 度量基线(含路由 gap / MST 下界 / 作业点/静默率)
|
||||
uv run python scripts/run_suite.py --problem 4 --suite core --jobs 6 \
|
||||
--variants directional --label q4-baseline --out output/suite
|
||||
uv run python scripts/run_suite.py --problem 3 --suite core --jobs 6 \
|
||||
--variants bayes --label q3-baseline --out output/suite
|
||||
|
||||
# S2 覆盖证书(生成 + 细网格校核;--fine-prune 走证书分辨率剪枝)
|
||||
uv run python scripts/verify_q4_cover.py --spacing 100 --radius 2850 \
|
||||
--target-step 50 --headings 48 --design-radius 965 --heading-margin 5 \
|
||||
--verify-step 15 --verify-headings 72 --fine-prune \
|
||||
--write-plan q4_mincover_v2 --out output/q4/cover-certificate-v2.json
|
||||
|
||||
# S3/D1 口径配对(可见性/值函数/极限三档)
|
||||
uv run python scripts/run_suite.py --problem 4 --suite core --jobs 6 \
|
||||
--variants "directional#search.probe_score_mode=visibility,\
|
||||
directional#search.probe_score_mode=expected_radius;search.probe_lambda_move_s=5,\
|
||||
directional#search.probe_score_mode=nearest_visible" --label q4-probe-ab
|
||||
|
||||
# 回归
|
||||
uv run pytest -q # 268 项
|
||||
```
|
||||
|
||||
### 8.7 仍未达标项的处置建议
|
||||
|
||||
| 未达标 | 差距 | 建议 |
|
||||
| --- | --- | --- |
|
||||
| Q3 ≤2900 s | −9% | 已无低风险空间:七站巡回 1392 s + 覆盖检测 501 s + 每源 ≥2 次探测是成本地板;需要改停止条件或覆盖语义才能继续下降 |
|
||||
| Q4 ≤6500 s | −24% | 31 点巡回 + 缺席检测 ≈5566 s 是地板,与 §2.3 一致;只能改"逐频道缺席证明"口径 |
|
||||
| `route_gap` Q4 24.3% | — | 该指标在"作业点数随决策变化"时口径不稳(本方案把后备格换成探测点后作业点从 206 降到 110);建议改为"覆盖骨架段"与"临时任务段"分别报 gap |
|
||||
| Q4 计算墙钟 4.1 s(目标 1.5 s) | 2.7 s | 热点在 `belief` 凸多边形与 `select_measurement_point`(D1 之前的基线即 5.3 s),属 P0-4b 表示层张量化,与本方案无关 |
|
||||
| 严格连续空间覆盖证书 | 0.65 m | 若要正式采用 36 点严格方案:`--verify-radius 989 --verify-heading-margin 2.5`,代价约 +1000 s |
|
||||
@@ -0,0 +1,595 @@
|
||||
# 第三、四问系统性架构优化与运行提速方案
|
||||
|
||||
- 版本:2026-09-12
|
||||
- 依据:本仓库运行历史(`session.jsonl`、`practice-p3-*.jlog`、`referee-data/sessions/*`、
|
||||
`output/hrl/ui/q3-main/*`、`docs/` 全部性能记录)+ 本文档新增的复跑与离线校核实验
|
||||
- 范围:第三问(全向源)与第四问(混合定向源)的**闭环架构、算法分解、运行治理**,
|
||||
不改动题面约束、协议层语义与既有保守性不变量
|
||||
- 结论先行:**虚拟时间的 75–83% 是移动**,移动里约一半不是覆盖巡回而是定位/清除往返;
|
||||
**第四问的全部失败来自定向源**;旧机器学习层已被证明不如手写基线。
|
||||
因此本轮优化的主线是"**把定位并入覆盖巡回 + 把发现集压到最小命中集 + 给定向源建位置×朝向联合信念**",
|
||||
而不是再堆学习算法。
|
||||
|
||||
---
|
||||
|
||||
## 0. 摘要(TL;DR)
|
||||
|
||||
### 0.1 三个待解的结构性浪费
|
||||
|
||||
| 编号 | 现象 | 直接证据 | 估计可回收 |
|
||||
| --- | --- | --- | --- |
|
||||
| W1 | Q3 定位探点往返 5741 m / 1148 s,与覆盖巡回(6960 m / 1392 s)相当 | 本次复跑(13 源,seed 0):24 次探点,均值 442 m/次 | **−900 ~ −1100 s/局** |
|
||||
| W2 | Q4 发现集是 700 m 方格 49 点,巡回 36.4 km;同一保证下贪心最小命中集只要 31 点、22.9 km | `scripts/verify_q4_cover.py`:43 032 个 (p,h) 采样,两组点集均 100% 覆盖 | **−2700 s(巡回)+ −1140 s(检测)** |
|
||||
| W3 | Q4 定向源没有"位置×朝向"联合信念,只能阶梯探点,最后退化到 ≤400 格盲清 | referee 两局:全向 17/17 清除,定向仅 2/6;4/4 漏检全是定向 | **−1500 ~ −3000 s/局** |
|
||||
|
||||
### 0.2 预期收益(自建模拟器,同一冻结基准集,配对口径)
|
||||
|
||||
| 指标 | Q3 现状 | Q3 目标 | Q4 现状 | Q4 目标 |
|
||||
| --- | ---: | ---: | ---: | ---: |
|
||||
| 平均虚拟时间 | 3707 s(本次 6 场景)/ 3536 s(13 源单局) | **≤ 2700 s** | 13 652 s(10 源 3 定向单局)/ 13 165 s(4 场景均值) | **≤ 9000 s** |
|
||||
| 最大虚拟时间 | 4628 s | **≤ 4000 s** | > 17 000 s(边界压力) | ≤ 12 000 s |
|
||||
| 每局计算墙钟 | 2.9–6.5 s | **≤ 0.8 s** | 6.5 s | **≤ 1.0 s** |
|
||||
| 有效局(全清+缺席无误判+零异常+正常退出+计时复算一致) | 6/6 | 基准集 100% | 4/4 | 基准集 100% |
|
||||
|
||||
> 4000 s 是本项目 `docs/算法审核与优化-20260911.md` 自设的验收线,不是题面评分线;
|
||||
> 第四问在 10 源场景下**不承诺**达成 4000 s,理由见 §6.2。
|
||||
|
||||
### 0.3 明确不做的事
|
||||
|
||||
- 不重新引入 PPO/HRL。运行历史支持"保留手写基线":HRL 训练 4007.9 s / 109 776 步后
|
||||
仍被 `bayes` 稳定压制 4.3–5.0%(17–18/20 场景,符号检验 p=0.0001),且 94% 的差距来自
|
||||
多走的移动;其评估集 20 个场景**全部是单源全向**,无法支撑任何第四问结论。
|
||||
- 不引入新的第三方运行时依赖(仍是 `requests`;`numpy` 仅作**可选加速后端**,缺失时回退纯 Python)。
|
||||
|
||||
### 0.4 实施进展与实测回填(2026-09-12)
|
||||
|
||||
冻结基准集 `harness/suites.py`(第三问 18 主场景 + 2 边界;第四问 9 + 2),
|
||||
并行 runner `scripts/run_suite.py`,配对口径同场景同误差种子。
|
||||
|
||||
| 项 | 状态 | 实测 | 结论 |
|
||||
| --- | --- | --- | --- |
|
||||
| P1-4 安全阀 | ✅ 已实现 | `summary` 新增 `completion_basis` / `absence_evidence` / `redundant_rescan_measures` / `virtual_reserve_s`;`--persistent` 输出单轮上界告警 | 采纳(5 项测试) |
|
||||
| P0-2 最小命中集 | ✅ 已实现并默认开启 | 第四问 11 场景配对:**14003.0 → 10975.5 s**(均值 −19.3%、中位 −25.4%、10/11 更快);检测 444.5→319.2;请求 569.5→395.6;位移 54.9→44.2 km;最大 17007→12919 s;11/11 valid | **采纳** |
|
||||
| P0-4 感知层向量化 | ✅ 已实现 | 单局计算 **3.10 → 1.00 s(3.08×)**,最大 5.84 → 1.30 s;20 场景的虚拟时间与动作序列**逐位一致** | **采纳**(目标 ≤0.8 s 未达,余下为 belief 表示层) |
|
||||
| P0-1 融合巡回 | ❌ 实测无效,已回退默认 | 仅追加站点方位线:3454.6 vs 3426.9 s(+0.8%,检测 +10.8 次、位移仅 −188 m);追加 + 抑制专用探点:**4989.0 vs 3426.9 s(+45.6%,20/20 更慢)** | 关闭(保留开关与代码) |
|
||||
| P1-1 C₂₀ 最近清除点 | ⊘ 复核后降为非项 | 现有 `_near_clear_target` 已取安全球 `B(c, limit−ρ)` 内离当前位置最远点;与完整 C₂₀ 最优之差 ≤ slack+margin ≈ 0.74 m | 不改 |
|
||||
| P1-3 基准集/并行 | ✅ 已实现 | 20 场景 × 3 变体 = 60 局 **26.5 s**(6 进程);20 局 **3.9 s** | 采纳 |
|
||||
| P0-3 联合信念 | ✅ 已实施并于 2026-09-12 修正 | 网格粒子 × 24 档朝向 bin + 可见性选点;**发现并修复 `no_signal` 朝向规则反向缺陷**(原会删掉真源朝向档);配套 D1 探点口径使第四问后备盲清 **125.7 → 4.3 次/局** | 采纳(结论与实测见[图论与 DP 优化方案 §8](第三四问图论与动态规划优化方案.md)) |
|
||||
|
||||
**P0-1 为什么失败(重要负面结论)**:七站环上通常只有 2–3 个站能对同一源取得
|
||||
有效方位线,且站间交会角不足以直接换到 20 m 证书,专用信息增益探点仍然必要;
|
||||
"顺路测量"于是退化为平白多花检测,一旦抑制专用探点则会白等巡回、远端绕行。
|
||||
**结论:第三问的定位往返不是可回收的浪费,`bayes` 的滚动时域调度已在做正确的事。**
|
||||
|
||||
**第四问现状与目标差距(2026-09-12 图论/DP 轮回填)**:启用硬化覆盖计划
|
||||
`q4_mincover_v2` 与 D1 探点口径后,第四问 11 场景(core + boundary)均值为
|
||||
**9069.9 s**、最大 12081.1 s、后备盲清 24.8 次/局(core 9 场景 8602.0 s /
|
||||
4.3 次),仍远高于 4000 s 验收线。剩余成本主要是最小命中集巡回
|
||||
(23.9 km ≈ 4777 s)与缺席频道检测,二者都是覆盖证明的固有代价;
|
||||
本方案 §2.3 的"可压缩 23–32%"在本模拟器上只回收了约 1%(详见图论与 DP 方案 §8)。
|
||||
|
||||
---
|
||||
|
||||
## 1. 运行历史复核
|
||||
|
||||
### 1.1 数据资产清单(本次实际读取)
|
||||
|
||||
| 资产 | 内容 | 可用信息 |
|
||||
| --- | --- | --- |
|
||||
| `session.jsonl`(31 011 行) | 官方第三问演练 J2WH-2K7D-5ZXS-DNK6 的客户端日志 | 10 323 次 `/measure`、12 次 `/clear`、逐动作计时 |
|
||||
| `practice-p3-*.jlog` | 同一局的官方行为日志封装 | 仅明文信封(题号 3、队号、案例码);载荷 AES-256-GCM + RSA-OAEP,不可解 |
|
||||
| `referee-data/sessions/*`(6 局,5 局完整) | 本地评测机 q3×3、q4×2、截断×1 | 逐请求响应、真值、summary、step 事件 |
|
||||
| `output/hrl/ui/q3-main/` | HRL 训练(109 776 步)+ `eval/at-40k`、`eval/at-final` | monitor CSV、模型元数据、20 场景配对评估 |
|
||||
| `docs/算法审核与优化-20260911.md`、`docs/algo-3.md`、`docs/algo-3-bayes.md` | 历史 KPI 与消融 | 官方/自建成绩、验证集分档 |
|
||||
| 本次新增复跑 | `scripts/benchmark_q3_bayes.py`(6 场景)、Q3/Q4 单局计时分解、`cProfile`、站点三角化可行性 | §1.3 中标注"本次复跑"的数字 |
|
||||
| 本次新增工具(已入库) | `scripts/analyze_run_timing.py`(任意会话日志的计时分相复算)、`scripts/verify_q4_cover.py`(第四问覆盖证书 + 最小命中集搜索) | F4、F9 可复现 |
|
||||
|
||||
### 1.2 先确认度量可信(否则一切优化都是自欺)
|
||||
|
||||
官方计时模型:`T = L/5 + 5·N_measure + N_switch + 5·N_clear⁺ + 3·N_clear⁻`,
|
||||
其中 `L` 为累计直线位移,`/clear` **不计**换频。`scripts/verify_q3_practice.py` 对 5 局完整
|
||||
评测机日志逐微秒复算,与服务器返回的 `virtual_time_s` 完全一致(例:BBM9 复算 3134.642907 = 记录 3134.642907)。
|
||||
结论:**后续所有"虚拟时间"结论都可用动作日志独立复算**,这是本项目最可靠的一条工程资产,必须保留。
|
||||
|
||||
### 1.3 关键事实(F1–F13)
|
||||
|
||||
| 编号 | 事实 | 出处 |
|
||||
| --- | --- | --- |
|
||||
| F1 | 虚拟时间构成:**移动 75–83%|检测动作 15–21%|换频 2.3–3.7%** | 官方会话 82.30/14.76/2.94;referee q3 80.50/16.78/2.71、q4 78.39/18.63/2.98;本次复跑 Q3 79.9/17.3/2.9、Q4 75.2/21.1/3.7 |
|
||||
| F2 | 官方第三问演练 J2WH 总虚拟 350 227.7 s,其中 **346 169.9 s(98.84%)是空复扫**:7 个固定站点 × 8 个未清除频道 × 183 轮,0 检出,结束点正好落在 `virtual_reserve_s=10 000` 边界 | `session.jsonl` 相位切分;`planner/__init__.py:236`、`config.py:99` |
|
||||
| F3 | **第四问的全部失败来自定向源**:referee 两局全向 17/17 清除,定向仅 2/6;4/4 漏检全是定向;每清除源 356.4 s(Q3 274.0 s,+30%) | `referee-data/sessions/{6X58,BAU8}`;`{BBM9,RM7Y,VQKB}` |
|
||||
| F4 | 本次复跑 Q3(13 源,seed 0):3535.9 s / 109 次检测 / 13 清除;**七站巡回 6960 m(1392 s,39%)+ 探点往返 5741 m(1148 s)+ 清除移动 3668 m(734 s)** | 本次复跑(方法见附录 A) |
|
||||
| F4b | 评测机 Q3 单局 BBM9:**21 次专用探点占 1138.7 s 移动,等于该局虚拟时间的 36.3%**,而站点扫频 98 次只占 991.2 s | `scripts/analyze_run_timing.py referee-data/sessions/BBM9-.../log.jsonl --plan seven` |
|
||||
| F5 | 本次复跑 Q4(10 源 3 定向,seed 0):13 652.1 s / 560 次检测 / 18 次清除(8 次失败)/ 51 356 m | 本次复跑 |
|
||||
| F6 | 感知层是墙钟热点:单局计算 6.04 s;`cProfile` 下 `expected_survival` 占累计时间 **72%**(2808 次调用),`apply_measure` ≈ 20%,`_kill_outside_wedge` 触发 498 万次 `angular_gap` | 本次 `cProfile`(13 源单局) |
|
||||
| F7 | HRL 运行:109 776/200 000 步、4007.9 s、**27.0 steps/s(6.75/env)**、1.51 s 墙钟/局;被 SIGTERM 截断时回报仍在上升(Q1 1.05 → Q4 6.20) | `output/hrl/ui/q3-main/q3.json` + 4 份 monitor CSV |
|
||||
| F8 | HRL 评估:20 场景**全部单源全向**;`bayes` 比 HRL 快 4.3–5.0%(17–18/20,p=0.0001),**94% 的差距是多走的移动**;40k→109k 步仅再改善 0.7% | `eval/at-40k|at-final/aggregate.*`、`eval/manifest.json` |
|
||||
| F9 | 现役 Q4 发现集:700 m 方格 49 点、NN 巡回 **36.4 km**;满足同一"前向半盘命中"保证的贪心最小集 **31 点、22.9 km(−37%)**,覆盖校核 100% | `scripts/verify_q4_cover.py`;本文附录 A |
|
||||
| F10 | Q3 七站巡回**已经最优**:6 × 1159.7 = 6958 m(观测 6960 m),无可压缩空间;把环半径降到覆盖下界 1122.9 m 仅省 222 m(44 s) | 几何计算 + 本次复跑 |
|
||||
| F11 | 站点三角化可行:**99.28% 的源位置有 ≥2 个站在 1500 m 内**;最佳交会角 5 分位 58.5°、中位 122.9°;只有 0.72% 的源位置仅 1 个站在射程内 | 本次离线采样(20 000 点) |
|
||||
| F12 | 墙钟量级差三个数量级:内存回放 **0.22–0.25 ms/请求**(≈0.35 s/局),官方接口 **9.42 ms 均值/请求**(104.49 s / 10 337 请求);本地评测机 24.5–97.9 ms/请求 | referee `session_end`、`runs/*/summary.json`、`session.jsonl elapsed_s` |
|
||||
| F13 | 历史 KPI 参照:Q3 官方最好 4058.71 s(3BW5,13/13)、5232.13 s(J4ER,12/12);自建 `bayes` 3707.3 s(本次 6 场景)/ 3726.2 s(14 场景消融)/ 验证集 3315.89 s;Q4 自建 13 164.7 s,**无官方第四问演练记录** | `docs/算法审核与优化-20260911.md`、`docs/algo-3-bayes.md`、`README.md:135` |
|
||||
|
||||
### 1.4 由事实导出的六处结构性瓶颈
|
||||
|
||||
| 编号 | 瓶颈 | 支撑事实 | 归属层 |
|
||||
| --- | --- | --- | --- |
|
||||
| B1 | 定位/清除移动没有并入覆盖巡回,探点按"信息增益"选,移动权重 λ=0.0105 几乎不惩罚绕行 | F1、F4、F4b、F11 | 决策层 |
|
||||
| B2 | Q4 发现集冗余 63%(49 点 vs 30 点),且每个"真缺席"频道要在全部 49 点各测一次 | F5、F9 | 保证层 |
|
||||
| B3 | Q4 定向源缺少位置×朝向联合信念:`no_signal` 只记"被遮挡过几次",选点靠固定 45° 阶梯,收敛失败后退化到 ≤400 格 20 m 盲清(≤1200 s + ≤400 请求) | F3、`planner/__init__.py:691-768` | 感知层 + 决策层 |
|
||||
| B4 | 感知层全 Python 容器:每局 20 个频道各建 ~6.1 万格 `set`,`_kill_outside_wedge` 每次遍历全部幸存格,评分核 `expected_survival` 为 7×800 的纯 Python 双循环 | F6 | 感知层 |
|
||||
| B5 | 研发吞吐低:场景串行、单进程;一局 0.35–6.5 s;HRL 27 steps/s;14–52 场景的配对基准要跑几分钟到十几分钟 | F6、F7、F12 | 实验层 |
|
||||
| B6 | 运行治理缺失:单源评估集使指标退化(`avg_locate_clear_time_s ≡ virtual_time_s`)、`eval/manifest.json` 三份重复且与 `output/hrl/ui/manifest.json` 不一致(后者缺失)、`persistent_search=True` 可造成 98.84% 的无效开销且无硬性告警 | F2、F8、F13 | 遥测/实验层 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 目标架构
|
||||
|
||||
### 2.1 分层与依赖方向(依赖只能向下)
|
||||
|
||||
```
|
||||
┌───────────────────────────────────────────────┐
|
||||
L6 实验/遥测 │ bench/ 冻结基准集·并行 runner·配对统计 │
|
||||
│ telemetry/ manifest·step log·replay·summary │
|
||||
└───────────────▲───────────────────────────────┘
|
||||
│ 只读观测
|
||||
┌───────────────┴───────────────────────────────┐
|
||||
L5 执行/安全 │ exec/ 动作账本·预算守卫·安全阀·幂等客户端 │
|
||||
└───────────────▲───────────────────────────────┘
|
||||
│ Action 请求
|
||||
┌───────────────┴───────────────────────────────┐
|
||||
L4 决策 │ plan/ 任务表·滚动时域排序·信息/时间评分 │
|
||||
└───────────────▲───────────────────────────────┘
|
||||
│ Task[]
|
||||
┌───────────────┴───────────────────────────────┐
|
||||
L3 保证 │ guarantee/ 命中集覆盖计划·清除证书·缺席证明 │
|
||||
└───────────────▲───────────────────────────────┘
|
||||
│ 候选集/幸存集
|
||||
┌───────────────┴───────────────────────────────┐
|
||||
L2 信念 │ belief/ BeliefBackend:OccupancyTensor(Q3) │
|
||||
│ JointTensor(位置×朝向, Q4) │
|
||||
└───────────────▲───────────────────────────────┘
|
||||
│ Observation
|
||||
┌───────────────┴───────────────────────────────┐
|
||||
L1 观测模型 │ model/ 可见性半平面·r_eff 带·有界误差·舍入裕量 │
|
||||
└───────────────▲───────────────────────────────┘
|
||||
│ 原始响应
|
||||
┌───────────────┴───────────────────────────────┐
|
||||
L0 协议/时钟 │ protocol/ 规则表·计时模型·状态机·客户端(冻结)│
|
||||
└───────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
与现状的差别:**用组合取代继承**。现状是
|
||||
`OmnidirectionalLoop → SevenBatch → BayesLoop → DirectionalExtension` 的单继承链,
|
||||
调度、信念、清除、预算、报表五种职责混在同一族类里(`planner/__init__.py` 818 行 +
|
||||
`planner/bayes_loop.py` 513 行),任何一处改动都会跨层影响其它三问。
|
||||
目标形态是
|
||||
|
||||
```python
|
||||
Episode(
|
||||
client = SimulatorClient(...), # L0,冻结
|
||||
belief = OccupancyTensor(...) | JointTensor(...), # L2
|
||||
plan = CoveragePlan.points, # L3
|
||||
scorer = InfoGainPerSecond(...), # L4
|
||||
policy = FusedTourPolicy(...) | DirectionalPolicy(...), # L4
|
||||
certifier= Certificate(...), # L3
|
||||
ledger = Telemetry(...), # L6
|
||||
)
|
||||
```
|
||||
|
||||
三层保证策略矩阵只差**三个可替换部件**,其余代码共享:
|
||||
|
||||
| 问题 | 信念后端 | 覆盖计划 | 选点策略 |
|
||||
| --- | --- | --- | --- |
|
||||
| Q3 | `OccupancyTensor`(全向) | 七站环(已经最优,冻结) | 融合巡回 |
|
||||
| Q4 | `JointTensor`(位置×朝向) | **最小命中集**(30 点,离线搜索 + 运行期校验证书) | 可见性约束选点 |
|
||||
|
||||
### 2.2 接口契约(关键签名)
|
||||
|
||||
```python
|
||||
# L1 model/observation.py —— 只此一处定义"什么算看见"
|
||||
@dataclass(frozen=True)
|
||||
class ObservationModel:
|
||||
r_eff_min_m: float; r_eff_max_m: float
|
||||
near_radius_m: float; half_angle_deg: float
|
||||
directional_half_deg: float = 90.0 # 含边界
|
||||
|
||||
def visible(self, src: Vec2, heading_deg: float | None, reader: Vec2) -> bool: ...
|
||||
def detectable(self, src: Vec2, heading_deg: float | None, reader: Vec2) -> bool: ...
|
||||
|
||||
# L2 belief/base.py —— 两种后端实现同一协议
|
||||
class BeliefBackend(Protocol):
|
||||
def apply(self, channel: int, reader: Vec2, result: str,
|
||||
svd_deg: float | None) -> None: ...
|
||||
def alive(self, channel: int) -> int: ...
|
||||
def is_absent(self, channel: int) -> bool: ... # 幸存集为空
|
||||
def certificate(self, channel: int) -> ClearCertificate | None: ...
|
||||
def guaranteed_no_signal(self, channel: int, reader: Vec2) -> bool: ...
|
||||
def prob_detect(self, channel: int, reader: Vec2) -> float: ... # 决策用
|
||||
def version(self, channel: int) -> int: ... # 派生量缓存键
|
||||
|
||||
# L3 guarantee/cover.py —— 覆盖计划是"可离线搜索、可在运行期校验"的一等对象
|
||||
@dataclass(frozen=True)
|
||||
class CoveragePlan:
|
||||
problem: int # 3 | 4
|
||||
points: tuple[Vec2, ...]
|
||||
certificate: CoverCertificate # 采样分辨率、缺失数、最坏裕量、脚本哈希
|
||||
version: str
|
||||
def verify(self, model: ObservationModel, tol_m: float) -> CoverCertificate: ...
|
||||
|
||||
# L4 plan/policy.py
|
||||
class Policy(Protocol):
|
||||
def tasks(self, belief: BeliefBackend, state: RobotState) -> list[Task]: ...
|
||||
def order(self, tasks: list[Task], state: RobotState) -> list[Task]: ...
|
||||
def measured_at_point(self, point: Vec2) -> tuple[int, ...]: ... # 一停多测
|
||||
```
|
||||
|
||||
### 2.3 四条设计原则
|
||||
|
||||
1. **保守性不变量不动**:真源永不被删;清除证书始终基于完整 ±1.005° 角域与题面距离带;
|
||||
所有启发式(均匀先验、朝向先验、信息增益)**只用于选点,不用于安全证明**。
|
||||
Q4 的朝向联合信念必须自带"朝向未观测时退化为全向"的保守分支。
|
||||
2. **保证 = 离线搜索 + 运行期校验**:覆盖集、清除点、巡回顺序都可以离线用数值搜索优化,
|
||||
但运行期必须用同一份 `ObservationModel` 重新校验证书(点集、分辨率、裕量),
|
||||
校验失败即拒绝启动,而不是相信一个写死的点表。
|
||||
3. **一个事实一个来源**:题面常量、`r_eff` 带、清除半径、动作耗时只在 `config.py`;
|
||||
可见性语义只在 `model/observation.py`;训练/部署若将来共存,必须共用同一控制器模块
|
||||
(这是旧 HRL 用 13 000 行换来的教训:v1 的 5 个特征索引错位、BC 激活函数不一致、
|
||||
旧基准把 `completed` 当全清)。
|
||||
4. **每个优化都必须可配对、可复算、可回归**:冻结基准集 + 逐局 `manifest` + 动作日志
|
||||
+ 逐微秒复算;任何"更快"的声明必须给出配对分布(均值/中位/最大/更快场数),
|
||||
而不是单个最优场景。
|
||||
|
||||
### 2.4 迁移映射(现状 → 目标)
|
||||
|
||||
| 现状 | 目标 | 处理 |
|
||||
| --- | --- | --- |
|
||||
| `protocol/*`、`evaluation/recompute_virtual_time` | 不变 | 冻结,作为度量基准 |
|
||||
| `belief/__init__.py`(凸外包) | `belief/convex.py` | 原样搬迁,继续负责精确角域证书 |
|
||||
| `particles.py`(网格幸存集) | `belief/occupancy.py`(numpy 张量 + 纯 Python 回退) | 重写热核,语义不变,保留 `tests/test_particles.py` 的真源保留性质测试 |
|
||||
| — | `belief/joint.py`(位置×朝向) | 新增,Q4 专用 |
|
||||
| `coverage/` | `guarantee/cover.py` | 49 点常量表 → 可校验的 `CoveragePlan` |
|
||||
| `planner/bayes_loop.py` 的 `_build_tasks/_order_tasks` | `plan/tasks.py`、`plan/schedule.py` | 抽出,Q3/Q4 共用 |
|
||||
| `planner/bayes_loop.py::_select_point` | `plan/score.py` | 抽出 + 向量化评分 |
|
||||
| `planner/__init__.py::DirectionalExtension` | `plan/policy.py::DirectionalPolicy` + `belief/joint.py` | 保留后备清除作为最终兜底,但目标是不再触发 |
|
||||
| `planner/seven.py` | `guarantee/plans.py::SEVEN` | 保留解析证明 |
|
||||
| 4 个 benchmark 脚本 | `bench/` 统一 runner(并行 + manifest + 配对统计) | 合并,消除指标口径分叉 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 优化项
|
||||
|
||||
### P0-1 第三问:发现—定位融合巡回(消除探点往返)
|
||||
|
||||
**现状**:每次收到 `direction` 后进入独立定位子循环,由信息增益选点器在同一站点之外
|
||||
另选探点;13 源单局共 24 次探点、5741 m 往返、1148 s,与整个覆盖巡回(6960 m / 1392 s)同量级;
|
||||
评测机单局 BBM9 更极端:**21 次探点花掉 1138.7 s 移动 = 全局限时时间的 36.3%**(F4b)。
|
||||
评分函数 `score = E[log 幸存占比] + 0.0105 × 移动秒数`,移动项几乎不影响排名。
|
||||
|
||||
**方案**:把"已发现未定证"的频道加入**每一个覆盖站点**的批量扫频清单,
|
||||
使第二/第三条方位线在巡回途中顺路取得;只有站点三角化不成立时才回退到专用探点。
|
||||
|
||||
- 巡回仍由 `plan/schedule.py` 滚动排序(现有最近邻 + 2-opt 已足够,`bayes` 的 −39.5% 移动
|
||||
主要来自这里),新增"同点顺路测量"任务类型:`Task(kind="station", channels=(未知, 未定证))`。
|
||||
- 站点任务的固有耗时按 `6 s × 频道数 + 1 s × 换频次数` 记账(现有 `_path_cost` 已支持)。
|
||||
- 保底:`F11` 显示 0.72% 的源位置只有 1 个站在 1500 m 内,对这类频道保留
|
||||
`_select_point` 的专用探点路径;判定用 `belief.prob_detect` 而非硬编码。
|
||||
|
||||
**预期收益(已被实测推翻)**:原估 F4 的 1148 s 探点移动可回收 70–80%,
|
||||
即 −800 ~ −900 s。**实测结果相反**:仅追加站点方位线 +0.8%,追加并抑制专用探点
|
||||
+45.6%(20/20 场景更慢),见 §0.4。原因是站间交会角不足以保证 20 m 证书,
|
||||
专用信息增益探点仍然必要;抑制它只会白等巡回。**本项不采纳,默认关闭。**
|
||||
|
||||
**验收**:18 场景验证集平均 ≤ 2700 s、最大 ≤ 4000 s;`measures` 增幅 ≤ 20%;
|
||||
`move_distance_m` 降幅 ≥ 25%;全部 `valid`。
|
||||
|
||||
**风险**:站点距离最远 2 × 1159.7 = 2319 m,可能出现 `no_signal` 浪费 5 s。
|
||||
缓解:仅在 `prob_detect > 0` 时顺路测量;`no_signal` 同样是有效负观测(进信念,不算异常)。
|
||||
|
||||
---
|
||||
|
||||
### P0-2 第四问:用最小命中集替换 49 点发现集(离线搜索 + 运行期校验)
|
||||
|
||||
**现状**:`coverage_points()` 生成 700 m 方格 49 点(±2100),依据是"单元内任一点到 4 顶点
|
||||
≤ 700√2 ≈ 989.95 ≤ 1000"。该条件是**充分但远非必要**:它要求单元内任意位置、任意朝向都由
|
||||
**同一个**顶点命中。实际上,第四问的保证是"对 ∀(p,h) ∈ Ω×S¹,存在被访点位于其前向半盘内",
|
||||
允许由**多个**顶点共同围住 p。
|
||||
|
||||
**方案**:把它当作一个显式的**击中集(hitting set)问题**离线求解,运行期校验:
|
||||
|
||||
1. 目标空间离散:`p` 取 Ω 内 75 m 网格,`h` 取 24 个方向 → 43 032 个 (p,h) 单元;
|
||||
2. 候选点:半径 2850 m 圆盘内 200–300 m 网格(641/293 个候选);
|
||||
3. 贪心集合覆盖 + 局部改进,**同时把巡回长度作为次级目标**(641/293 个候选中取 31/30 个,
|
||||
仍有大量同覆盖度的等价解可用于缩短巡回);
|
||||
4. 生成 `CoveragePlan(points, certificate)`,`certificate` 记录采样分辨率、未覆盖数、
|
||||
最坏裕量、生成脚本哈希;`CoveragePlan.verify()` 在每次运行前用同一 `ObservationModel`
|
||||
重新校核,未通过即拒绝启动。
|
||||
|
||||
**本次离线校核结果**(附录 A 可复现):
|
||||
|
||||
| 点集 | 点数 | (p,h) 覆盖 | NN 巡回 | 折算移动 |
|
||||
| --- | ---: | ---: | ---: | ---: |
|
||||
| 现役 700 m 方格 ±2100 | 49 | 100% | 36.4 km | 7280 s |
|
||||
| 三角格 1000 m(半径 2800 盘) | 31 | 100% | 34.3 km | 6863 s |
|
||||
| **贪心最小命中集(200 m 候选)** | **31** | **100%** | **22.9 km** | **4587 s** |
|
||||
| **贪心最小命中集(300 m 候选)** | **30** | **100%** | **23.6 km** | **4719 s** |
|
||||
|
||||
**实测收益(2026-09-12,11 场景配对)**:均值 **14003.0 → 10975.5 s(−19.3%)**,
|
||||
中位 −25.4%,10/11 场景更快;检测 444.5 → 319.2,请求 569.5 → 395.6,
|
||||
位移 54.9 → 44.2 km,后备清除尝试 113.3 → 64.1,11/11 valid。
|
||||
低于原估的 −26%,差额来自"缺席频道省下的检测"被定位探点部分抵消。
|
||||
|
||||
**验收**:`CoveragePlan.verify()` 通过;10/13/16 源 × 定向 1/半数/仅一个全向 × 边界朝外压力
|
||||
场景全部全清;请求数、日志体积仍满足 1200 s / 2 MB 预算。
|
||||
|
||||
**风险**:数值采样证书弱于解析证明。缓解:(a) 保留解析七站证明用于 Q3;
|
||||
(b) 证书采样分辨率与裕量写入 summary 并随日志归档;(c) 保留 10% 的额外"保险点"预算,
|
||||
若演练观测到任何漏检立即回退到 49 点表。
|
||||
|
||||
---
|
||||
|
||||
### P0-3 第四问:位置×朝向联合信念与可见性选点
|
||||
|
||||
**现状**:定向源只被当作"候选空间集合",`no_signal` 只用于累计 `blocked_probes`
|
||||
(`planner/__init__.py:663-677`),选点沿固定 45° 阶梯试探;梯子用尽后调用
|
||||
`_fallback_clear`,对候选外包多边形做 20 m 网格覆盖逐格 `/clear`(≤400 格、≤1200 s、≤400 请求)。
|
||||
运行历史显示这正是第四问的失败点:定向源 2/6 清除,4/4 漏检全是定向。
|
||||
|
||||
**方案**:为第四问频道维护 **(x, y, heading) 联合可行集**,把"看不看得见"变成可推理的量:
|
||||
|
||||
- 表示:`JointTensor` —— 复用 Q3 的占用网格,每格附加 24 个朝向 bin 的可行性位掩码
|
||||
(uint8 位域,20 频道 × 6.1 万格 × 3 B ≈ 3.7 MB,可接受)。
|
||||
- 更新规则(全部保守,仅删"必然看不见"的格):
|
||||
- `direction`@s:位置删 `>1500`、角向删 `|gap| > δ_eff + 角裕量`;朝向删
|
||||
"以该格为源、朝向不覆盖 s" 的半盘(保留 ±90° 含边界)。
|
||||
- `no_signal`@s:对 `|g−s| ≤ 1000` 的格,朝向**只能**落在背向 s 的半盘;
|
||||
对 `1000 < |g−s| ≤ 1500` 的格,朝向要么背向 s,要么源实际半径 < |g−s|(保持不确定);
|
||||
对 `|g−s| > 1500` 的格不删。
|
||||
- `near`@s:位置收缩到 5 m 盘,朝向置为全向(近场无遮挡语义)。
|
||||
- 决策:`prob_detect(channel, reader)` = 该点能看到该频道任一可行 (p,h) 的概率质量;
|
||||
选点直接最大化 `prob_detect / (移动秒数 + 6)`,即**保证有信号的信息增益/秒**。
|
||||
- 定位收敛:位置由多条 `direction` 的角域交会收缩(与 Q3 相同);朝向在 2 次
|
||||
"一次被遮挡 + 一次可见"后通常已被压到很窄的楔形,此时可**主动选择必然可见的探点**,
|
||||
用一个正常角域证书结束,而不再进入 400 格盲清。
|
||||
- 后备清除**保留**(`fallback_clear_cells` 已有格数与预算双硬顶),但目标变为
|
||||
"只在证书无法建立时触发",并在 summary 中把触发次数作为回归指标。
|
||||
|
||||
**预期收益(未实测)**:定向频道的探点次数与后备清除触发次数大幅下降。参考量级:
|
||||
后备清除上限 400 格 × 3 s = 1200 s + 400 请求/频道;`docs` 记录的 Q4 改进版每局后备
|
||||
尝试 616/4 局 ≈ 154 次。保守估计 **−1500 ~ −3000 s/局**。
|
||||
|
||||
**验收**:10 源 3 定向场景全清率 100%(现状 referee 仅 2/6 定向);
|
||||
`fallback_clear_attempts` 相对基线下降 ≥ 70%;定向信道平均检测次数 ≤ 全向信道的 1.5 倍。
|
||||
|
||||
**风险**:朝向均匀先验是工程假设,官方分布未知。缓解:先验只影响选点顺序,
|
||||
不影响任何安全性判定;朝向不确定时自动退化为全向分支(`prob_detect` 取全向值)。
|
||||
|
||||
---
|
||||
|
||||
### P0-4 感知层向量化与并行(研发运行速度)
|
||||
|
||||
**现状**(F6):`expected_survival` 占单局 CPU 的 72%,2808 次调用 × (7 代表点 × 800 子采样)
|
||||
≈ 1570 万次纯 Python 距离/角度运算;`apply_measure` 另有 20%,其中 `_kill_outside_wedge`
|
||||
每次遍历全部幸存格(498 万次 `angular_gap`)。
|
||||
|
||||
**方案**:
|
||||
|
||||
1. `belief/occupancy.py` 用 `numpy` 布尔/整数张量替换 `set[int]`:
|
||||
每频道 248×248 uint8(≈61 KB),20 频道 ≈ 1.2 MB;盘/楔删除用切片 + 布尔掩码,
|
||||
粗格索引用 `np.add.reduceat`;`numpy` 缺失时回退现有纯 Python 实现(测试保证两条路径同结果)。
|
||||
2. 采样评分向量化:把 `subsample` 与 `representatives` 预转 `(n,2)` 数组,
|
||||
一次广播算完距离、角度、角向 gap,`expected_survival` 由 5600 次标量循环变成 2–3 条向量指令。
|
||||
3. 站点级编译缓存:`(station, channel, version)` → 掩码结果,`version` 不变直接复用;
|
||||
当前 `_skip_cache` 只缓存"必 no_signal"布尔,可扩展到整块删除掩码。
|
||||
4. MEC 增量维护:当前 `mec()` 每次版本变化重算全部幸存点(最多 6 万点);改为
|
||||
版本变化时只对新增删除点做包围圆增量更新,或按 1/8 子采样给出候选、证书判定用精确值。
|
||||
5. 批量基准并行化:`bench/runner.py` 用 `concurrent.futures.ProcessPoolExecutor`
|
||||
按场景分发(每进程独立 `LocalSimulator`,真值仍只进测试端),Mac M 系列 8 核可得 ~6–8×。
|
||||
6. 观测/遥测:单局 summary 写入 `compute_wall_s`、`profile_top`(可选 `--profile`),
|
||||
纳入基准表的必填列,防止"虚拟时间变快、墙钟悄悄变慢"。
|
||||
|
||||
**实测收益(2026-09-12)**:单局计算 **3.10 → 1.00 s(3.08×)**,最坏 5.84 → 1.30 s;
|
||||
20 场景基准集 12.2 → 3.9 s。虚拟时间与动作序列**逐位一致**(纯 Python 回退路径保留,
|
||||
`MCM26B_NO_NUMPY=1` 可强制)。未达 ≤0.8 s 的目标:剩余开销在
|
||||
`apply_measure` 的网格删除与 `ChannelParticles.__init__`,需要 P0-4b(表示层张量化)。
|
||||
|
||||
**验收**:`tests/test_particles.py`、`tests/test_bayes_loop_q3.py` 全过;
|
||||
纯 Python 与 numpy 两条路径在同一动作日志下产出逐位相同的 summary(`actions_sha256` 相同);
|
||||
单局 `compute_wall_s` ≤ 0.8 s。
|
||||
|
||||
---
|
||||
|
||||
### P1-1 清除点选在 C₂₀(K) 内离当前位置最近处
|
||||
|
||||
现状 `_near_clear_target` 只沿"当前位置 → 候选圆心"方向平移,平移量受
|
||||
`limit − radius` 限制,是被证明的一维特例。一般解是
|
||||
`C₂₀(K) = ∩_{g∈K} B(g, 20)`,取其内离当前位置最近的点:
|
||||
- 对凸多边形候选集,`C₂₀(K)` 仍是凸集(若干圆盘之交),可用"到当前点的最近点"的
|
||||
简单迭代/投影求得;
|
||||
- 若 `C₂₀(K)` 非空,则从该点清除的移动代价最小,且三角不等式保证仍在 20 m 内。
|
||||
|
||||
**预期收益**:Q3 单局清除移动 3668 m(734 s)中回收 20–35%(**−150 ~ −260 s**);
|
||||
Q4 同步受益。
|
||||
**风险**:`C₂₀` 空时退回现有 MEC 圆心逻辑(已实现)。
|
||||
|
||||
---
|
||||
|
||||
### P1-2 覆盖几何参数改为"离线搜索 + 运行期校验"
|
||||
|
||||
- Q3:`station_ring_radius_m` 由 1159.7 的可证明下界 1122.9 重算,并在 `validate_seven_coverage`
|
||||
中同时校验 **PF 网格覆盖**(现行文档已记录 1159.7 与 Optuna 建议值差 3.3 m 的取舍)。
|
||||
收益很小(≈44 s),但把"设计性质 vs 1.7% 均值差异"的取舍写成可复算脚本,而不是口口相传。
|
||||
- Q4:见 P0-2,覆盖集与巡回联合优化(在等价覆盖解中选巡回最短者,
|
||||
预期可在 23.6 km 基础上再降 5–10%)。
|
||||
- 所有覆盖计划产物落 `output/<problem>/coverage/<version>.json` + 证书,纳入版本管理。
|
||||
|
||||
---
|
||||
|
||||
### P1-3 评估与遥测重构(消除 B6)
|
||||
|
||||
1. **冻结基准集**(`bench/suites/`,一题一份、版本化、写入 `manifest.json`):
|
||||
- Q3:10/13/16 源 × 8 个种子 + 2 个圆周边界场景(`r_eff=1000`,扇区接缝);
|
||||
- Q4:10 源 3 定向、13 源 6 定向、16 源 8 定向 × 每种 3 个种子 + 朝外边界压力场景;
|
||||
- 场景与真值只进模拟器与校验器,策略端看不到(真值隔离不变量保持不变)。
|
||||
2. **统一 runner + 逐局 artifact**:`manifest.json` / `results.json` / `aggregate.{json,csv}` /
|
||||
`runs/<scene>/<variant>/summary.json` + `session.jsonl`,任何一局无效则 `all_valid=false` 且非零退出。
|
||||
3. **指标口径去重**:目前 `avg_locate_clear_time_s ≡ virtual_time_s`(单源评估集下二者必然相等),
|
||||
必须在多源基准集上重新定义并标注:
|
||||
`T_total`(整场虚拟时间)、`T_accounted`(首次证明找全时刻)、`T_last_clear`、
|
||||
`T_total / cleared`、`T_total / truth`。
|
||||
4. **运行历史持久化**:`output/` 目前不纳入 git,导致 `output/rl/review-20260911/`、
|
||||
`output/q3/optimization-20260911/` 等证据在 revert 后无法追溯。
|
||||
本轮起把**基准集的 summary/aggregate/manifest(压缩,不含大日志)**纳入版本管理,
|
||||
官方 jlog/日志仍按题面要求单独归档。
|
||||
|
||||
---
|
||||
|
||||
### P1-4 安全阀与退出可解释性
|
||||
|
||||
| 阀门 | 现状 | 改进 |
|
||||
| --- | --- | --- |
|
||||
| `persistent_search` | 默认已关闭,但没有任何"这会浪费多少"的提示;历史上已造成一次 346 170 s(98.84%)的无效演练 | 显式开启时输出预估上限(未清除频道数 × 站点数 × 6 s)并要求二次确认;`summary` 记录 `redundant_rescan_measures` |
|
||||
| `virtual_reserve_s` | 10 000 s 固定 | 改为 `max(10 000, 2 × 最远单动作上界)`,并在接近时把 `stop_reason` 与剩余预算写入 summary |
|
||||
| 完成判定 | `completed = 无中断 ∧ (清除数≥16 ∨ 每频道 CLEARED/ABSENT)` | 增加 `completion_basis ∈ {count_cap, per_channel_proof}` 字段,避免把"数量上界"与"逐频道证明"混为一谈 |
|
||||
| 缺席证明 | 覆盖账本 + PF 双通道 | summary 增加 `absence_evidence[channel] ∈ {pf_empty, coverage, count_cap}`,供论文与审计逐条追溯 |
|
||||
|
||||
---
|
||||
|
||||
### P2-1 选点评分的解析化/查表化
|
||||
|
||||
`expected_survival` 的物理含义可用解析近似替代:对给定测点 q 与候选集,
|
||||
`E[log 幸存占比]` 的一阶近似只依赖 (候选集到 q 的距离分布, 角向展布)。
|
||||
先在离线基准上验证解析近似与采样评分选出的点一致率 ≥ 90%,再切默认实现。
|
||||
预计再省 30–50% 的评分开销(P0-4 之后已不是瓶颈,列入 P2)。
|
||||
|
||||
---
|
||||
|
||||
### P2-2 是否重启学习类方法的门控条件
|
||||
|
||||
只有在**下列四条同时满足**时才考虑把 ML 重新引入,且只允许替换"任务排序"这一个部件:
|
||||
|
||||
1. P0-1/P0-2/P0-3 全部落地并通过基准集验收;
|
||||
2. 手写基线仍有 ≥ 10% 的可测改进空间,且已定位到具体的排序子问题;
|
||||
3. 训练与部署共用同一控制器模块,并有逐状态一致性测试(旧 HRL 的 13 000 行里最有价值的
|
||||
就是这条纪律:`HRLControllerMixin` 被 env 与 runtime 逐字节共用);
|
||||
4. 评估集包含多源与定向场景(现役 20 场景单源评估集不足以支撑任何结论)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 实施路线图
|
||||
|
||||
| 阶段 | 内容 | 交付物 | 验收门槛 | 依赖 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| **S1(P0,2–3 天)** | P0-2 最小命中集 + P0-4 并行基准;P1-3 冻结基准集 + manifest;P1-4 安全阀 | `guarantee/cover.py`、`bench/runner.py`、`bench/suites/*`、`docs/` 更新 | Q4 巡回 ≤ 25 km;基准集并行跑完 ≤ 60 s;全部 `valid` | 无 |
|
||||
| **S2(P0,2–3 天)** | P0-1 融合巡回(Q3);P1-1 清除点 | `plan/schedule.py`、`plan/score.py`、`Episode` 组合化重构 | Q3 均值 ≤ 2700 s、最大 ≤ 4000 s;移动 −25% | S1 的 runner |
|
||||
| **S3(P0/P1,3–5 天)** | P0-4 信念层 numpy 化;P0-3 联合信念 + 可见性选点(Q4) | `belief/occupancy.py`、`belief/joint.py`、`plan/policy.py` | 单局计算 ≤ 0.8 s;Q4 定向全清 100%、后备触发 −70% | S2 的接口 |
|
||||
| **S4(P1/P2,2–3 天)** | P1-2 覆盖参数搜索固化;P2-1 评分解析化;论文数据表 | 覆盖计划证书、`docs/paper` 数据 | 与 S3 结果配对不退化 | S3 |
|
||||
| **S5(演练)** | 官方演练:Q3 用全向、Q4 用混合;先实测单请求延迟与总请求数,核对 1200 s / 2 MB | 官方日志与报告 | 全清 + 正常退出 + 计时复算一致 | 团队排期 |
|
||||
|
||||
**顺序约束**:S1 的基准集必须先于任何算法改动落地,否则无法配对;否则"更快"不可证。
|
||||
|
||||
---
|
||||
|
||||
## 5. 验收与度量
|
||||
|
||||
### 5.1 有效局定义(沿用现有基准脚本,不得放宽)
|
||||
|
||||
```
|
||||
valid := 全部真实源被清除
|
||||
∧ 被判缺席的频道都与真值一致(无误判)
|
||||
∧ 正常 /exit(exit_reason == user_exit)
|
||||
∧ 零算法异常(anomalies == [])
|
||||
∧ 逐动作虚拟时间复算与服务器返回逐微秒一致
|
||||
```
|
||||
|
||||
### 5.2 必报指标(每个变体、每个场景,落盘 + 汇总)
|
||||
|
||||
- 虚拟时间:`T_total`、`T_accounted`、`T_last_clear`、`T_total/cleared`,以及
|
||||
均值/中位/最大/P90 与 **< 4000 s 场次比例**;
|
||||
- 动作:`measures`、`clears`、`failed_clears`、`fallback_clear_attempts`、
|
||||
`redundant_rescan_measures`、`total_requests`;
|
||||
- 移动:`move_distance_m`,以及 move/action/switch 三分项占比;
|
||||
- 墙钟:`compute_wall_s`、请求吞吐(ms/请求)、批量基准总时长、训练 steps/s(若涉及);
|
||||
- 配对比较:同场景同误差种子下 `1 − T_variant / T_baseline` 的均值、
|
||||
更快场数、以及对场景做 10 000 次 bootstrap 的 95% 区间(参照 `summarize_rl_revision.py` 的做法)。
|
||||
|
||||
### 5.3 回归
|
||||
|
||||
`uv run pytest`(当前 232 项)必须全过;新增:
|
||||
- `CoveragePlan.verify()` 的正/负例(故意删一个点必须被检出);
|
||||
- 联合信念的真源保留性质测试(随机 (p,h) × 随机观测序列,真源 (p,h) 永不被删);
|
||||
- 融合巡回的"每源至少一条站点方位线"性质测试;
|
||||
- 逐位一致性测试:numpy 后端与纯 Python 后端在同一动作序列下 summary 完全相同。
|
||||
|
||||
---
|
||||
|
||||
## 6. 风险与明确不承诺的部分
|
||||
|
||||
### 6.1 风险登记
|
||||
|
||||
| 风险 | 影响 | 缓解 |
|
||||
| --- | --- | --- |
|
||||
| 最小命中集证书依赖数值采样,官方分布未知 | 极端 (p,h) 漏检 → 漏源 | 保留解析七站证明(Q3);Q4 证书采样分辨率/裕量写入 summary;保留 10% 保险点预算与 49 点回退开关 |
|
||||
| 站点三角化在 0.72% 源位置上只有 1 站在射程内 | 这些场景仍需专用探点 | `prob_detect` 触发回退路径,保留现有 `_select_point` |
|
||||
| 朝向先验为工程假设 | 选点次优 | 先验只影响顺序;不确定时退化为全向;安全性由 20 m 证书 + 覆盖证明负责 |
|
||||
| numpy 化引入浮点路径差异 | 真源保留性质被破坏 | 两条后端同时保留并对拍;保持整数微秒复算计时 |
|
||||
| 并行基准引入进程/端口/临时目录问题 | 基准不稳定 | 每进程独立内存回环;不共用端口;`manifest` 记录随机种子 |
|
||||
|
||||
### 6.2 不承诺的部分
|
||||
|
||||
- **第四问 4000 s**:10 源场景下,仅"发现 + 缺席证明"就约需
|
||||
最小命中集巡回 4719 s + 缺席检测 ≈1900 s ≈ 6600 s,加之定位清除,4000 s 不可达。
|
||||
若必须逼近该线,需要改变停止条件(例如接受 `unresolved_channels` 而非逐频道缺席证明),
|
||||
那是**成绩口径的取舍**,必须先与题面评分方式核对,本轮不作为代码默认行为。
|
||||
- 本文所有收益数字都是**自建模拟器上的估计或本次复跑观测**,不是官方成绩;
|
||||
官方分布、r_eff 分布、误差场分布仍是测试假设。
|
||||
- 不声称 P0-1/P0-3 已在官方演练验证——需新的演练记录。
|
||||
|
||||
---
|
||||
|
||||
## 附录 A 复现命令与本次新增实验
|
||||
|
||||
```bash
|
||||
# 1) 基线配对(本次复跑:bayes 3707.3 s vs seven_batch 5581.8 s,局部 −32.1%)
|
||||
uv run python scripts/benchmark_q3_bayes.py --seeds 0,1 --out /tmp/mcmopt/q3
|
||||
|
||||
# 2) 运行历史计时分解(已入库):任意会话日志 → 移动/检测/换频分相 + 重复复扫检测
|
||||
uv run python scripts/analyze_run_timing.py session.jsonl --plan seven
|
||||
uv run python scripts/analyze_run_timing.py referee-data/sessions/BBM9-HZG2-77N5-NKKC/log.jsonl --plan seven
|
||||
uv run python scripts/analyze_run_timing.py referee-data/sessions/6X58-XQ7U-CJ24-JQQR/log.jsonl --plan 49
|
||||
# 实测输出(与服务器记录最大偏差 0.000000 s,证明复算口径正确):
|
||||
# session.jsonl 350227.692 s,移动 82.3%,检测 10323 次,重复率 96.4,
|
||||
# 末次清除后仍花 346169.9 s / 10225 次空复扫
|
||||
# BBM9 (Q3) 3134.643 s,移动 75.9%,探点 21 次 / 1138.7 s
|
||||
# 6X58 (Q4) 3251.021 s,移动 78.3%,检测 112 次
|
||||
|
||||
# 3) 第四问覆盖证书与最小命中集(已入库)
|
||||
uv run python scripts/verify_q4_cover.py --out output/q4/cover-certificate.json
|
||||
# 实测输出:目标空间 43032 个 (p,h)
|
||||
# 现役 49 点方格:缺口 0,NN 巡回 36.4 km(7280 s 移动)
|
||||
# 贪心最小命中集:31 点,缺口 0,NN 巡回 22.9 km(4587 s 移动)
|
||||
|
||||
# 4) 感知层剖面(单局 6.04 s 计算中 expected_survival 占 ~72%)
|
||||
python -m cProfile -s cumulative <单局驱动脚本>
|
||||
```
|
||||
|
||||
> 第 2、3 项已作为常驻工具入库,使"虚拟时间花在哪"与"覆盖集是否仍然合法"
|
||||
> 成为可随时重跑的回归项;第 1、4 项仍是一次性分析,建议在 S1 阶段并入
|
||||
> `bench/runner.py` 的必报指标(`compute_wall_s`、`profile_top`)。
|
||||
|
||||
## 附录 B 数字出处索引
|
||||
|
||||
| 数字 | 出处 |
|
||||
| --- | --- |
|
||||
| 移动 80.50% / 16.78% / 2.71%(Q3) | `referee-data/sessions/{BBM9,RM7Y,VQKB}/log.jsonl` 按 `verify_q3_practice.py` 模型复算 |
|
||||
| 移动 82.30%(官方 J2WH) | `session.jsonl` 求和 |
|
||||
| 346 169.9 s 空复扫 | `session.jsonl` `/exit` 时刻 350 227.691846 s 减去 12 源首轮清除的 4 057.839 s |
|
||||
| Q4 定向 2/6、漏检 4/4 全为定向 | `referee-data/sessions/{6X58,BAU8}/meta.json` + `log.jsonl` |
|
||||
| bayes 3707.3 / seven 5581.8 | 本次复跑 `benchmark_q3_bayes.py --seeds 0,1` |
|
||||
| 3535.9 s / 109 检测 / 6960 m 巡回 / 5741 m 探点 | 本次复跑计时分解 |
|
||||
| 13652.1 s / 560 检测 / 51 356 m(Q4) | 本次复跑单局 |
|
||||
| `expected_survival` 72% | 本次 `cProfile` 单局 |
|
||||
| HRL 27.0 steps/s、4007.9 s、109 776 步 | `output/hrl/ui/q3-main/q3.json` + `*.monitor.csv` |
|
||||
| bayes 比 HRL 快 4.3–5.0%、94% 差距为移动 | `output/hrl/ui/q3-main/eval/at-final/aggregate.json` + `runs/*/summary.json` |
|
||||
| 站点三角化 99.28%、交会角 5 分位 58.5° | 本次离线采样 20 000 点 |
|
||||
| 49 点 vs 30 点覆盖集 | 本次贪心击中集计算(附录 A 第 5 项) |
|
||||
Reference in New Issue
Block a user