23 KiB
问题三算法说明:全向多源搜索的覆盖证明、贝叶斯闭环与任务调度(当前实现)
本文汇总当前仓库中问题三的全部实际算法,生产默认策略为
src/mcm26b/planner/bayes_loop.py::BayesLoop(CLI 策略名bayes)。grid(OmnidirectionalLoop,49 点)与seven(SevenBatch,七点解析覆盖 + 本站批量)保留为可复现对照。 问题三当前生产不包含任何机器学习策略;历史 HRL/RL 代码已移除, 自建消融中手写基线稳定优于学习策略(见docs/第三四问架构优化与运行提速方案.md)。
1. 问题定位与策略族
问题三中每局有 10\sim16 个全向干扰源,位置未知、频道互异(1..20),
源位于 \Omega=B(0,1800) 内;有效接收半径 r_{\rm eff} 未知,但满足
1000\le r_{\rm eff}\le1500 m。策略端只能看到协议响应
(direction / near / no_signal),目标是发现、定位并清除全部源,
使虚拟总耗时尽量小,同时给出“不存在漏源”的证明或明确未完成。
| 策略 | 类 | 发现保证 | 定位/决策 | 角色 |
|---|---|---|---|---|
grid |
OmnidirectionalLoop |
700 m 方格 49 点,单元对角 700\sqrt2\approx989.95\le1000 |
发现后立即就近定位,无信息增益 | 历史基线 |
seven |
SevenBatch |
原点 + 半径 1200 m 六等分环,解析覆盖界 968.90 m | 本站先扫频,再按候选集 MEC 圆心最近服务队列 | A2 中间对照 |
bayes |
BayesLoop |
七站环(默认半径 1159.7 m,界 984.54 m)+ PF 负观测 | 网格粒子滤波 + 信息增益选点 + 滚动任务排序 | 生产默认 |
七站族还有 SevenImmediate(A1,发现即离站)与 SevenSweep
(A3,先扫完全域再统一服务)。它们的差异只在发现/定位的调度顺序,
七点覆盖证明与距离语义相同。
1.1 统一计时模型
忽略微秒量化时,一次动作回合的虚拟耗时为
T=\frac{L}{5}+5N_m+N_s+5N_c^++3N_c^-,
其中 L 为移动距离(速度 5 m/s),N_m 为检测次数,N_s 为换频次数,
N_c^+ 为成功清除次数,N_c^- 为失败清除次数。/clear 本身不切换
测向频道,/exit 不增加虚拟时间,访问顺序是起点固定、终点自由的开放
路径问题,不是必须回到原点的 TSP 回路。官方内部按整数微秒累计,项目用
evaluation.recompute_virtual_time 逐动作复算并校验。
2. 三层在线架构
BayesLoop 在 OmnidirectionalLoop 的会话/预算/收尾骨架上重构为三层:
- 保证层:七点覆盖账本
CoverageLedger+ 解析覆盖定理;负责 “全测无信号\Rightarrow该频道不存在源”。 - 信念层:
- 凸候选集
CandidateRegion(正观测方向/近距离约束,用于清除证书); - 网格幸存集
ChannelParticles(正/负观测统一更新,用于缺席证明、 跳测与信息增益); - 两者同读一套响应流,任何一方证毕即结束该频道。
- 凸候选集
- 决策层:统一任务表(站点扫频 / 定位测量 / 清除),开放路径 最近邻 + 2-opt 滚动重排;每执行一个任务后用真实响应重规划。
问题三不复用问题四的(位置 × 朝向)联合信念;no_signal 在全向源上
可以排除 $B(s,1000)$。
3. 证据层:逐频道候选集与负观测
3.1 凸候选集 CandidateRegion
对每个已收到过有效信号的频道 c 维护凸多边形 $P_c^{\rm out}$:
- 初始为
\Omega的 192 边外切正多边形; - 每次
direction:- 与 $B(s,1500)$(192 边外切多边形)求交;
- 与角域
W(s,\theta,\delta_{\rm eff})求交,$\delta_{\rm eff}=1^\circ+0.005^\circ$;
near:与B(s,5)外切多边形求交;no_signal:不收缩凸候选集。对全向源,负观测信息由ChannelParticles处理;让凸表示保留所有正观测候选,可以避免因模型误差误删真源。
圆盘外切多边形的径向超出逐次累加到 slack_m,所有裁剪容差只向
“保留更多候选”的方向倾斜,因此真源始终包含在 P_c^{\rm out} 内。
清除证书使用 P_c^{\rm out} 的最小包围圆 $B(m_c,\rho_c)$:
\rho_c+\text{slack}_c+0.5\le20
\quad\Longrightarrow\quad
\text{在 }m_c\text{ 处一次 `/clear` 必命中真源}.
其中 0.5 m 是工程余量,slack 是外包多边形的半径余量;该判据是保守的
充分条件,而不是“必须达到的最优半径”。
3.2 网格幸存集 ChannelParticles
每个频道维护 \Omega 外扩裕量上的方格网格,间距
h=14.65 m,约 4.7 万格;Voronoi 裕量
m=\frac{\sqrt2}{2}h\approx10.359\ {\rm m}
是格心到格内任一点的最大距离。所有删除判定都内缩 $m$:只有整个格子与 可行集不相交时才删除该格。
| 观测 | 位置/距离更新 | 实现 |
|---|---|---|
no_signal @ s |
距离 $>r_{\rm eff}\ge1000$,删除格心距 s\le1000-m 的格 |
_kill_disk(s,1000-m) |
direction @ $s$,读数 \theta |
距离 $\le1500$;角向偏差不超过 \delta_{\rm eff} + 格内角裕量 |
先删距 s>1500+m 的格,再删角向超出的格 |
near @ s |
距离 \le5 |
删格心距 s>5+m 的格 |
定理(真源保留). 真源 P 所在格的格心 g 满足
$|g-P|\le m$;与真值一致的观测不会让该格与可行集完全不相交,因此
“整格不相交”判定永不删除该格。于是:
- 缺席证明:幸存集为空
\Longrightarrow可行集为空\Longrightarrow该频道无源; - 跳测规则:若某测点到所有幸存格心的最小距离 $>1500+m$,则在该点
测量必为
no_signal且不产生任何新排除,可直接跳过,省 5 秒检测和 1 秒换频; - PF 清除证书:若幸存集 MEC 半径
\rho_{\rm pf}满足 $\rho_{\rm pf}+m+0.5\le20$,则以该 MEC 圆心清除必命中真源。
BayesLoop 在每次测量后同步更新凸候选集与 PF,PF 只在频道尚未
CLEARED/ABSENT 时接收新证据。
3.3 数量上界证明
题面给定每局源总数 10\sim16 且每频道至多一个源。若
DETECTED/CLEARED 的不同频道数达到 16,则所有仍未出现有效信号的
UNKNOWN 频道都不可能有源,可直接标记为 ABSENT,证据来源记为
count_bound。这条规则与位置无关,是严格计数证明,不需要等七站扫完。
summary.completion_basis 会区分 count_cap 与 per_channel_proof,
避免把“达到数量上界”误读成“逐频道缺席证明”。
4. 发现保证:七点解析覆盖与覆盖账本
4.1 full_disk 语义
全向源在测点 s 返回 no_signal,意味着真实距离严格大于
$r_{\rm eff}\ge1000$。因此若集合 S 满足
\Omega\subseteq\bigcup_{s\in S}B(s,1000),
则任何合法源必在某个 s\in S 的 1000 m 内,在该点不可能返回
no_signal。于是“某频道在全部 S 点都测得 no_signal”构成严格的
缺席证明。
CoverageLedger 按 (channel, point_index) 二维记录,只有所有点都已测
(或显式跳过)才允许 coverage_complete(channel)=True;“路过某点”不算
“测过某频道”。
4.2 七点构造与解析界
取原点加半径 a 的六等分环:
S(a)=\{(0,0)\}\cup\left\{a(\cos(k\pi/3),\sin(k\pi/3)):\ k=0,\dots,5\right\}.
定理 1. 当 0<a/\sqrt3\le R 时,
\max_{g\in\Omega}\min_{s\in S(a)}\lVert g-s\rVert
\le D(a):=
\max\left\{\frac a{\sqrt3},\ \sqrt{R^2+a^2-\sqrt3Ra}\right\},
其中 $R=1800$。证明:任意 g=(r\cos\varphi,r\sin\varphi) 与最近环站的
夹角不超过 $\pi/6$。若 $r\le a/\sqrt3$,选原点;否则选最近环站,由
余弦定理和 \cos\varphi\ge\cos(\pi/6)=\sqrt3/2 得
$\lVert g-s\rVert^2\le r^2+a^2-\sqrt3ar$。右端关于 r 是凸二次函数,
在区间端点取最大值,分别得 a^2/3 和 $R^2+a^2-\sqrt3Ra$。证毕。
代入可验证:a=1200 时 D=968.901572 m;当前 bayes 默认
station_ring_radius_m=1159.7 m 时 D=984.539136 m;两者都小于 1000,
从站上有 15.5 m 以上的几何余量。a 的可行下界为
a_{\min}\approx1122.955832 m(零余量)或 1125.254906 m(1 m 余量)。
七点巡回长度 $6a$;a=1200 时纯覆盖路线约 7200 m。
4.3 49 点基线
网格 S=\{(700i,700j):i,j\in[-3,3]\} 覆盖 $[-2100,2100]^2\supseteq\Omega$;
每个单元的对角线 $700\sqrt2\approx989.95\le1000$,因此任意源都在某个
网格点的 1000 m 内。该证明不需要朝向信息,对问题三的七点方案是更强的
充分条件,但会发现阶段检测次数多一个数量级(频道 × 49 次);问题四仍
需要它作为 legacy 对照(见 algo-4.md)。
4.4 计划可验证性
guarantee/cover.py 提供两套校核:
verify_full_disk(points):在\Omega内按网格采样,校核 “每个采样位置都在某个点的 1000 m 内”;verify_forward_half_disk(...):第四问语义,问题三不使用。
七点计划走解析证明;49 点计划走 full_disk 校核。运行期不使用未校核的
点集来宣称“已证明无源”。
5. 定位与清除
5.1 问题二选点器的推广(基线与安全后备)
planner.select_measurement_point 对任意 CandidateRegion 工作:
给定当前后验外包多边形 $P$,令其 MEC 为 $B(m,\rho)$,则安全接收盘为
B\left(m,\ 1000-\rho-10^{-3}\right)
(\rho>1000 时退化为探索 MEC 圆心,可能 no_signal)。盘内取
37 个候选点(圆心 + 3 环 × 12 方向),以“边界见证点 × 读数区间端点 +
near 分支”的最坏后验 MEC 半径为主指标、移动时间为次指标;选出后再按
1/4 间距加密复核。该函数最初为问题二实现,在问题三中服务于
SevenBatch、OmnidirectionalLoop 的定位子循环,以及 BayesLoop
的阻塞式安全后备 _localize_and_clear。
5.2 bayes 的信息增益选点
BayesLoop._select_point 在 PF 幸存集上选择定位测点,候选集分两种:
- 凸候选集安全盘非空:取
select_measurement_point的环形候选,并额外 加入以当前位置到 MEC 圆心方向为轴的对称候选,以及零移动的当前位置; - $\rho>1000$、无保证安全盘:取 PF MEC 的
1.0/1.5/2.0倍探索环和 PF MEC 圆心。
对候选 $q$,默认评分模式 entropy 是期望对数幸存占比:
\text{score}(q)=
\mathbb E[\log(\text{后验幸存占比})]
+\lambda\left(\frac{\lVert p-q\rVert}{5}
+\frac{\lVert q-m_{\rm MEC}\rVert}{5}\right),
\qquad \lambda=0.05.
其中期望对代表假设(从幸存格均匀抽样)和 r_{\rm eff}\sim U(1000,1500)
解析积分:
no_signal分支(概率 $(d-1000)/500$):幸存格收缩到B(q,1000-m)之外;direction分支:以代表点方位为期望读数,用\pm\delta_{\rm eff}角域裁剪后的子采样估计幸存占比;near分支($d\le5+m$):后验为 5 m 盘,占比趋近零。
可选 score_mode=radius 直接最小化期望后验 MEC 半径。
score_subsample=800、mc_representatives=7 是工程参数;均匀源分布、
零误差代表读数和 r_{\rm eff} 均匀分布都只是选点评分假设,不参与
任何安全证明。安全证明只使用题面给定的 \pm1.005^\circ 角域与距离界。
当前配置中以下“更强的耦合”均已实现但实测为净负收益,默认关闭:
fuse_localize_into_tour=False(站点顺带定位)、exact_order_limit=0
(Held-Karp 精确任务序)、route_aware_probe=False(路线感知探点)。
代码与开关保留,便于复现与后续研究。
5.3 清除动作
BayesLoop._best_certificate 的优先级:
- 凸证书:
clear_certificate(region); - PF 证书(
grid_cert_enabled=True):负观测挖洞使 PF 幸存集更紧时, 若 PF MEC 半径 $+m+0.5\le20$,用 PF MEC 圆心作为清除点。
得到证书后,_near_clear_target 还会把清除点从证书圆心向当前位置方向
平移一小段:设当前证书半径 $r$、限值 $limit$,最多平移
$\Delta=\min(\text{当前距离},limit-r-10^{-6})$,新目标点仍保证所有候选
源在 r+\Delta\le limit 内。该平移可省去最后一段接近路程,不放松证书。
定位子循环每频道最多 max_localize_steps=8 次追加测量;若在安全接收盘内
出现 no_signal,记录异常 no_signal_in_safe_disk,不静默忽略。
6. 决策层:统一任务表与滚动重排
6.1 任务类型
BayesLoop._build_tasks 在每次决策点构建:
| 任务 | 位置 | 服务时间 | 触发条件 |
|---|---|---|---|
station |
七站之一 | $5\times$待测频道数 + 换频 | 该站仍有 UNKNOWN 频道未测,且跳测规则未排除 |
locate |
信息增益选点 | 5 s + 可能的换频 | 频道 DETECTED 且暂无清除证书,且未委托给站点 |
clear |
证书中心(可平移) | 5 s 成功(失败 3 s) | 频道有清除证书且未处于“清除失败后待补测”状态 |
站点任务的服务时间计算当前频道优先:若机器人当前测向频道包含在待测集合, 则少一次换频。清除动作不改变测向频道。
6.2 跳测规则
guaranteed_no_signal(station):若该频道所有幸存格心到站点的距离都
大于 $1500+m$,则该次测量必为 no_signal 且不会产生新排除,任务构造时
直接跳过。跳测缓存按 PF version 生效;幸存集单调收缩,因此判定单调。
这条规则是负观测给问题三带来的主要检测节省之一。
6.3 开放路径排序
任务表规模通常为 6~12 项,代价包含移动、检测、换频,终点自由。默认:
- 最近邻构造初始序;
- 开放路径 2-opt 改进,最多
two_opt_passes=4轮; - 换频代价按实际频道状态沿路径逐步传递,而不是固定服务费;
- 执行排序后的第一个任务,拿到响应后立即回到决策点重新构建/排序 (滚动时域)。
可选的 Held-Karp 精确 DP(含“已访问集合 × 结束时频道”状态)与
route_aware_probe 路线感知精修均已实现;在当前冻结基准集上,
精确 DP 与 2-opt 差距在噪声内(甚至略差),默认关闭。
6.4 已停靠位置的机会测量
_measure_at_stop 在每次决策点评估当前位置是否值得顺带补测已发现频道:
只对尚未在该 0.5 m 点测过、无证书、到凸候选集所有顶点距离均不超过
1000-10^{-3} m、且预测后验半径至少收缩
opportunistic_radius_ratio=0.5 的频道追加一次测量。每次仍支付 5 秒与
实际换频成本,只是省去移动;它不是“免费检测”。
7. 停止、预算与异常收尾
7.1 完成证明
循环停止后,summary.completed 的依据分两类:
count_cap:清除数达到 16(题面源数上界);per_channel_proof:每个频道状态都是CLEARED或ABSENT。
缺席证据在 summary.absence_evidence 中逐频道记录来源:
| 证据 | 条件 | 严格性 |
|---|---|---|
pf_empty |
某频道 PF 幸存集为空 | 严格(Voronoi 真源保留) |
coverage |
七点/49 点全部测得 no_signal(或计划上显式跳过) |
严格(解析/校核覆盖) |
count_bound |
已有 16 个不同频道的有效存在证据 | 严格(题面数量上界) |
persistent_search 默认 False:一旦“每频道已清除或证明不存在”就立即
/exit,因为虚拟时间是成绩口径。显式 --persistent 会继续复扫未清除
频道直到清除数达 16、实机预算耗尽或虚拟安全余量不足;历史官方演练证明
无条件复扫会浪费大量时间,因此该项目只作为可审计的保险开关保留。
7.2 预算与中断
- 虚拟安全余量
virtual_reserve_s=10000s:剩余虚拟时间不足以完成 一个保守单动作上界时停止发起新动作,避免触及 100 小时上限; - 现实预算以服务器
/enter返回的remaining_real_duration_s为准;DeadlineExceeded时 stop_reason 记为real_budget_exhausted; - 传输、协议或守恒性异常都会尽力
/exit并输出未完成 summary, 不静默丢会话;completed=False与具体stop_reason同时留档。
8. 基线与自建消融
8.1 调度变体
| 版本 | 发现/定位关系 | 说明 |
|---|---|---|
A0 grid |
49 点,发现后立即离站定位 | 历史生产基线 |
A1 SevenImmediate |
七点,发现后立即离站定位 | 只替换覆盖点 |
A2 SevenBatch |
七点,本站先扫完再按最近候选圆心服务队列 | 中间对照,曾用于官方演练 |
A3 SevenSweep |
七点,先扫完全部必要站点再统一服务 | 批量调度的另一极端 |
A2 相对 A0 的收益主要来自“减少站间往返 + 每站批量扫频 + 无源频道只需
7 次而不是 49 次检测”。在早期 12 个自建场景上,A0/A1/A2/A3 平均虚拟
时间分别为 13788.04 / 7476.09 / 5496.20 / 6094.73 秒,A2 逐局平均降幅
58.55%;更晚的 14 场景消融中,bayes 相对 A2 再降 30.0%
(3726.2 s vs 5352.7 s)。这些数字来自自建模拟器与固定误差场,不是官方成绩。
8.2 冻结基准集当前结果
project/q3 在 18 个冻结 core 场景(seeds 100–105 × 10/13/16 源,
harness/suites.py 版本化清单)上的配对结果:
| 方法 | 有效局 | 全清率 | 虚拟时间均值 | 中位 | 最大 | 检测均值 | 请求均值 | 位移均值 |
|---|---|---|---|---|---|---|---|---|
bayes |
18/18 | 100% | 3188.7 s | 3111.3 s | 3663.7 s | 111.1 | 124.1 | 12338 m |
seven |
18/18 | 100% | 5361.7 s | 5367.5 s | 6462.4 s | 100.8 | 113.8 | 23508 m |
grid |
18/18 | 100% | 14935.7 s | 15275.1 s | 16691.4 s | 430.6 | 443.6 | 61550 m |
配对变化:bayes 相对 grid 虚拟时间 −78.4%,18/18 场景更快;
所有 54 局零缺席误判、零异常、正常退出且计时复算一致。另有边界场景
(源在 \Omega 边界、$r_{\rm eff}=1000$)验证七点覆盖的扇区接缝。
8.3 官方演练记录(历史口径)
- 旧 49 点实现通过官方问题三演练案例
DHFK-PHD2-SR2P-FXK9: 10/10 全清、540 次检测、10 次清除,虚拟总时间 16217.45 s, 现实 7.629 s; - A2 七点批量通过官方案例
J4ER-Y9EP-Q379-UPFA:12/12 全清、 109 次检测、虚拟 5232.13 s、现实 3.406 s; - 后续一次官方问题三演练(
3BW5-9R54-NZNA-MYB9)记录 13/13 全清、 虚拟 4058.71 s。
以上案例的源场景、实现版本、误差场均不同,不能直接横向比较,也不能把
历史官方数字当成当前 bayes 同案例重跑结果。当前默认 bayes 的官方向
成绩仍需新的演练验证。
9. 复杂度、参数与保守性边界
| 模块 | 计算量(近似) |
|---|---|
| PF 更新 | 与删除格/角向扫描的幸存格数相关;粗格索引与向量化(numpy 可选)控制常数 |
| 七点覆盖 | 最多 7\times20=140 个 (站, 频道) 记录 |
| 任务排序 | 最近邻 O(n^2) + 2-opt $O(n^2)$/轮;精确 DP 为 $O(2^nn\cdot |
| 凸候选集 | 192 边形外切 + O(m) Sutherland–Hodgman 裁剪;MEC 用 Welzl |
| 选点评分 | $O(N_q\times(\text{子采样}))$;候选约 37 个,子采样 800 |
保守性边界:
- 所有“证毕”结论依赖题面观测语义和保守外包/内缩;若接口语义、 接收半径范围或误差界改变,必须重新校核;
- 性能结论依赖自建场景分布、固定误差场和排序启发式,不构成对全部合法 场景的最坏时间保证;
- PF 的均匀先验与信息增益评分是选点启发式,不是官方概率模型;
persistent_search关闭时,如果官方场景违反题面数量上界或存在模型外 行为,不能由本算法保证发现;官方接口语义变化时必须记录异常而不是 强行把未完成解释为成功。
10. 与问题一、二、四当前实现的复用关系和差异(核对结果)
| 复用/差异 | 具体情况 |
|---|---|
| 问题一几何内核 | belief/__init__.py 与 particles.py 只从 geometry.localization 导入 smallest_enclosing_circle;在线 clip_wedge 复刻同一半平面公式,但工作在凸多边形裁剪流程,不调用 solve |
| 问题二选点器 | select_measurement_point 直接从问题二推广而来,问题三用它做基线定位与安全后备;safe_disk_radius 同样复用 |
| 问题一没有的 | 负观测 no_signal、PF 非凸挖洞、覆盖缺席证明、数量上界、任务排序、滚动时域、预算/异常治理 |
| 问题二没有的 | 多频道数据关联、发现覆盖、清除动作、在线顺序决策;问题二只做一次第二点选择 |
| 问题四的差异 | 问题四不能用七点 full_disk 缺席证明;no_signal 可能由朝向遮挡造成,因此改用 forward_half_disk 31 点覆盖 +(位置 × 朝向)联合信念 + 预算化后备清除(见 algo-4.md) |
一句话概括:问题三 = 问题一的保守几何 + 问题二的保证接收选点 + 逐频道非凸负观测 + 覆盖/数量缺席证明 + 开放路径滚动调度。
11. 复现与参考
# 冻结基准集:bayes vs seven_batch(runner 内置策略名)
uv run python scripts/run_suite.py --problem 3 --suite core --jobs 6 \
--variants bayes,seven_batch --label q3-core
# 项目对比框架(含 grid 基线,生成 project/results/q3.{md,json})
uv run python -m project.run --question 3
# 回归测试
uv run pytest tests/test_particles.py tests/test_bayes_loop_q3.py \
tests/test_planner.py tests/test_coverage_plan.py tests/test_safety_valves.py -q
主要文件:
src/mcm26b/planner/bayes_loop.py:bayes决策层;src/mcm26b/planner/__init__.py:OmnidirectionalLoop、问题二选点器、清除证书;src/mcm26b/planner/seven.py:七点策略族与覆盖校验;src/mcm26b/particles.py:网格幸存集与信息增益;src/mcm26b/belief/__init__.py:凸候选集;src/mcm26b/coverage/__init__.py、src/mcm26b/guarantee/cover.py:覆盖账本与校核;src/mcm26b/routing.py:开放路径 DP/2-opt/MST 下界;src/mcm26b/planner/bayes_loop.py::_exact_order:可选 Held-Karp。
延伸阅读:docs/algo-3-bayes.md(早期贝叶斯闭环设计与消融细节)、
docs/第三四问架构优化与运行提速方案.md(运行历史与瓶颈)、
docs/第三四问图论与动态规划优化方案.md(图论/DP 轮实测与负面结论)。
参考文献
- 全国大学生数学建模竞赛组委会. 2026 年高教社杯全国大学生数学建模竞赛 B 题[Z]. 2026. 本地题面
issue/B题.pdf。 - 模拟器配套材料. 附件 1《模拟器使用说明》、附件 2《通信接口说明及编程指南》[Z]. 2026. 本地
issue/附件/。 - 本项目.
docs/algo-1.md、docs/algo-2.md、docs/algo-4.md。 - 本项目.
docs/算法审核与优化-20260911.md、docs/第三四问架构优化与运行提速方案.md、docs/第三四问图论与动态规划优化方案.md。