baseline
This commit is contained in:
@@ -9,6 +9,8 @@
|
|||||||
- [问题三搜索与总耗时改进方案(含七点覆盖证明和自建消融)](docs/algo-3.md)
|
- [问题三搜索与总耗时改进方案(含七点覆盖证明和自建消融)](docs/algo-3.md)
|
||||||
- [第三、四问系统性架构优化与运行提速方案(运行历史复核、最小命中集、联合信念)](docs/第三四问架构优化与运行提速方案.md)
|
- [第三、四问系统性架构优化与运行提速方案(运行历史复核、最小命中集、联合信念)](docs/第三四问架构优化与运行提速方案.md)
|
||||||
- [第三、四问图论与动态规划优化方案(定点实测、路由 gap、定向定位 DP、证书硬化)](docs/第三四问图论与动态规划优化方案.md)
|
- [第三、四问图论与动态规划优化方案(定点实测、路由 gap、定向定位 DP、证书硬化)](docs/第三四问图论与动态规划优化方案.md)
|
||||||
|
- [1-4 问最优与基线实现对比框架](project/README.md)(`uv run python -m project.run`,结果在 `project/results/`)
|
||||||
|
- [最优与基线实现对比(论文口径说明)](docs/最优与基线实现对比.md)
|
||||||
- [论文](docs/paper/main.tex)
|
- [论文](docs/paper/main.tex)
|
||||||
|
|
||||||
## 项目结构
|
## 项目结构
|
||||||
@@ -27,6 +29,8 @@ src/mcm26b/
|
|||||||
├── evaluation/ # 日志复算与统计(已完工)
|
├── evaluation/ # 日志复算与统计(已完工)
|
||||||
└── harness/ # 测试侧适配:评测机<->Transport 内存回环、故障注入、回放、场景
|
└── harness/ # 测试侧适配:评测机<->Transport 内存回环、故障注入、回放、场景
|
||||||
tests/ # pytest(含解耦守卫:运行时零导入 referee + 规则表不漂移)
|
tests/ # pytest(含解耦守卫:运行时零导入 referee + 规则表不漂移)
|
||||||
|
project/ # 1-4 问"最优实现 / 基线实现"对比框架:逐问 optimal+baseline
|
||||||
|
# +独立参考,冻结基准集配对出数;见 project/README.md
|
||||||
scripts/ # 采集、基准、可视化与校核脚本(含问题四批量仿真 benchmark_q4.py、
|
scripts/ # 采集、基准、可视化与校核脚本(含问题四批量仿真 benchmark_q4.py、
|
||||||
# 运行历史计时分解 analyze_run_timing.py、第四问覆盖证书 verify_q4_cover.py)
|
# 运行历史计时分解 analyze_run_timing.py、第四问覆盖证书 verify_q4_cover.py)
|
||||||
output/ examples/ # 演练证据、算例与生成产物
|
output/ examples/ # 演练证据、算例与生成产物
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
# 问题一至四:最优实现与基线实现对比
|
||||||
|
|
||||||
|
本文件是四问"最优实现 / 基线实现"对比的**论文口径说明**。代码与可复现
|
||||||
|
产物在仓库根目录 `project/`,运行 `uv run python -m project.run` 一次生成
|
||||||
|
`project/results/summary.md` 与逐问 `q1.md … q4.md`。本文件只摘录设计与结论,
|
||||||
|
数字以后者为唯一来源。
|
||||||
|
|
||||||
|
## 1. 交付物与目录
|
||||||
|
|
||||||
|
```text
|
||||||
|
project/
|
||||||
|
├── README.md 设计原则、各问对比设计、口径与边界
|
||||||
|
├── common.py 方法描述 / 指标聚合 / 落盘 / 闭环装配
|
||||||
|
├── run.py 统一入口(--question/--quick)
|
||||||
|
├── results/ 自动生成:q1.md/json … q4.md/json + summary.md
|
||||||
|
├── q1/ optimal.py baseline.py reference.py cases.py compare.py
|
||||||
|
├── q2/ optimal.py baseline.py reference.py region.py cases.py compare.py
|
||||||
|
├── q3/ optimal.py baseline.py compare.py
|
||||||
|
└── q4/ optimal.py baseline.py compare.py
|
||||||
|
```
|
||||||
|
|
||||||
|
角色边界:
|
||||||
|
|
||||||
|
- **最优实现**:直接适配 `src/mcm26b` 中已由 pytest 固化的生产实现,
|
||||||
|
不重写算法;
|
||||||
|
- **基线实现**:同题下更朴素、但能独立跑通的对照,不共享最优实现的关键步骤;
|
||||||
|
- **独立参考**:与分析算法正交的数值/枚举标准,用于判定谁对谁错。
|
||||||
|
|
||||||
|
第三、四问统一走 `LocalSimulator` 内存回环(与官方 HTTP 协议同一边界),
|
||||||
|
真值只在评测机侧;有效性判定沿用项目口径:
|
||||||
|
**全清 ∧ 无缺席误判 ∧ 正常 `/exit` ∧ 零异常 ∧ 逐微秒计时复算一致**。
|
||||||
|
|
||||||
|
## 2. 四问方法设计对照
|
||||||
|
|
||||||
|
| 问 | 最优实现 | 基线实现 | 独立参考 | 主要指标 |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| 1 | 半平面交 + 顶点对精确直径 + Welzl MEC + 覆盖反例 | 网格采样 + **包围盒对角线**代理直径/覆盖 | 大框半平面裁剪 + 暴力直径/MEC | 状态、直径误差、覆盖准确率、耗时 |
|
||||||
|
| 2 | 保证接收盘内最小最坏后验半径(生产选点器) | ① 垂直 1000 m 最大交会角;② 候选集 MEC 圆心 | 保证接收盘内 9×9 网格枚举 | 保证接收率、最坏后验半径、移动时间 |
|
||||||
|
| 3 | `bayes`:粒子滤波 + 信息增益 + 滚动 2-opt | `grid`:49 点网格 + 就近服务;`seven`:七点批量 | 冻结基准集 + 真值全清校验 | 全清率、虚拟时间及其分布 |
|
||||||
|
| 4 | `directional`:31 点定向覆盖 + 联合信念 + 后备清除 | 问题三策略直接搬用(`bayes` / `grid`) | 冻结基准集(core+boundary) | 全清率、**缺席误判**、虚拟时间 |
|
||||||
|
|
||||||
|
## 3. 对比结果
|
||||||
|
|
||||||
|
### 3.1 问题一(40 个随机交会算例)
|
||||||
|
|
||||||
|
| 指标 | 最优 | 基线 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 状态判定准确率 | 100% | 100% |
|
||||||
|
| 直径平均绝对误差 | **0.000 m** | 4.583 m(最大 14.60 m) |
|
||||||
|
| 直径圆覆盖判定准确率 | **100%** | 52.5% |
|
||||||
|
| 平均耗时 | **0.11 ms** | 22.35 ms |
|
||||||
|
|
||||||
|
固定示例进一步给出结构性反例(`examples/q1`,解析期望由
|
||||||
|
`tests/test_geometry*.py` 固化):
|
||||||
|
|
||||||
|
- `normal`:直径 26.99 m,**直径圆不覆盖**定位区域(最优判否、基线判是);
|
||||||
|
- `triangle`:直径 40.00 m 的等边三角形反例,最小包围圆半径 23.09 m > 20 m,
|
||||||
|
直径圆不覆盖;基线因"包围盒中心 + 对角线"恒判覆盖;
|
||||||
|
- `poor`:近共线检测点 → 无界,最优/基线/参考一致判无界;
|
||||||
|
- `segment`:零误差退化线段,最优判线段 D=100 m 且覆盖成立,基线退化为点;
|
||||||
|
- `empty`:观测不相容 → 空集。
|
||||||
|
|
||||||
|
> 基线的两处结构性缺陷:直径用包围盒对角线在斜/细长区域系统性高估;
|
||||||
|
> 覆盖用包围盒中心圆则恒判"能覆盖",恰好丢掉问题一要考的反例。
|
||||||
|
|
||||||
|
### 3.2 问题二(30 个随机首次示向度,初始 ρ 均值 681.5 m)
|
||||||
|
|
||||||
|
| 方法 | 保证接收率 | 最坏后验均值 | 终值比 ρ'/ρ | 移动均值 |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| 最优(保证盘内最小最坏后验) | 100% | **73.89 m** | 0.104 | 170.4 s |
|
||||||
|
| 基线① 垂直 1000 m 最大交会角 | **0%** | 80.11 m | 0.116 | 200.0 s |
|
||||||
|
| 基线② MEC 圆心 | 100% | 340.75 m | 0.500 | 136.3 s |
|
||||||
|
| 参考(保证盘内网格枚举) | 100% | 75.63 m | 0.106 | 170.3 s |
|
||||||
|
|
||||||
|
> 基线①说明"只追交会角"会在本题失败:首次角域可延伸到 1500 m,
|
||||||
|
> 真源在远端时第二点距离超过 1000 m,测量直接 `no_signal`。
|
||||||
|
> 基线②保证接收但后验几乎不收缩(≈0.5ρ)。最优在同等保证下把后验压到 0.10ρ,
|
||||||
|
> 且与网格参考几乎重合(生产采样质量足够)。
|
||||||
|
|
||||||
|
### 3.3 问题三(18 个冻结 core 场景,全向 10/13/16 源)
|
||||||
|
|
||||||
|
| 方法 | 有效局 | 全清率 | 虚拟时间均值 | 中位 | 检测均值 | 请求均值 | 位移均值 |
|
||||||
|
| --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
| 最优 `bayes` | 18/18 | 100% | **3188.7 s** | 3111.3 s | 111.1 | 124.1 | 12338 m |
|
||||||
|
| 中间 `seven` | 18/18 | 100% | 5361.7 s | 5367.5 s | 100.8 | 113.8 | 23508 m |
|
||||||
|
| 基线 `grid` | 18/18 | 100% | 14935.7 s | 15275.1 s | 430.6 | 443.6 | 61550 m |
|
||||||
|
|
||||||
|
配对变化:`bayes` 相对 `grid` 虚拟时间 **−78.4%**(18/18 场景更快),
|
||||||
|
`seven` 相对 `grid` −63.7%。全部 54 局零异常、计时复算一致。
|
||||||
|
|
||||||
|
### 3.4 问题四(11 个冻结 core+boundary 场景,含朝外发射的边界定向源)
|
||||||
|
|
||||||
|
| 方法 | 有效局 | 全清率 | 缺席误判 | 虚拟时间均值 | 后备清除均值 |
|
||||||
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
| 最优 `directional` | 11/11 | **100%** | **0** | 9069.9 s | 24.8 |
|
||||||
|
| 基线 `grid`(49 点全向) | 0/11 | 67% | 0 | 18179.2 s | 0 |
|
||||||
|
| 基线 `bayes`(七站全向缺席证明) | 0/11 | 77% | **24** | 3890.8 s | 0 |
|
||||||
|
|
||||||
|
> 两条搬用基线的失效机理不同:
|
||||||
|
> `bayes` 用七站环的**全向**缺席证明,遇到背离圆心发射的边界定向源时
|
||||||
|
> 会把真源直接判成 ABSENT(累计 24 次 **缺席误判**,属"快而错",其虚拟时间
|
||||||
|
> 不可与最优直接比较);`grid` 的 49 点覆盖对定向源发现是完备的,但缺少
|
||||||
|
> 定向观测推理与 ≤20 m 后备清除,定位停滞只能停在 DETECTED("稳而不全")。
|
||||||
|
> 最优 `directional` 两种工况都是 11/11 全清、零误判,代价是多花时间与
|
||||||
|
> 平均 24.8 次后备清除。
|
||||||
|
|
||||||
|
## 4. 结论
|
||||||
|
|
||||||
|
1. 问题一:生产几何内核对状态、直径、覆盖三项与独立参考**逐案一致**;
|
||||||
|
朴素包围盒基线在覆盖判定上只有约 52% 准确率,且对固定反例全错。
|
||||||
|
2. 问题二:只有在"保证接收"约束内优化后验才既安全又有效;最优相对保守
|
||||||
|
基线把最坏后验半径从 0.5ρ 降到 0.10ρ,且与网格参考几乎重合。
|
||||||
|
3. 问题三:`bayes` 在相同全清率下把虚拟时间降到 `grid` 的约 1/5,
|
||||||
|
且逐场景配对 18/18 更快,说明"负观测感知 + 信息增益选点 + 全局排序"
|
||||||
|
三步都产生净收益。
|
||||||
|
4. 问题四:定向源的难点不是"发现"而是"证明缺席"与"失去信号后的清除";
|
||||||
|
直接搬用第三问策略会得到 24 次缺席误判,不可接受。`directional` 的
|
||||||
|
联合信念 + 后备清除是 11/11 全清的关键。
|
||||||
|
|
||||||
|
写论文时建议按"基线为什么错 → 最优补了哪一步 → 独立参考如何判定"的三段式
|
||||||
|
展开,并声明:随机分布是测试假设;基线时间快于最优的场景(如问题四 `bayes`)
|
||||||
|
不构成有效性优势,因为该局基线未完成全清任务。
|
||||||
|
|
||||||
|
## 5. 复现
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv run python -m project.run # 四问全部(约 3–4 分钟)
|
||||||
|
uv run python -m project.run --question 1 # 只跑问题一
|
||||||
|
uv run python -m project.run --quick # 缩小算例/基准集规模
|
||||||
|
uv run pytest tests/test_geometry.py tests/test_geometry_certificates.py -q # 几何回归
|
||||||
|
```
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
# 1-4 问「最优实现 / 基线实现」对比框架
|
||||||
|
|
||||||
|
本目录**不是**比赛运行时代码,而是把四问的生产实现与独立基线放在同一口径下
|
||||||
|
配对对比、生成可写进论文的"算法对比"证据。运行时代码仍只有 `src/mcm26b`,
|
||||||
|
`project/` 只依赖它和测试侧内存回环,不反向污染求解入口。
|
||||||
|
|
||||||
|
## 1. 设计原则
|
||||||
|
|
||||||
|
| 角色 | 含义 | 约束 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| **最优实现** | 复用 `src/mcm26b` 中已由测试固化的生产实现 | 不复制算法,只做 I/O 适配 |
|
||||||
|
| **基线实现** | 同题下更朴素、但能独立跑通的对照 | 不得共享最优实现的关键步骤 |
|
||||||
|
| **独立参考** | 与分析算法正交的数值/枚举标准 | 用于判定谁对谁错,而非自证 |
|
||||||
|
|
||||||
|
- 第三、四问走 `LocalSimulator` 内存回环,与官方 HTTP 协议同一边界;
|
||||||
|
真值只在评测机侧,策略端只看响应,因此"全清率/缺席误判"是可信的。
|
||||||
|
- 闭环有效性判定沿用项目口径:**全清 ∧ 无缺席误判 ∧ 正常 `/exit` ∧ 零异常
|
||||||
|
∧ 逐微秒计时复算一致**,任何一项不满足该局记为无效,失败不静默丢样本。
|
||||||
|
- 所有对比都按 `scene_id` 配对,汇总同时给均值、中位、最大与相对基线的
|
||||||
|
配对变化,避免"均值好看但逐局更差"。
|
||||||
|
|
||||||
|
## 2. 目录结构与职责
|
||||||
|
|
||||||
|
```text
|
||||||
|
project/
|
||||||
|
├── README.md 本文件
|
||||||
|
├── common.py 公共层:方法描述、指标聚合、结果落盘、闭环装配
|
||||||
|
├── run.py 统一入口:一次跑完四问并生成 results/
|
||||||
|
├── results/ 自动生成:q1.md/json … q4.md/json + summary.md
|
||||||
|
├── q1/ 问题一:交会定位区域直径与直径圆覆盖
|
||||||
|
│ ├── optimal.py 最优:半平面交 + 顶点对精确直径 + Welzl MEC + 覆盖反例
|
||||||
|
│ ├── baseline.py 基线:网格采样 + 轴对齐包围盒对角线代理
|
||||||
|
│ ├── reference.py 参考:大框 Sutherland–Hodgman 半平面裁剪 + 精确直径/MEC
|
||||||
|
│ ├── cases.py 固定示例(examples/q1)+ 随机交会算例
|
||||||
|
│ └── compare.py 同案配对:状态/直径/覆盖/耗时
|
||||||
|
├── q2/ 问题二:第二检测点选择
|
||||||
|
│ ├── optimal.py 最优:保证接收盘内最小最坏后验半径(生产选点器)
|
||||||
|
│ ├── baseline.py 基线:垂直 1000 m 最大交会角 / 候选集 MEC 圆心
|
||||||
|
│ ├── reference.py 参考:保证接收盘内稠密网格枚举
|
||||||
|
│ ├── region.py K1 = Ω ∩ 角域 ∩ B(s,1500) 统一构造
|
||||||
|
│ ├── cases.py 随机首次示向度算例
|
||||||
|
│ └── compare.py 统一评估器:保证接收、最坏后验、移动代价
|
||||||
|
├── q3/ 问题三:全向源闭环
|
||||||
|
│ ├── optimal.py 最优:bayes(粒子滤波 + 信息增益 + 滚动 2-opt)
|
||||||
|
│ ├── baseline.py 基线:grid(49 点网格)/ seven(七点批量)
|
||||||
|
│ └── compare.py 冻结基准集 core 配对对比
|
||||||
|
└── q4/ 问题四:含定向源闭环
|
||||||
|
├── optimal.py 最优:directional(联合信念 + 后备清除 + 定向覆盖计划)
|
||||||
|
├── baseline.py 基线:把问题三策略直接搬用(bayes / grid)
|
||||||
|
└── compare.py 冻结基准集 core+boundary 配对对比
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. 各问对比设计
|
||||||
|
|
||||||
|
### 问题一(`q1/`)
|
||||||
|
|
||||||
|
- **最优**:`mcm26b.geometry.localization.solve`,半平面交求凸多边形,顶点对
|
||||||
|
枚举直径,Welzl 最小包围圆,并输出直径圆覆盖判断、圆外顶点与最大超出距离。
|
||||||
|
- **基线**:固定大框网格采样,直径取**包围盒对角线**、覆盖取"包围盒中心 +
|
||||||
|
代理直径圆"。它在斜/细长区域上系统性高估直径,并对任意包围盒都几乎恒判覆盖。
|
||||||
|
- **参考**:从 ±1e7 m 大框出发逐半平面 Sutherland–Hodgman 裁剪,独立实现
|
||||||
|
直径暴力枚举与 1/2/3 点支撑圆暴力最小包围圆。与最优实现共享 0 个内部步骤。
|
||||||
|
- 指标:状态判定、直径绝对误差、直径圆覆盖准确率、运行耗时。
|
||||||
|
|
||||||
|
### 问题二(`q2/`)
|
||||||
|
|
||||||
|
- 候选集 `K1 = Ω ∩ W(s,θ±δ_eff) ∩ B(s,1500)`,三个方法共用同一次构造。
|
||||||
|
- **最优**:保证接收盘 `B(MEC 圆心, 1000−ρ)` 内环形采样,最小化"目标边界点 ×
|
||||||
|
读数区间端点"的最坏后验半径,移动时间同分决胜;保证接收严格,采样是启发式。
|
||||||
|
- **基线**:① 垂直首次示向度 1000 m 的"90° 交会点"——忽略接收保证,真源在
|
||||||
|
角域远端时必然 `no_signal`;② 候选集 MEC 圆心——保证接收但交会角可能最差。
|
||||||
|
- **参考**:保证接收盘内 9×9 网格枚举同一目标函数,检验生产采样接近网格最优。
|
||||||
|
- 指标:保证接收率、最坏后验包围半径、收缩量、移动时间。
|
||||||
|
|
||||||
|
### 问题三(`q3/`)
|
||||||
|
|
||||||
|
- **最优** `bayes`:网格粒子滤波幸存集 + 期望信息增益选点 + 滚动 2-opt 任务排序。
|
||||||
|
- **基线** `grid`:49 点(700 m 步长)网格发现 + 就近服务;`seven`:七点解析
|
||||||
|
覆盖 + 同站批量扫频,作为"覆盖证明 + 批量调度"的中间对照。
|
||||||
|
- 冻结基准集 `harness.suites`(种子/源数版本化),逐局校验全清与计时复算。
|
||||||
|
- 指标:全清率、均值/中位/最大虚拟时间、检测数、请求数、位移、墙钟。
|
||||||
|
|
||||||
|
### 问题四(`q4/`)
|
||||||
|
|
||||||
|
- **最优** `directional`:31 点定向覆盖计划 + 朝向遮挡推理 + (位置×朝向) 联合
|
||||||
|
信念 + ≤20 m 有限圆盘后备清除。
|
||||||
|
- **基线**:把问题三策略原样搬用——`bayes` 的七站全向缺席证明会把朝外定向源
|
||||||
|
误判为 ABSENT("快而错");`grid` 的 49 点覆盖对定向源发现完备,但无后备清除,
|
||||||
|
定位停滞只能停在 DETECTED("稳而不全")。
|
||||||
|
- 冻结基准集 `core+boundary`,其中 `boundary` 场景全部为背离圆心发射的边界定向
|
||||||
|
源,是最不利工况。
|
||||||
|
- 指标:全清率、缺席误判数、虚拟时间、后备清除次数、墙钟。
|
||||||
|
|
||||||
|
## 4. 运行方式
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv run python -m project.run # 四问全部,写入 project/results/
|
||||||
|
uv run python -m project.run --question 1 # 只跑问题一
|
||||||
|
uv run python -m project.run --quick # 缩小随机算例/基准集规模
|
||||||
|
```
|
||||||
|
|
||||||
|
> `project/` 用到 `numpy`(网格参考解)。它随 dev 依赖组(optuna)安装;
|
||||||
|
> 只跑正式求解入口 `mcm26b` 不需要它。
|
||||||
|
|
||||||
|
## 5. 结果文件
|
||||||
|
|
||||||
|
| 文件 | 内容 |
|
||||||
|
| --- | --- |
|
||||||
|
| `results/q1.md` / `q1.json` | 固定示例 + 随机算例的逐案明细与汇总 |
|
||||||
|
| `results/q2.md` / `q2.json` | 逐案第二点位置、保证接收、最坏后验与汇总 |
|
||||||
|
| `results/q3.md` / `q3.json` | 冻结基准集逐场景明细、逐方法汇总、配对变化 |
|
||||||
|
| `results/q4.md` / `q4.json` | core+boundary 逐场景明细与汇总 |
|
||||||
|
| `results/summary.md` | 四问核心指标总览 + 方法映射 + 目录结构 |
|
||||||
|
|
||||||
|
## 6. 口径与边界(写论文时必须一并声明)
|
||||||
|
|
||||||
|
- 问题一参考解与最优实现都是精确算法;固定示例的解析期望另有
|
||||||
|
`tests/test_geometry*.py` 固化,本框架在其上补齐"朴素基线错在哪"。
|
||||||
|
- 问题二"最坏后验半径"是**对候选集外包多边形的启发式评估**,不是连续问题
|
||||||
|
的全局最优证明;生产实现与参考网格共享该目标函数,只比较搜索质量。
|
||||||
|
- 问题三、四的随机分布(源位置均匀、误差场逐点固定等)是**测试假设**,
|
||||||
|
题面未规定;正式成绩以官方模拟器演练/正式测试为准。
|
||||||
|
- 基线不是稻草人:`q2` 的 MEC 圆心、`q3` 的 `seven`、`q4` 的 `grid` 都是
|
||||||
|
能独立跑通、在部分场景有效的真实方法;它们暴露的是"缺了哪一步"。
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
"""1-4 问"最优实现 / 基线实现"统一对比框架(非运行时依赖,仅用于分析与论文)。
|
||||||
|
|
||||||
|
本包不改动正式求解入口 ``mcm26b``,只做三层组织:
|
||||||
|
|
||||||
|
- ``project.q1`` … ``project.q4``:四个问题各自的
|
||||||
|
``optimal.py``(生产最优实现,直接复用 ``mcm26b`` 内核)、
|
||||||
|
``baseline.py``(朴素基线,参照组)、``compare.py``(同题配对对比);
|
||||||
|
- ``project.common``:方法描述、指标聚合、结果落盘、离线闭环装配;
|
||||||
|
- ``project.run``:统一入口,一次跑完四问并生成 ``project/results/``。
|
||||||
|
|
||||||
|
运行方式(仓库根目录)::
|
||||||
|
|
||||||
|
uv run python -m project.run # 四问全部
|
||||||
|
uv run python -m project.run --question 3 # 只跑第三问
|
||||||
|
uv run python -m project.run --quick # 缩小随机场景数
|
||||||
|
"""
|
||||||
|
|
||||||
|
__all__ = ["common"]
|
||||||
@@ -0,0 +1,308 @@
|
|||||||
|
"""对比框架公共层:方法描述、指标聚合、结果落盘、离线闭环装配。
|
||||||
|
|
||||||
|
设计口径
|
||||||
|
--------
|
||||||
|
- **最优实现** = 复用 ``src/mcm26b`` 中已由测试固化的生产实现;
|
||||||
|
- **基线实现** = 同题下更朴素但可独立运行的参照实现(不得共享最优实现的
|
||||||
|
关键步骤),用于量化"生产实现好在哪里、差多少";
|
||||||
|
- **参考解** = 与分析算法相互独立的数值/枚举标准,用来判定谁对谁错。
|
||||||
|
|
||||||
|
第三、四问是闭环问题,统一走 ``LocalSimulator`` 内存回环(与官方 HTTP
|
||||||
|
协议同一边界),真值只在评测机侧,策略端只看响应。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import statistics
|
||||||
|
from dataclasses import dataclass, replace
|
||||||
|
from pathlib import Path
|
||||||
|
from time import perf_counter
|
||||||
|
|
||||||
|
from mcm26b.config import ProblemConfig
|
||||||
|
from mcm26b.evaluation import recompute_virtual_time
|
||||||
|
from mcm26b.harness import LocalSimTransport, LocalSimulator
|
||||||
|
from mcm26b.protocol.client import SimulatorClient
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
RESULTS_DIR = Path(__file__).resolve().parent / "results"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class Method:
|
||||||
|
"""一个可对比方法:key 用作图表列名,kind 用于区分最优/基线/参考。"""
|
||||||
|
|
||||||
|
key: str
|
||||||
|
name: str
|
||||||
|
kind: str # optimal | baseline | reference
|
||||||
|
note: str = ""
|
||||||
|
|
||||||
|
|
||||||
|
def problem_config(*, persistent_search: bool = False,
|
||||||
|
margin_deg: float | None = None) -> ProblemConfig:
|
||||||
|
"""对比口径配置:默认关闭"扫到限时"的持续复扫(对齐首次找全证明)。"""
|
||||||
|
base = ProblemConfig()
|
||||||
|
kwargs: dict = {"persistent_search": persistent_search}
|
||||||
|
if margin_deg is not None:
|
||||||
|
kwargs["bearing_rounding_margin_deg"] = margin_deg
|
||||||
|
return replace(base, search=replace(base.search, **kwargs))
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- 结果落盘
|
||||||
|
def write_json(path: Path, payload) -> Path:
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
path.write_text(json.dumps(payload, ensure_ascii=False, indent=2,
|
||||||
|
allow_nan=False) + "\n", encoding="utf-8")
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def write_text(path: Path, text: str) -> Path:
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
path.write_text(text, encoding="utf-8")
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- 指标工具
|
||||||
|
def mean(values) -> float:
|
||||||
|
values = list(values)
|
||||||
|
return round(statistics.fmean(values), 4) if values else 0.0
|
||||||
|
|
||||||
|
|
||||||
|
def median(values) -> float:
|
||||||
|
values = list(values)
|
||||||
|
return round(statistics.median(values), 4) if values else 0.0
|
||||||
|
|
||||||
|
|
||||||
|
def ratio(hit: int, total: int) -> float:
|
||||||
|
return round(hit / total, 4) if total else 0.0
|
||||||
|
|
||||||
|
|
||||||
|
def relative_change(value: float, reference: float) -> float | None:
|
||||||
|
if reference == 0:
|
||||||
|
return None
|
||||||
|
return round(value / reference - 1.0, 4)
|
||||||
|
|
||||||
|
|
||||||
|
def markdown_table(headers: list[str], rows: list[list[str]]) -> str:
|
||||||
|
lines = ["| " + " | ".join(headers) + " |",
|
||||||
|
"| " + " | ".join("---" for _ in headers) + " |"]
|
||||||
|
for row in rows:
|
||||||
|
lines.append("| " + " | ".join(str(cell) for cell in row) + " |")
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def _num(value, digits: int = 2) -> str:
|
||||||
|
if value is None:
|
||||||
|
return "—"
|
||||||
|
if isinstance(value, bool):
|
||||||
|
return "✔" if value else "✘"
|
||||||
|
if isinstance(value, float):
|
||||||
|
return f"{value:.{digits}f}"
|
||||||
|
return str(value)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- 第三/四问闭环
|
||||||
|
def run_loop_episode(loop_cls, scene_spec, config: ProblemConfig) -> dict:
|
||||||
|
"""在冻结场景上跑一局闭环,返回逐局指标行(与 ``scripts/run_suite.py`` 同口径)。"""
|
||||||
|
sources = scene_spec.sources(config) # 真值:仅测试端可见
|
||||||
|
simulator = LocalSimulator(sources, robot_id="COMPARE", config=config,
|
||||||
|
error_seed=scene_spec.error_seed)
|
||||||
|
client = SimulatorClient("COMPARE", config=config,
|
||||||
|
transport=LocalSimTransport(simulator))
|
||||||
|
start = perf_counter()
|
||||||
|
loop = loop_cls(client, config)
|
||||||
|
summary = loop.run()
|
||||||
|
compute_wall_s = perf_counter() - start
|
||||||
|
report = recompute_virtual_time(client.log.action_events())
|
||||||
|
|
||||||
|
truth = sorted(source.channel for source in sources)
|
||||||
|
cleared = sorted(summary["cleared_channels"])
|
||||||
|
absent = sorted(summary["absent_channels"])
|
||||||
|
misjudged = sorted(set(absent) & set(truth))
|
||||||
|
valid = (cleared == truth and not misjudged
|
||||||
|
and summary["exit_reason"] == "user_exit"
|
||||||
|
and not summary["anomalies"]
|
||||||
|
and not report["mismatches"])
|
||||||
|
return {
|
||||||
|
"scene_id": scene_spec.scene_id,
|
||||||
|
"problem": scene_spec.problem,
|
||||||
|
"kind": scene_spec.kind,
|
||||||
|
"sources": len(truth),
|
||||||
|
"directional": scene_spec.directional,
|
||||||
|
"cleared": len(cleared),
|
||||||
|
"cleared_ratio": ratio(len(cleared), len(truth)),
|
||||||
|
"cleared_matches_truth": cleared == truth,
|
||||||
|
"absent_misjudged": len(misjudged),
|
||||||
|
"virtual_time_s": round(summary["virtual_time_s"], 3),
|
||||||
|
"accounted_virtual_time_s": (
|
||||||
|
round(summary["accounted_virtual_time_s"], 3)
|
||||||
|
if summary.get("accounted_virtual_time_s") is not None else None),
|
||||||
|
"measures": summary["measures"],
|
||||||
|
"clears": summary["clears"],
|
||||||
|
"clear_success": summary["clear_success"],
|
||||||
|
"total_requests": summary["total_requests"],
|
||||||
|
"move_distance_m": round(summary["move_distance_m"], 2),
|
||||||
|
"fallback_clear_attempts": summary.get("fallback_clear_attempts", 0),
|
||||||
|
"coverage_points": summary.get("coverage_points", 0),
|
||||||
|
"stop_reason": summary["stop_reason"],
|
||||||
|
"exit_reason": summary["exit_reason"],
|
||||||
|
"anomaly_count": len(summary["anomalies"]),
|
||||||
|
"virtual_recompute_mismatches": len(report["mismatches"]),
|
||||||
|
"compute_wall_s": round(compute_wall_s, 3),
|
||||||
|
"valid": valid,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# 第三/四问统一汇报列(顺序即 Markdown 列序)
|
||||||
|
EPISODE_COLUMNS = (
|
||||||
|
"cleared_ratio", "cleared_matches_truth", "absent_misjudged",
|
||||||
|
"virtual_time_s", "measures", "total_requests", "move_distance_m",
|
||||||
|
"fallback_clear_attempts", "compute_wall_s", "valid",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def aggregate_episodes(rows: list[dict], labels: list[str],
|
||||||
|
base_label: str | None = None) -> dict:
|
||||||
|
"""逐方法汇总 + 相对基线的配对变化(按 scene_id 对齐)。"""
|
||||||
|
summary: dict[str, dict] = {}
|
||||||
|
for label in labels:
|
||||||
|
part = [row for row in rows if row["variant"] == label]
|
||||||
|
if not part:
|
||||||
|
continue
|
||||||
|
summary[label] = {
|
||||||
|
"episodes": len(part),
|
||||||
|
"valid_episodes": sum(1 for row in part if row["valid"]),
|
||||||
|
"cleared_ratio_mean": mean(row["cleared_ratio"] for row in part),
|
||||||
|
"cleared_matches_truth": sum(1 for row in part
|
||||||
|
if row["cleared_matches_truth"]),
|
||||||
|
"absent_misjudged_total": sum(row["absent_misjudged"] for row in part),
|
||||||
|
"virtual_time_mean_s": mean(row["virtual_time_s"] for row in part),
|
||||||
|
"virtual_time_median_s": median(row["virtual_time_s"] for row in part),
|
||||||
|
"virtual_time_max_s": round(max(row["virtual_time_s"] for row in part), 3),
|
||||||
|
"measures_mean": mean(row["measures"] for row in part),
|
||||||
|
"total_requests_mean": mean(row["total_requests"] for row in part),
|
||||||
|
"move_distance_mean_m": mean(row["move_distance_m"] for row in part),
|
||||||
|
"fallback_clear_attempts_mean": mean(
|
||||||
|
row["fallback_clear_attempts"] for row in part),
|
||||||
|
"compute_wall_mean_s": mean(row["compute_wall_s"] for row in part),
|
||||||
|
}
|
||||||
|
paired: dict[str, dict] = {}
|
||||||
|
if base_label and len(labels) > 1:
|
||||||
|
base_rows = {row["scene_id"]: row for row in rows
|
||||||
|
if row["variant"] == base_label}
|
||||||
|
for label in labels:
|
||||||
|
if label == base_label:
|
||||||
|
continue
|
||||||
|
deltas, faster, valid_pairs = [], 0, 0
|
||||||
|
for row in rows:
|
||||||
|
if row["variant"] != label or row["scene_id"] not in base_rows:
|
||||||
|
continue
|
||||||
|
base_row = base_rows[row["scene_id"]]
|
||||||
|
reference = base_row["virtual_time_s"]
|
||||||
|
deltas.append(relative_change(row["virtual_time_s"], reference))
|
||||||
|
if not (row["valid"] and base_row["valid"]):
|
||||||
|
continue
|
||||||
|
valid_pairs += 1
|
||||||
|
if row["virtual_time_s"] < reference:
|
||||||
|
faster += 1
|
||||||
|
paired[f"{label}_vs_{base_label}"] = {
|
||||||
|
"mean_relative_change": mean(d for d in deltas if d is not None),
|
||||||
|
"median_relative_change": median(d for d in deltas if d is not None),
|
||||||
|
"faster_scenes": faster,
|
||||||
|
"compared": len(deltas),
|
||||||
|
"both_valid_scenes": valid_pairs,
|
||||||
|
}
|
||||||
|
return {"variants": summary, "paired": paired}
|
||||||
|
|
||||||
|
|
||||||
|
def episodes_markdown(title: str, rows: list[dict], labels: list[str],
|
||||||
|
base_label: str | None = None) -> str:
|
||||||
|
"""逐场景明细 + 汇总表。"""
|
||||||
|
headers = ["scene", "sources", "variant", "cleared", "T(s)", "检测",
|
||||||
|
"请求", "位移(m)", "后备清除", "墙钟(s)", "有效"]
|
||||||
|
body = []
|
||||||
|
for row in sorted(rows, key=lambda r: (r["scene_id"], labels.index(r["variant"]))):
|
||||||
|
body.append([
|
||||||
|
row["scene_id"], row["sources"], row["variant"],
|
||||||
|
f"{row['cleared']}/{row['sources']}",
|
||||||
|
_num(row["virtual_time_s"], 1), row["measures"],
|
||||||
|
row["total_requests"], _num(row["move_distance_m"], 1),
|
||||||
|
row["fallback_clear_attempts"], _num(row["compute_wall_s"], 2),
|
||||||
|
_num(row["valid"]),
|
||||||
|
])
|
||||||
|
text = [f"# {title}", "", "## 逐场景明细", "",
|
||||||
|
markdown_table(headers, body), "", "## 逐方法汇总", ""]
|
||||||
|
agg = aggregate_episodes(rows, labels, base_label)
|
||||||
|
agg_headers = ["方法", "有效局", "全清率", "全清对数", "缺席误判",
|
||||||
|
"T 均值(s)", "T 中位(s)", "T 最大(s)", "检测均值",
|
||||||
|
"请求均值", "位移均值(m)", "后备清除均值", "墙钟均值(s)"]
|
||||||
|
agg_rows = []
|
||||||
|
for label in labels:
|
||||||
|
s = agg["variants"].get(label)
|
||||||
|
if not s:
|
||||||
|
continue
|
||||||
|
agg_rows.append([
|
||||||
|
label, f"{s['valid_episodes']}/{s['episodes']}",
|
||||||
|
_num(s["cleared_ratio_mean"]), s["cleared_matches_truth"],
|
||||||
|
s["absent_misjudged_total"], _num(s["virtual_time_mean_s"], 1),
|
||||||
|
_num(s["virtual_time_median_s"], 1), _num(s["virtual_time_max_s"], 1),
|
||||||
|
_num(s["measures_mean"], 1), _num(s["total_requests_mean"], 1),
|
||||||
|
_num(s["move_distance_mean_m"], 1),
|
||||||
|
_num(s["fallback_clear_attempts_mean"], 2),
|
||||||
|
_num(s["compute_wall_mean_s"], 2),
|
||||||
|
])
|
||||||
|
text.append(markdown_table(agg_headers, agg_rows))
|
||||||
|
if agg["paired"]:
|
||||||
|
text += ["", "## 相对基线的配对变化(虚拟时间)", ""]
|
||||||
|
pair_rows = [[name, f"{v['mean_relative_change']*100:+.1f}%",
|
||||||
|
f"{v['median_relative_change']*100:+.1f}%",
|
||||||
|
f"{v['faster_scenes']}/{v['compared']}",
|
||||||
|
v["both_valid_scenes"]]
|
||||||
|
for name, v in agg["paired"].items()]
|
||||||
|
text.append(markdown_table(
|
||||||
|
["配对", "均值变化", "中位变化", "更快场景", "双方均有效"], pair_rows))
|
||||||
|
invalid = [name for name, value in agg["paired"].items()
|
||||||
|
if value["both_valid_scenes"] < value["compared"]]
|
||||||
|
if invalid:
|
||||||
|
text += ["",
|
||||||
|
"> 注:" + "、".join(invalid)
|
||||||
|
+ " 存在未通过有效性判定的配对;其虚拟时间仅作参考,"
|
||||||
|
"基线未完成全清任务时不构成「更快」证据"
|
||||||
|
"(以「双方均有效」列为准)。"]
|
||||||
|
return "\n".join(text) + "\n"
|
||||||
|
|
||||||
|
|
||||||
|
def json_safe(payload):
|
||||||
|
"""把 numpy 标量等转成 json 可写类型(防御式,非必需)。"""
|
||||||
|
return json.loads(json.dumps(payload, ensure_ascii=False, default=float))
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- 闭环批量执行
|
||||||
|
def execute_variants(scenes, variants, config: ProblemConfig) -> list[dict]:
|
||||||
|
"""对每个场景跑遍所有变体(串行;对比规模小,进程池收益有限且便于复现)。"""
|
||||||
|
rows = []
|
||||||
|
for scene in scenes:
|
||||||
|
for label, loop_cls in variants:
|
||||||
|
row = run_loop_episode(loop_cls, scene, config)
|
||||||
|
row["variant"] = label
|
||||||
|
rows.append(row)
|
||||||
|
return rows
|
||||||
|
|
||||||
|
|
||||||
|
def closed_loop_payload(question: int, suite_name: str, scenes, variants,
|
||||||
|
rows: list[dict], labels: list[str],
|
||||||
|
base_label: str, config: ProblemConfig) -> dict:
|
||||||
|
return {
|
||||||
|
"question": question,
|
||||||
|
"suite": suite_name,
|
||||||
|
"scenes": [scene.as_dict() for scene in scenes],
|
||||||
|
"variants": [{"label": label, "class": loop_cls.__name__}
|
||||||
|
for label, loop_cls in variants],
|
||||||
|
"config": {
|
||||||
|
"persistent_search": config.search.persistent_search,
|
||||||
|
"bearing_rounding_margin_deg": config.search.bearing_rounding_margin_deg,
|
||||||
|
"q4_coverage_plan": config.search.q4_coverage_plan,
|
||||||
|
},
|
||||||
|
"rows": rows,
|
||||||
|
"aggregate": aggregate_episodes(rows, labels, base_label),
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
"""问题一:交会定位多边形直径与直径圆覆盖。
|
||||||
|
|
||||||
|
- ``optimal.py`` :生产实现 ``mcm26b.geometry.localization.solve``(半平面交 +
|
||||||
|
顶点对精确直径 + Welzl 最小包围圆 + 覆盖反例见证);
|
||||||
|
- ``baseline.py`` :基线实现(网格采样 + 轴对齐包围盒代理直径/覆盖);
|
||||||
|
- ``reference.py``:独立精确参考解(大框半平面裁剪 + 暴力直径/最小包围圆);
|
||||||
|
- ``cases.py`` :固定示例(``examples/q1``)与随机算例生成;
|
||||||
|
- ``compare.py`` :同案配对对比并落盘。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from . import baseline, cases, optimal, reference
|
||||||
|
|
||||||
|
__all__ = ["optimal", "baseline", "reference", "cases"]
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
"""问题一基线实现:网格采样 + 轴对齐包围盒(bbox)代理。
|
||||||
|
|
||||||
|
这是"先上手"的朴素做法,常见于直接对可行点打网格再读包围盒:
|
||||||
|
|
||||||
|
1. 在固定大框上按步长采样,保留满足全部角域约束的点;
|
||||||
|
2. 直径用包围盒对角线代理 —— 只要区域是斜的/细长的就会系统性高估;
|
||||||
|
3. 覆盖判断用"包围盒中心 + 代理直径"的圆 —— 因为包围盒自身对其中心对称,
|
||||||
|
该判据几乎恒为"能覆盖",从而丢掉问题一真正要考的反例。
|
||||||
|
|
||||||
|
本实现刻意保留这些结构性缺陷,作为``optimal.py``的对照;它不读取最优实现的
|
||||||
|
任何中间量,两条代码路径完全独立。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import math
|
||||||
|
from time import perf_counter
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from mcm26b.geometry.localization import Measurement, halfplanes
|
||||||
|
|
||||||
|
from project.common import Method
|
||||||
|
|
||||||
|
METHOD = Method(
|
||||||
|
key="baseline",
|
||||||
|
name="网格采样 + bbox 对角线代理",
|
||||||
|
kind="baseline",
|
||||||
|
note="轴对齐包围盒近似直径与覆盖,O(盒面积/步长²),与参考解同分辨率",
|
||||||
|
)
|
||||||
|
|
||||||
|
BOX_M = 2600.0
|
||||||
|
STEP_M = 5.0
|
||||||
|
|
||||||
|
|
||||||
|
def solve_case(case: dict) -> dict:
|
||||||
|
start = perf_counter()
|
||||||
|
measurements = [Measurement(row["x"], row["y"], row["bearing_deg"],
|
||||||
|
row.get("half_angle_deg", 1.0))
|
||||||
|
for row in case["measurements"]]
|
||||||
|
planes = [plane for m in measurements for plane in halfplanes(m)]
|
||||||
|
|
||||||
|
xs = np.arange(-BOX_M, BOX_M + STEP_M, STEP_M)
|
||||||
|
grid_x, grid_y = np.meshgrid(xs, xs) # 行=y,列=x
|
||||||
|
mask = np.ones(grid_x.shape, dtype=bool)
|
||||||
|
for a, b, c in planes:
|
||||||
|
mask &= (a * grid_x + b * grid_y <= c)
|
||||||
|
|
||||||
|
result = _evaluate(mask, xs)
|
||||||
|
result["method"] = METHOD.key
|
||||||
|
result["runtime_ms"] = round((perf_counter() - start) * 1000.0, 4)
|
||||||
|
result["algorithm"] = METHOD.name
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _evaluate(mask: np.ndarray, axis: np.ndarray) -> dict:
|
||||||
|
if not mask.any():
|
||||||
|
return _empty("empty")
|
||||||
|
# 可行点触到采样框边界 => 区域至少延伸到框外,判无界
|
||||||
|
if (mask[0, :].any() or mask[-1, :].any()
|
||||||
|
or mask[:, 0].any() or mask[:, -1].any()):
|
||||||
|
return _empty("unbounded")
|
||||||
|
|
||||||
|
rows, cols = np.nonzero(mask)
|
||||||
|
x_min, x_max = axis[cols].min(), axis[cols].max()
|
||||||
|
y_min, y_max = axis[rows].min(), axis[rows].max()
|
||||||
|
width, height = x_max - x_min, y_max - y_min
|
||||||
|
diameter = math.hypot(width, height) # 包围盒对角线代理
|
||||||
|
center = ((x_min + x_max) / 2.0, (y_min + y_max) / 2.0)
|
||||||
|
px = axis[cols]
|
||||||
|
py = axis[rows]
|
||||||
|
max_distance = float(np.max(np.hypot(px - center[0], py - center[1])))
|
||||||
|
return {
|
||||||
|
"status": "polygon" if mask.sum() >= 2 else "point",
|
||||||
|
"diameter_m": diameter,
|
||||||
|
"diameter_infinite": False,
|
||||||
|
"covers": bool(max_distance <= diameter / 2.0 + 1e-9),
|
||||||
|
"mec_radius_m": None,
|
||||||
|
"max_excess_m": round(max_distance - diameter / 2.0, 6),
|
||||||
|
"outside_vertex_count": None,
|
||||||
|
"vertex_count": int(mask.sum()),
|
||||||
|
"bbox_width_m": round(width, 3),
|
||||||
|
"bbox_height_m": round(height, 3),
|
||||||
|
"runtime_ms": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _empty(status: str) -> dict:
|
||||||
|
return {
|
||||||
|
"status": status,
|
||||||
|
"diameter_m": None,
|
||||||
|
"diameter_infinite": status == "unbounded",
|
||||||
|
"covers": None,
|
||||||
|
"mec_radius_m": None,
|
||||||
|
"max_excess_m": None,
|
||||||
|
"outside_vertex_count": None,
|
||||||
|
"vertex_count": 0,
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
"""问题一算例:官方示例目录 ``examples/q1`` + 随机交会算例。"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import math
|
||||||
|
import random
|
||||||
|
|
||||||
|
from project.common import ROOT
|
||||||
|
|
||||||
|
EXAMPLE_DIR = ROOT / "examples" / "q1"
|
||||||
|
EXAMPLE_IDS = ("normal", "triangle", "poor", "segment", "empty")
|
||||||
|
|
||||||
|
|
||||||
|
def load_examples() -> list[dict]:
|
||||||
|
"""读取固定算例;缺文件时跳过,保证离线可跑。"""
|
||||||
|
cases = []
|
||||||
|
for key in EXAMPLE_IDS:
|
||||||
|
path = EXAMPLE_DIR / f"{key}.json"
|
||||||
|
if not path.exists():
|
||||||
|
continue
|
||||||
|
raw = json.loads(path.read_text(encoding="utf-8-sig"))
|
||||||
|
cases.append({
|
||||||
|
"id": raw.get("id", key),
|
||||||
|
"name": raw.get("name", key),
|
||||||
|
"source": "examples/q1",
|
||||||
|
"measurements": [
|
||||||
|
{"x": float(row["x"]), "y": float(row["y"]),
|
||||||
|
"bearing_deg": float(row["bearing_deg"]),
|
||||||
|
"half_angle_deg": float(row.get("half_angle_deg", 1.0))}
|
||||||
|
for row in raw["measurements"]
|
||||||
|
],
|
||||||
|
})
|
||||||
|
return cases
|
||||||
|
|
||||||
|
|
||||||
|
def random_case(seed: int) -> dict:
|
||||||
|
"""随机三点/四点交会:目标在 Ω 内,测站分布在不同象限。
|
||||||
|
|
||||||
|
仅生成"区域有界且非退化"的良态算例,避免参考网格因细长区域失效;
|
||||||
|
生成后由 ``compare`` 再用最优实现复核状态。
|
||||||
|
"""
|
||||||
|
rng = random.Random(seed)
|
||||||
|
target = (rng.uniform(-900, 900), rng.uniform(-900, 900))
|
||||||
|
count = rng.choice((3, 3, 4, 5))
|
||||||
|
stations = []
|
||||||
|
base_angle = rng.uniform(0, 360)
|
||||||
|
for index in range(count):
|
||||||
|
angle = base_angle + 360 * index / count + rng.uniform(-25, 25)
|
||||||
|
radius = rng.uniform(500, 1100)
|
||||||
|
stations.append((target[0] + radius * math.cos(math.radians(angle)),
|
||||||
|
target[1] + radius * math.sin(math.radians(angle))))
|
||||||
|
measurements = []
|
||||||
|
for (sx, sy) in stations:
|
||||||
|
true_bearing = math.degrees(math.atan2(target[1] - sy, target[0] - sx)) % 360.0
|
||||||
|
# 观测误差固定在同一地点、落在 ±1° 内;读数保留两位小数
|
||||||
|
error = rng.uniform(-0.9, 0.9)
|
||||||
|
measurements.append({"x": round(sx, 2), "y": round(sy, 2),
|
||||||
|
"bearing_deg": round((true_bearing + error) % 360.0, 2),
|
||||||
|
"half_angle_deg": 1.0})
|
||||||
|
return {"id": f"rand-{seed}", "name": "随机交会", "source": "synthetic",
|
||||||
|
"target": [round(target[0], 2), round(target[1], 2)],
|
||||||
|
"measurements": measurements}
|
||||||
|
|
||||||
|
|
||||||
|
def random_cases(count: int, base_seed: int = 7000) -> list[dict]:
|
||||||
|
return [random_case(base_seed + index) for index in range(count)]
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
"""问题一同案配对对比:最优 vs 基线 vs 独立参考解。"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from project.common import (Method, RESULTS_DIR, markdown_table, mean,
|
||||||
|
ratio, write_json, write_text)
|
||||||
|
from project.q1 import baseline, cases, optimal, reference
|
||||||
|
|
||||||
|
METHODS: tuple[Method, ...] = (optimal.METHOD, baseline.METHOD, reference.METHOD)
|
||||||
|
FINITE = ("point", "segment", "polygon")
|
||||||
|
|
||||||
|
|
||||||
|
def _run(case: dict) -> dict:
|
||||||
|
opt = optimal.solve_case(case)
|
||||||
|
base = baseline.solve_case(case)
|
||||||
|
ref = reference.solve_case(case)
|
||||||
|
finite = ref["status"] in FINITE and ref["diameter_m"] is not None
|
||||||
|
return {
|
||||||
|
"id": case["id"],
|
||||||
|
"name": case.get("name", case["id"]),
|
||||||
|
"source": case.get("source", "synthetic"),
|
||||||
|
"measurements": len(case["measurements"]),
|
||||||
|
"status_ref": ref["status"],
|
||||||
|
"status_opt": opt["status"],
|
||||||
|
"status_base": base["status"],
|
||||||
|
"status_opt_ok": opt["status"] == ref["status"],
|
||||||
|
"status_base_ok": base["status"] == ref["status"],
|
||||||
|
"diameter_ref_m": ref["diameter_m"],
|
||||||
|
"diameter_opt_m": opt["diameter_m"],
|
||||||
|
"diameter_base_m": base["diameter_m"],
|
||||||
|
"abs_err_opt_m": (round(abs(opt["diameter_m"] - ref["diameter_m"]), 4)
|
||||||
|
if finite and opt["diameter_m"] is not None else None),
|
||||||
|
"abs_err_base_m": (round(abs(base["diameter_m"] - ref["diameter_m"]), 4)
|
||||||
|
if finite and base["diameter_m"] is not None else None),
|
||||||
|
"covers_ref": ref["covers"],
|
||||||
|
"covers_opt": opt["covers"],
|
||||||
|
"covers_base": base["covers"],
|
||||||
|
"covers_opt_ok": (opt["covers"] == ref["covers"]
|
||||||
|
if ref["covers"] is not None and opt["covers"] is not None
|
||||||
|
else None),
|
||||||
|
"covers_base_ok": (base["covers"] == ref["covers"]
|
||||||
|
if ref["covers"] is not None and base["covers"] is not None
|
||||||
|
else None),
|
||||||
|
"mec_radius_opt_m": opt["mec_radius_m"],
|
||||||
|
"mec_radius_ref_m": ref["mec_radius_m"],
|
||||||
|
"outside_vertices_opt": opt["outside_vertex_count"],
|
||||||
|
"runtime_opt_ms": opt["runtime_ms"],
|
||||||
|
"runtime_base_ms": base["runtime_ms"],
|
||||||
|
"runtime_ref_ms": ref["runtime_ms"],
|
||||||
|
"reference_resolution_m": ref["resolution_m"],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _aggregate(rows: list[dict]) -> dict:
|
||||||
|
polygon_rows = [r for r in rows if r["status_ref"] == "polygon"]
|
||||||
|
opt_errs = [r["abs_err_opt_m"] for r in polygon_rows
|
||||||
|
if r["abs_err_opt_m"] is not None]
|
||||||
|
base_errs = [r["abs_err_base_m"] for r in polygon_rows
|
||||||
|
if r["abs_err_base_m"] is not None]
|
||||||
|
cov_opt = [r["covers_opt_ok"] for r in rows if r["covers_opt_ok"] is not None]
|
||||||
|
cov_base = [r["covers_base_ok"] for r in rows if r["covers_base_ok"] is not None]
|
||||||
|
return {
|
||||||
|
"cases": len(rows),
|
||||||
|
"diameter_compared": len(opt_errs),
|
||||||
|
"status_accuracy_optimal": ratio(sum(r["status_opt_ok"] for r in rows), len(rows)),
|
||||||
|
"status_accuracy_baseline": ratio(sum(r["status_base_ok"] for r in rows), len(rows)),
|
||||||
|
"diameter_mae_optimal_m": mean(opt_errs),
|
||||||
|
"diameter_mae_baseline_m": mean(base_errs),
|
||||||
|
"diameter_max_err_optimal_m": round(max(opt_errs), 4) if opt_errs else 0.0,
|
||||||
|
"diameter_max_err_baseline_m": round(max(base_errs), 4) if base_errs else 0.0,
|
||||||
|
"coverage_compared": len(cov_opt),
|
||||||
|
"coverage_accuracy_optimal": ratio(sum(cov_opt), len(cov_opt)),
|
||||||
|
"coverage_accuracy_baseline": ratio(sum(cov_base), len(cov_base)),
|
||||||
|
"runtime_optimal_mean_ms": mean(r["runtime_opt_ms"] for r in rows),
|
||||||
|
"runtime_baseline_mean_ms": mean(r["runtime_base_ms"] for r in rows),
|
||||||
|
"runtime_reference_mean_ms": mean(r["runtime_ref_ms"] for r in rows),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _render(rows: list[dict], aggregate: dict, title: str) -> str:
|
||||||
|
headers = ["算例", "来源", "参考状态", "最优状态", "基线状态",
|
||||||
|
"D参考(m)", "D最优(m)", "D基线(m)", "|ΔD|最优", "|ΔD|基线",
|
||||||
|
"覆盖参考", "覆盖最优", "覆盖基线"]
|
||||||
|
body = []
|
||||||
|
for r in rows:
|
||||||
|
body.append([
|
||||||
|
r["name"], r["source"], r["status_ref"], r["status_opt"], r["status_base"],
|
||||||
|
_fmt(r["diameter_ref_m"]), _fmt(r["diameter_opt_m"]),
|
||||||
|
_fmt(r["diameter_base_m"]),
|
||||||
|
_fmt(r["abs_err_opt_m"]), _fmt(r["abs_err_base_m"]),
|
||||||
|
_bool(r["covers_ref"]), _bool(r["covers_opt"]), _bool(r["covers_base"]),
|
||||||
|
])
|
||||||
|
text = [f"# {title}", "",
|
||||||
|
"参考解为独立的大框半平面裁剪 + 精确直径/MEC;",
|
||||||
|
"固定示例见 `examples/q1`,解析期望由 `tests/test_geometry*.py` 固化。",
|
||||||
|
"", markdown_table(headers, body), "",
|
||||||
|
"## 汇总", ""]
|
||||||
|
summary_rows = [
|
||||||
|
["状态判定准确率", _pct(aggregate["status_accuracy_optimal"]),
|
||||||
|
_pct(aggregate["status_accuracy_baseline"])],
|
||||||
|
["直径平均绝对误差 (m)", _fmt(aggregate["diameter_mae_optimal_m"], 3),
|
||||||
|
_fmt(aggregate["diameter_mae_baseline_m"], 3)],
|
||||||
|
["直径最大误差 (m)", _fmt(aggregate["diameter_max_err_optimal_m"], 3),
|
||||||
|
_fmt(aggregate["diameter_max_err_baseline_m"], 3)],
|
||||||
|
["覆盖判定准确率", _pct(aggregate["coverage_accuracy_optimal"]),
|
||||||
|
_pct(aggregate["coverage_accuracy_baseline"])],
|
||||||
|
["平均耗时 (ms)", _fmt(aggregate["runtime_optimal_mean_ms"], 3),
|
||||||
|
_fmt(aggregate["runtime_baseline_mean_ms"], 3)],
|
||||||
|
]
|
||||||
|
text.append(markdown_table(["指标", "最优", "基线"], summary_rows))
|
||||||
|
return "\n".join(text) + "\n"
|
||||||
|
|
||||||
|
|
||||||
|
def _fmt(value, digits: int = 2) -> str:
|
||||||
|
if value is None:
|
||||||
|
return "—"
|
||||||
|
if isinstance(value, bool):
|
||||||
|
return "是" if value else "否"
|
||||||
|
if isinstance(value, float):
|
||||||
|
return f"{value:.{digits}f}"
|
||||||
|
return str(value)
|
||||||
|
|
||||||
|
|
||||||
|
def _bool(value) -> str:
|
||||||
|
return "—" if value is None else ("是" if value else "否")
|
||||||
|
|
||||||
|
|
||||||
|
def _pct(value: float) -> str:
|
||||||
|
return f"{value*100:.1f}%"
|
||||||
|
|
||||||
|
|
||||||
|
def run(quick: bool = False) -> dict:
|
||||||
|
example_rows = [_run(case) for case in cases.load_examples()]
|
||||||
|
random_rows = [_run(case) for case in
|
||||||
|
cases.random_cases(12 if quick else 40)]
|
||||||
|
payload = {
|
||||||
|
"question": 1,
|
||||||
|
"methods": [m.__dict__ for m in METHODS],
|
||||||
|
"examples": example_rows,
|
||||||
|
"random": random_rows,
|
||||||
|
"aggregate_examples": _aggregate(example_rows),
|
||||||
|
"aggregate_random": _aggregate(random_rows),
|
||||||
|
}
|
||||||
|
write_json(RESULTS_DIR / "q1.json", payload)
|
||||||
|
text = _render(example_rows, payload["aggregate_examples"],
|
||||||
|
"问题一:固定示例对比")
|
||||||
|
text += "\n" + _render(random_rows, payload["aggregate_random"],
|
||||||
|
"问题一:随机交会算例对比")
|
||||||
|
write_text(RESULTS_DIR / "q1.md", text)
|
||||||
|
return payload
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
"""问题一最优实现:复用生产几何内核 ``mcm26b.geometry.localization.solve``。
|
||||||
|
|
||||||
|
算法(已由 ``tests/test_geometry.py`` / ``tests/test_geometry_certificates.py``
|
||||||
|
固化,本模块只做输入输出适配,不重写):
|
||||||
|
|
||||||
|
1. 每条带 ±δ 误差的示向度写成前向角域的三条半平面约束(含 0°/360° 跨界);
|
||||||
|
2. 枚举全部边界直线交点,保留满足全部约束者,单调链求凸包;
|
||||||
|
3. 检查是否存在非零衰退方向判定无界;否则枚举凸包顶点对取精确直径;
|
||||||
|
4. 用直径两端点中点圆检验覆盖,并给出圆外顶点、最大超出距离等反例见证;
|
||||||
|
5. Welzl 增量法求最小包围圆,给出 20 m 清除余量证书。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from time import perf_counter
|
||||||
|
|
||||||
|
from mcm26b.geometry.localization import Measurement, solve
|
||||||
|
|
||||||
|
from project.common import Method
|
||||||
|
|
||||||
|
METHOD = Method(
|
||||||
|
key="optimal",
|
||||||
|
name="精确半平面交 + 顶点对直径 + MEC",
|
||||||
|
kind="optimal",
|
||||||
|
note="生产内核 mcm26b.geometry.localization.solve",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def solve_case(case: dict) -> dict:
|
||||||
|
"""输入 case["measurements"],输出归一化结果。"""
|
||||||
|
measurements = [Measurement(row["x"], row["y"], row["bearing_deg"],
|
||||||
|
row.get("half_angle_deg", 1.0))
|
||||||
|
for row in case["measurements"]]
|
||||||
|
start = perf_counter()
|
||||||
|
result = solve(measurements)
|
||||||
|
elapsed_ms = (perf_counter() - start) * 1000.0
|
||||||
|
return normalize(result, elapsed_ms)
|
||||||
|
|
||||||
|
|
||||||
|
def normalize(result: dict, elapsed_ms: float) -> dict:
|
||||||
|
status = result["status"]
|
||||||
|
diameter = result.get("diameter_m")
|
||||||
|
circle = result.get("diameter_circle") or {}
|
||||||
|
mec = result.get("minimum_enclosing_circle") or {}
|
||||||
|
covers = circle.get("covers") if status in ("point", "segment", "polygon") else None
|
||||||
|
return {
|
||||||
|
"method": METHOD.key,
|
||||||
|
"status": status,
|
||||||
|
"diameter_m": diameter,
|
||||||
|
"diameter_infinite": result.get("diameter_infinite"),
|
||||||
|
"covers": covers,
|
||||||
|
"mec_radius_m": mec.get("radius_m"),
|
||||||
|
"max_excess_m": circle.get("max_excess_m"),
|
||||||
|
"outside_vertex_count": len(circle.get("outside_vertex_indices", []))
|
||||||
|
if circle else None,
|
||||||
|
"vertex_count": len(result.get("vertices", [])),
|
||||||
|
"runtime_ms": round(elapsed_ms, 4),
|
||||||
|
"algorithm": METHOD.name,
|
||||||
|
}
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
"""问题一独立参考解:大框 Sutherland–Hodgman 半平面裁剪 + 精确直径/MEC。
|
||||||
|
|
||||||
|
与生产实现 ``mcm26b.geometry.localization.solve`` 的独立性:
|
||||||
|
|
||||||
|
- 生产实现枚举"半平面边界直线两两交点"再筛可行点;
|
||||||
|
- 本参考解从一个大方框出发,逐条半平面做 Sutherland–Hodgman 裁剪,
|
||||||
|
是另一条构造路径,且这里的半平面系数、直径、最小包围圆都在本文件内
|
||||||
|
独立编码(不复用生产实现的 ``halfplanes`` / ``diameter`` / ``mec``)。
|
||||||
|
|
||||||
|
只要区域有界,大框(±1e7 m)裁剪得到的顶点即精确多边形;顶点触到大框
|
||||||
|
边界则判无界。最小包围圆用"枚举所有 1/2/3 点支撑圆"的暴力算法,直径用
|
||||||
|
顶点对暴力枚举,二者都是 O(m²)/O(m³) 的朴素精确算法,m 为多边形顶点数。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import math
|
||||||
|
from time import perf_counter
|
||||||
|
|
||||||
|
from project.common import Method
|
||||||
|
|
||||||
|
METHOD = Method(
|
||||||
|
key="reference",
|
||||||
|
name="大框半平面裁剪 + 精确直径/MEC",
|
||||||
|
kind="reference",
|
||||||
|
note="独立于生产顶点枚举的精确参考解",
|
||||||
|
)
|
||||||
|
|
||||||
|
BOX_M = 1.0e7
|
||||||
|
_PARALLEL_EPS = 1e-12
|
||||||
|
|
||||||
|
|
||||||
|
def _planes(case: dict) -> list[tuple[float, float, float]]:
|
||||||
|
"""独立重写 a*x+b*y<=c 的半平面构造(与题面角域定义一致)。"""
|
||||||
|
planes = []
|
||||||
|
for row in case["measurements"]:
|
||||||
|
half = math.radians(row.get("half_angle_deg", 1.0))
|
||||||
|
theta = math.radians(row["bearing_deg"] % 360.0)
|
||||||
|
sx, sy = row["x"], row["y"]
|
||||||
|
for angle, sign in ((theta - half, 1.0), (theta + half, -1.0)):
|
||||||
|
a = sign * math.sin(angle)
|
||||||
|
b = -sign * math.cos(angle)
|
||||||
|
planes.append((a, b, a * sx + b * sy))
|
||||||
|
# 前向约束:不允许把角域取成反向无限双楔
|
||||||
|
a, b = -math.cos(theta), -math.sin(theta)
|
||||||
|
planes.append((a, b, a * sx + b * sy))
|
||||||
|
return planes
|
||||||
|
|
||||||
|
|
||||||
|
def _clip(polygon, a, b, c, tol=1e-7):
|
||||||
|
"""Sutherland–Hodgman:保留 a*x+b*y<=c 的一侧。"""
|
||||||
|
if not polygon:
|
||||||
|
return []
|
||||||
|
output = []
|
||||||
|
count = len(polygon)
|
||||||
|
for index in range(count):
|
||||||
|
p, q = polygon[index], polygon[(index + 1) % count]
|
||||||
|
fp = a * p[0] + b * p[1] - c
|
||||||
|
fq = a * q[0] + b * q[1] - c
|
||||||
|
p_in, q_in = fp <= tol, fq <= tol
|
||||||
|
if p_in:
|
||||||
|
output.append(p)
|
||||||
|
if p_in != q_in:
|
||||||
|
ratio = fp / (fp - fq)
|
||||||
|
output.append((p[0] + ratio * (q[0] - p[0]),
|
||||||
|
p[1] + ratio * (q[1] - p[1])))
|
||||||
|
deduped = []
|
||||||
|
for point in output:
|
||||||
|
if not deduped or math.dist(point, deduped[-1]) > 1e-9:
|
||||||
|
deduped.append(point)
|
||||||
|
if len(deduped) > 1 and math.dist(deduped[0], deduped[-1]) <= 1e-9:
|
||||||
|
deduped.pop()
|
||||||
|
return deduped
|
||||||
|
|
||||||
|
|
||||||
|
def _brute_force_diameter(points):
|
||||||
|
best = 0.0
|
||||||
|
pair = (points[0], points[0])
|
||||||
|
for i, pi in enumerate(points):
|
||||||
|
for pj in points[i + 1:]:
|
||||||
|
value = math.dist(pi, pj)
|
||||||
|
if value > best:
|
||||||
|
best, pair = value, (pi, pj)
|
||||||
|
return best, pair
|
||||||
|
|
||||||
|
|
||||||
|
def _circumcircle(a, b, c):
|
||||||
|
ux, uy = b[0] - a[0], b[1] - a[1]
|
||||||
|
vx, vy = c[0] - a[0], c[1] - a[1]
|
||||||
|
det = 2.0 * (ux * vy - uy * vx)
|
||||||
|
if abs(det) <= _PARALLEL_EPS * max(
|
||||||
|
math.hypot(ux, uy) * math.hypot(vx, vy), 1e-300):
|
||||||
|
return None
|
||||||
|
uu, vv = ux * ux + uy * uy, vx * vx + vy * vy
|
||||||
|
center = (a[0] + (uu * vy - vv * uy) / det,
|
||||||
|
a[1] + (ux * vv - vx * uu) / det)
|
||||||
|
return center, math.dist(center, a)
|
||||||
|
|
||||||
|
|
||||||
|
def _brute_force_mec(points):
|
||||||
|
"""枚举 1/2/3 点支撑圆的精确最小包围圆(朴素但无迭代)。"""
|
||||||
|
xs = [p[0] for p in points]
|
||||||
|
ys = [p[1] for p in points]
|
||||||
|
best_c = ((min(xs) + max(xs)) / 2.0, (min(ys) + max(ys)) / 2.0)
|
||||||
|
best_r = max(math.dist(best_c, p) for p in points)
|
||||||
|
candidates: list[tuple[tuple[float, float], float]] = []
|
||||||
|
for i, p in enumerate(points):
|
||||||
|
for q in points[i + 1:]:
|
||||||
|
candidates.append((((p[0] + q[0]) / 2.0, (p[1] + q[1]) / 2.0),
|
||||||
|
math.dist(p, q) / 2.0))
|
||||||
|
for i, p in enumerate(points):
|
||||||
|
for j in range(i + 1, len(points)):
|
||||||
|
for k in range(j + 1, len(points)):
|
||||||
|
circle = _circumcircle(p, points[j], points[k])
|
||||||
|
if circle is not None:
|
||||||
|
candidates.append(circle)
|
||||||
|
for center, radius in candidates:
|
||||||
|
if radius >= best_r:
|
||||||
|
continue
|
||||||
|
if all(math.dist(center, p) <= radius + 1e-7 for p in points):
|
||||||
|
best_c, best_r = center, radius
|
||||||
|
return best_c, best_r
|
||||||
|
|
||||||
|
|
||||||
|
def solve_case(case: dict) -> dict:
|
||||||
|
start = perf_counter()
|
||||||
|
planes = _planes(case)
|
||||||
|
polygon = [(-BOX_M, -BOX_M), (BOX_M, -BOX_M), (BOX_M, BOX_M), (-BOX_M, BOX_M)]
|
||||||
|
for a, b, c in planes:
|
||||||
|
polygon = _clip(polygon, a, b, c)
|
||||||
|
if not polygon:
|
||||||
|
return _finish(_degenerate("empty"), start)
|
||||||
|
boundary_tol = 1e-6 * BOX_M
|
||||||
|
if any(abs(point[0]) >= BOX_M - boundary_tol
|
||||||
|
or abs(point[1]) >= BOX_M - boundary_tol for point in polygon):
|
||||||
|
return _finish(_degenerate("unbounded"), start)
|
||||||
|
|
||||||
|
if len(polygon) == 1:
|
||||||
|
summary = {"status": "point", "diameter_m": 0.0, "covers": True,
|
||||||
|
"mec_radius_m": 0.0, "max_excess_m": 0.0,
|
||||||
|
"vertex_count": 1}
|
||||||
|
else:
|
||||||
|
value, (a, b) = _brute_force_diameter(polygon)
|
||||||
|
if len(polygon) == 2:
|
||||||
|
summary = {"status": "segment", "diameter_m": value, "covers": True,
|
||||||
|
"mec_radius_m": value / 2.0, "max_excess_m": 0.0,
|
||||||
|
"vertex_count": 2}
|
||||||
|
else:
|
||||||
|
_mec_center, radius = _brute_force_mec(polygon)
|
||||||
|
diameter_center = ((a[0] + b[0]) / 2.0, (a[1] + b[1]) / 2.0)
|
||||||
|
max_distance = max(math.dist(diameter_center, p) for p in polygon)
|
||||||
|
summary = {
|
||||||
|
"status": "polygon",
|
||||||
|
"diameter_m": value,
|
||||||
|
"covers": bool(radius <= value / 2.0 + 1e-7),
|
||||||
|
"mec_radius_m": radius,
|
||||||
|
"max_excess_m": round(max_distance - value / 2.0, 9),
|
||||||
|
"vertex_count": len(polygon),
|
||||||
|
}
|
||||||
|
return _finish(summary, start)
|
||||||
|
|
||||||
|
|
||||||
|
def _degenerate(status: str) -> dict:
|
||||||
|
return {"status": status, "diameter_m": None, "covers": None,
|
||||||
|
"mec_radius_m": None, "max_excess_m": None, "vertex_count": 0}
|
||||||
|
|
||||||
|
|
||||||
|
def _finish(summary: dict, start: float) -> dict:
|
||||||
|
result = {
|
||||||
|
"method": METHOD.key,
|
||||||
|
"algorithm": METHOD.name,
|
||||||
|
"resolution_m": 0.0,
|
||||||
|
"diameter_infinite": summary["status"] == "unbounded",
|
||||||
|
"outside_vertex_count": None,
|
||||||
|
"runtime_ms": round((perf_counter() - start) * 1000.0, 4),
|
||||||
|
}
|
||||||
|
result.update(summary)
|
||||||
|
return result
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
"""问题二:第二检测点选择策略。
|
||||||
|
|
||||||
|
- ``optimal.py`` :生产选点器 ``mcm26b.planner.select_measurement_point``
|
||||||
|
(保证接收盘内最坏后验半径最小化,采样启发式);
|
||||||
|
- ``baseline.py`` :"垂直 1000 m 最大交会角"朴素启发式(忽略接收保证),
|
||||||
|
以及"退到候选集 MEC 圆心"的保守基线;
|
||||||
|
- ``reference.py``:在保证接收盘内稠密网格枚举的近似最优参考;
|
||||||
|
- ``cases.py`` :随机首次示向度算例;
|
||||||
|
- ``compare.py`` :同案配对对比并落盘。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from . import baseline, cases, optimal, reference
|
||||||
|
|
||||||
|
__all__ = ["optimal", "baseline", "reference", "cases"]
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
"""问题二基线实现:两个朴素但常见的对照。
|
||||||
|
|
||||||
|
- ``candidate_perp``:沿首次示向度垂直方向、距检测点约 1000 m 取点。
|
||||||
|
这是"尽量把交会角做成 90°"的教科书启发式,**但它忽略接收距离保证**:
|
||||||
|
真源可能落在首次角域的远端(≤1500 m),此时第二点距离可超过 1000 m,
|
||||||
|
第二次测量直接 ``no_signal``,几何收益落空。
|
||||||
|
- ``candidate_mec``:退到候选集最小包围圆圆心。它确实落在保证接收盘内
|
||||||
|
(ρ≤1000 时),但交会角可能接近 0°、后验几乎不收缩,代表"最保守"一端。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import math
|
||||||
|
|
||||||
|
from mcm26b.config import ProblemConfig
|
||||||
|
from mcm26b.planner import safe_disk_radius
|
||||||
|
|
||||||
|
from project.common import Method, problem_config
|
||||||
|
from project.q2.region import first_bearing_region
|
||||||
|
|
||||||
|
PERP = Method(
|
||||||
|
key="baseline_perp",
|
||||||
|
name="垂直 1000 m 最大交会角",
|
||||||
|
kind="baseline",
|
||||||
|
note="忽略接收保证的几何启发式",
|
||||||
|
)
|
||||||
|
MEC = Method(
|
||||||
|
key="baseline_mec",
|
||||||
|
name="候选集 MEC 圆心",
|
||||||
|
kind="baseline",
|
||||||
|
note="保证接收但交会角最差的保守基线",
|
||||||
|
)
|
||||||
|
METHODS = (PERP, MEC)
|
||||||
|
|
||||||
|
|
||||||
|
def _region(case: dict, config: ProblemConfig):
|
||||||
|
return first_bearing_region(case, config)
|
||||||
|
|
||||||
|
|
||||||
|
def candidate_perp(case: dict, config: ProblemConfig | None = None) -> dict:
|
||||||
|
config = config or problem_config()
|
||||||
|
region = _region(case, config)
|
||||||
|
station = (case["station"][0], case["station"][1])
|
||||||
|
theta = math.radians(case["bearing_deg"] + 90.0)
|
||||||
|
distance = config.field.r_eff_min_m
|
||||||
|
position = (station[0] + distance * math.cos(theta),
|
||||||
|
station[1] + distance * math.sin(theta))
|
||||||
|
return {
|
||||||
|
"method": PERP.key,
|
||||||
|
"position": [round(position[0], 4), round(position[1], 4)],
|
||||||
|
"worst_posterior_radius_m": None, # 由 compare 统一评估
|
||||||
|
"move_cost_s": None,
|
||||||
|
"safe_disk_radius_m": safe_disk_radius(region, config),
|
||||||
|
"initial_radius_m": region.mec()[1],
|
||||||
|
"reason": "沿首次示向度法向 1000 m 取 90° 交会点",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def candidate_mec(case: dict, config: ProblemConfig | None = None) -> dict:
|
||||||
|
config = config or problem_config()
|
||||||
|
region = _region(case, config)
|
||||||
|
center, radius = region.mec()
|
||||||
|
return {
|
||||||
|
"method": MEC.key,
|
||||||
|
"position": [round(center[0], 4), round(center[1], 4)],
|
||||||
|
"worst_posterior_radius_m": None,
|
||||||
|
"move_cost_s": None,
|
||||||
|
"safe_disk_radius_m": safe_disk_radius(region, config),
|
||||||
|
"initial_radius_m": radius,
|
||||||
|
"reason": "取候选集最小包围圆圆心,保证接收但交会角可能最差",
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
"""问题二算例:随机首次示向度(全向源)。"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import math
|
||||||
|
import random
|
||||||
|
|
||||||
|
from mcm26b.config import ProblemConfig
|
||||||
|
|
||||||
|
|
||||||
|
def random_case(seed: int, config: ProblemConfig | None = None) -> dict:
|
||||||
|
"""随机检测点 s 与示向度 θ:默认让真源与读数一致(误差 ±1° 内)。"""
|
||||||
|
config = config or ProblemConfig()
|
||||||
|
rng = random.Random(seed)
|
||||||
|
radius = config.field.omega_radius_m
|
||||||
|
# 检测点可在目标域内或域外;这里取域内,符合题目常规场景
|
||||||
|
angle_s = rng.uniform(0, 2 * math.pi)
|
||||||
|
distance_s = radius * math.sqrt(rng.random()) * 0.9
|
||||||
|
station = (distance_s * math.cos(angle_s), distance_s * math.sin(angle_s))
|
||||||
|
# 真源与检测点距离落在 [60, 1500) 且仍在 Ω 内
|
||||||
|
while True:
|
||||||
|
bearing_true = rng.uniform(0, 2 * math.pi)
|
||||||
|
source_distance = rng.uniform(60, config.field.r_eff_max_m - 1)
|
||||||
|
target = (station[0] + source_distance * math.cos(bearing_true),
|
||||||
|
station[1] + source_distance * math.sin(bearing_true))
|
||||||
|
if math.hypot(*target) <= radius:
|
||||||
|
break
|
||||||
|
error = math.radians(rng.uniform(-0.95, 0.95))
|
||||||
|
bearing_deg = math.degrees(bearing_true + error) % 360.0
|
||||||
|
return {"id": f"q2-{seed}", "station": [round(station[0], 2), round(station[1], 2)],
|
||||||
|
"bearing_deg": round(bearing_deg, 2), "half_angle_deg": 1.0,
|
||||||
|
"true_source": [round(target[0], 2), round(target[1], 2)]}
|
||||||
|
|
||||||
|
|
||||||
|
def random_cases(count: int, base_seed: int = 4200,
|
||||||
|
config: ProblemConfig | None = None) -> list[dict]:
|
||||||
|
return [random_case(base_seed + index, config) for index in range(count)]
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
"""问题二同案配对对比:最优 / 两个基线 / 网格参考,用同一评估器打分。"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import math
|
||||||
|
|
||||||
|
from mcm26b.config import ProblemConfig
|
||||||
|
from mcm26b.planner import _worst_posterior_radius
|
||||||
|
|
||||||
|
from project.common import (Method, RESULTS_DIR, markdown_table, mean, ratio,
|
||||||
|
write_json, write_text)
|
||||||
|
from project.q2 import baseline, cases, optimal, reference
|
||||||
|
from project.q2.region import first_bearing_region
|
||||||
|
|
||||||
|
METHODS: tuple[Method, ...] = (optimal.METHOD, baseline.PERP, baseline.MEC,
|
||||||
|
reference.METHOD)
|
||||||
|
EVAL_WITNESS_SPACING_M = 60.0
|
||||||
|
|
||||||
|
|
||||||
|
def _evaluate(case: dict, config: ProblemConfig) -> list[dict]:
|
||||||
|
region = first_bearing_region(case, config)
|
||||||
|
station = (case["station"][0], case["station"][1])
|
||||||
|
initial_radius = region.mec()[1]
|
||||||
|
half_angle = (config.field.bearing_error_half_deg
|
||||||
|
+ config.search.bearing_rounding_margin_deg)
|
||||||
|
witnesses = region.witnesses(EVAL_WITNESS_SPACING_M)
|
||||||
|
|
||||||
|
plans = [optimal.candidate(case, config),
|
||||||
|
baseline.candidate_perp(case, config),
|
||||||
|
baseline.candidate_mec(case, config),
|
||||||
|
reference.candidate(case, config)]
|
||||||
|
rows = []
|
||||||
|
for plan in plans:
|
||||||
|
q = (plan["position"][0], plan["position"][1])
|
||||||
|
guarantee_distance = max(math.dist(q, vertex) for vertex in region.polygon)
|
||||||
|
worst = _worst_posterior_radius(
|
||||||
|
region, q, witnesses, half_angle,
|
||||||
|
config.field.near_radius_m, config.search.polygon_sides)
|
||||||
|
move_cost = math.dist(station, q) / config.cost.speed_mps
|
||||||
|
rows.append({
|
||||||
|
"case_id": case["id"],
|
||||||
|
"method": plan["method"],
|
||||||
|
"initial_radius_m": round(initial_radius, 4),
|
||||||
|
"q": [round(q[0], 2), round(q[1], 2)],
|
||||||
|
"guaranteed_reception": bool(guarantee_distance
|
||||||
|
<= config.field.r_eff_min_m + 1e-6),
|
||||||
|
"guarantee_distance_m": round(guarantee_distance, 3),
|
||||||
|
"worst_posterior_radius_m": round(worst, 4),
|
||||||
|
"improvement_m": round(initial_radius - worst, 4),
|
||||||
|
"final_ratio": round(worst / initial_radius, 4) if initial_radius > 0 else None,
|
||||||
|
"move_cost_s": round(move_cost, 4),
|
||||||
|
"reason": plan["reason"],
|
||||||
|
})
|
||||||
|
return rows
|
||||||
|
|
||||||
|
|
||||||
|
def _aggregate(rows: list[dict], labels: list[str]) -> dict:
|
||||||
|
summary: dict[str, dict] = {}
|
||||||
|
initial = mean(r["initial_radius_m"] for r in rows) if rows else 0.0
|
||||||
|
for label in labels:
|
||||||
|
part = [r for r in rows if r["method"] == label]
|
||||||
|
if not part:
|
||||||
|
continue
|
||||||
|
summary[label] = {
|
||||||
|
"cases": len(part),
|
||||||
|
"guaranteed_rate": ratio(sum(r["guaranteed_reception"] for r in part),
|
||||||
|
len(part)),
|
||||||
|
"worst_posterior_mean_m": mean(r["worst_posterior_radius_m"] for r in part),
|
||||||
|
"worst_posterior_median_m": sorted(
|
||||||
|
r["worst_posterior_radius_m"] for r in part)[len(part) // 2],
|
||||||
|
"improvement_mean_m": mean(r["improvement_m"] for r in part),
|
||||||
|
"final_ratio_mean": mean(r["final_ratio"] for r in part
|
||||||
|
if r["final_ratio"] is not None),
|
||||||
|
"move_cost_mean_s": mean(r["move_cost_s"] for r in part),
|
||||||
|
}
|
||||||
|
summary["_initial_radius_mean_m"] = initial
|
||||||
|
return summary
|
||||||
|
|
||||||
|
|
||||||
|
def _render(rows: list[dict], aggregate: dict, labels: list[str]) -> str:
|
||||||
|
headers = ["算例", "方法", "初始 ρ(m)", "第二点 q", "保证接收",
|
||||||
|
"q 到 K₁ 最远(m)", "最坏后验 ρ'(m)", "收缩(m)", "移动(s)"]
|
||||||
|
body = []
|
||||||
|
order = {label: index for index, label in enumerate(labels)}
|
||||||
|
for row in sorted(rows, key=lambda r: (r["case_id"], order.get(r["method"], 99))):
|
||||||
|
body.append([
|
||||||
|
row["case_id"], row["method"], _f(row["initial_radius_m"]),
|
||||||
|
f"({row['q'][0]:.0f},{row['q'][1]:.0f})",
|
||||||
|
"是" if row["guaranteed_reception"] else "否",
|
||||||
|
_f(row["guarantee_distance_m"]), _f(row["worst_posterior_radius_m"]),
|
||||||
|
_f(row["improvement_m"]), _f(row["move_cost_s"]),
|
||||||
|
])
|
||||||
|
text = ["# 问题二:第二检测点选择对比", "",
|
||||||
|
"初始 ρ 为首次示向度候选集 K₁ 的最小包围半径;最坏后验按"
|
||||||
|
"目标边界采样 × 读数区间端点统一评估。", "",
|
||||||
|
markdown_table(headers, body), "", "## 逐方法汇总", ""]
|
||||||
|
agg_headers = ["方法", "保证接收率", "最坏后验均值(m)", "中位(m)",
|
||||||
|
"平均收缩(m)", "终值比 ρ'/ρ", "移动均值(s)"]
|
||||||
|
agg_rows = []
|
||||||
|
for label in labels:
|
||||||
|
s = aggregate.get(label)
|
||||||
|
if not s:
|
||||||
|
continue
|
||||||
|
agg_rows.append([label, f"{s['guaranteed_rate']*100:.1f}%",
|
||||||
|
_f(s["worst_posterior_mean_m"]),
|
||||||
|
_f(s["worst_posterior_median_m"]),
|
||||||
|
_f(s["improvement_mean_m"]),
|
||||||
|
_f(s["final_ratio_mean"], 3),
|
||||||
|
_f(s["move_cost_mean_s"])])
|
||||||
|
text.append(markdown_table(agg_headers, agg_rows))
|
||||||
|
text.append("")
|
||||||
|
text.append(f"初始 ρ 均值:{aggregate['_initial_radius_mean_m']:.2f} m")
|
||||||
|
return "\n".join(text) + "\n"
|
||||||
|
|
||||||
|
|
||||||
|
def _f(value, digits: int = 2) -> str:
|
||||||
|
if value is None:
|
||||||
|
return "—"
|
||||||
|
if isinstance(value, bool):
|
||||||
|
return "是" if value else "否"
|
||||||
|
if isinstance(value, float):
|
||||||
|
return f"{value:.{digits}f}"
|
||||||
|
return str(value)
|
||||||
|
|
||||||
|
|
||||||
|
def run(quick: bool = False) -> dict:
|
||||||
|
config = ProblemConfig()
|
||||||
|
case_list = cases.random_cases(8 if quick else 30, config=config)
|
||||||
|
rows = [row for case in case_list for row in _evaluate(case, config)]
|
||||||
|
labels = [m.key for m in METHODS]
|
||||||
|
aggregate = _aggregate(rows, labels)
|
||||||
|
payload = {"question": 2, "methods": [m.__dict__ for m in METHODS],
|
||||||
|
"cases": case_list, "rows": rows, "aggregate": aggregate}
|
||||||
|
write_json(RESULTS_DIR / "q2.json", payload)
|
||||||
|
write_text(RESULTS_DIR / "q2.md", _render(rows, aggregate, labels))
|
||||||
|
return payload
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
"""问题二最优实现:保证接收盘内的最坏后验半径最小化选点(生产实现)。
|
||||||
|
|
||||||
|
复用 ``mcm26b.planner.select_measurement_point``:
|
||||||
|
|
||||||
|
1. 在"保证接收盘" B(mec_center, 1000-ρ) 内环形采样候选点;
|
||||||
|
2. 对每个候选,用"目标边界点 × 读数区间端点(+near 分支)"估计
|
||||||
|
最坏后验最小包围半径,并以移动时间同分决胜;
|
||||||
|
3. 选定后按 1/4 间距加密复核,返回位置、后验半径与理由。
|
||||||
|
|
||||||
|
采样是启发式(不是连续问题全局最优证明),但保证接收是严格的。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from mcm26b.config import ProblemConfig
|
||||||
|
from mcm26b.planner import safe_disk_radius, select_measurement_point
|
||||||
|
|
||||||
|
from project.common import Method, problem_config
|
||||||
|
from project.q2.region import first_bearing_region
|
||||||
|
|
||||||
|
METHOD = Method(
|
||||||
|
key="optimal",
|
||||||
|
name="保证接收盘内最小最坏后验半径",
|
||||||
|
kind="optimal",
|
||||||
|
note="生产实现 mcm26b.planner.select_measurement_point",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def candidate(case: dict, config: ProblemConfig | None = None) -> dict:
|
||||||
|
config = config or problem_config()
|
||||||
|
region = first_bearing_region(case, config)
|
||||||
|
station = (case["station"][0], case["station"][1])
|
||||||
|
plan = select_measurement_point(region, station, config)
|
||||||
|
return {
|
||||||
|
"method": METHOD.key,
|
||||||
|
"position": [round(plan.position[0], 4), round(plan.position[1], 4)],
|
||||||
|
"worst_posterior_radius_m": plan.worst_posterior_radius_m,
|
||||||
|
"move_cost_s": plan.move_cost_s,
|
||||||
|
"safe_disk_radius_m": safe_disk_radius(region, config),
|
||||||
|
"initial_radius_m": region.mec()[1],
|
||||||
|
"reason": plan.reason,
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
"""问题二近似最优参考:在保证接收盘内做稠密网格枚举。
|
||||||
|
|
||||||
|
目标函数与生产实现一致(最坏后验包围半径 + 移动时间同分决胜),但候选点
|
||||||
|
取自**均匀网格**而非环形采样族,因此可用来检验生产采样是否接近该目标下的
|
||||||
|
网格最优。注意这是"同一目标下的网格参考",不是连续问题的严格全局最优。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import math
|
||||||
|
|
||||||
|
from mcm26b.config import ProblemConfig
|
||||||
|
from mcm26b.planner import (_worst_posterior_radius, safe_disk_radius)
|
||||||
|
|
||||||
|
from project.common import Method, problem_config
|
||||||
|
from project.q2.region import first_bearing_region
|
||||||
|
|
||||||
|
METHOD = Method(
|
||||||
|
key="reference",
|
||||||
|
name="保证接收盘内网格枚举",
|
||||||
|
kind="reference",
|
||||||
|
note="同一目标下的稠密网格参考,非连续全局最优",
|
||||||
|
)
|
||||||
|
GRID_SIDES = 9
|
||||||
|
WITNESS_SPACING_M = 150.0
|
||||||
|
|
||||||
|
|
||||||
|
def candidate(case: dict, config: ProblemConfig | None = None) -> dict:
|
||||||
|
config = config or problem_config()
|
||||||
|
region = first_bearing_region(case, config)
|
||||||
|
station = (case["station"][0], case["station"][1])
|
||||||
|
center = region.mec()[0]
|
||||||
|
safe = safe_disk_radius(region, config)
|
||||||
|
if safe < 0:
|
||||||
|
return _payload(center, region, config, station, "无保证接收盘:退回 MEC 圆心")
|
||||||
|
|
||||||
|
field, search = config.field, config.search
|
||||||
|
half_angle = field.bearing_error_half_deg + search.bearing_rounding_margin_deg
|
||||||
|
witnesses = region.witnesses(WITNESS_SPACING_M)
|
||||||
|
step = 2.0 * safe / (GRID_SIDES - 1)
|
||||||
|
best_key, best_point, best_worst = None, center, None
|
||||||
|
for i in range(GRID_SIDES):
|
||||||
|
for j in range(GRID_SIDES):
|
||||||
|
point = (center[0] - safe + i * step, center[1] - safe + j * step)
|
||||||
|
if math.dist(point, center) > safe + 1e-9:
|
||||||
|
continue
|
||||||
|
worst = _worst_posterior_radius(region, point, witnesses, half_angle,
|
||||||
|
field.near_radius_m, search.polygon_sides)
|
||||||
|
move = math.dist(station, point) / config.cost.speed_mps
|
||||||
|
key = (round(worst, 6), round(move, 6))
|
||||||
|
if best_key is None or key < best_key:
|
||||||
|
best_key, best_point, best_worst = key, point, worst
|
||||||
|
return _payload(best_point, region, config, station,
|
||||||
|
f"安全盘网格 {GRID_SIDES}×{GRID_SIDES} 取最坏后验最小",
|
||||||
|
worst_value=best_worst)
|
||||||
|
|
||||||
|
|
||||||
|
def _payload(point, region, config, station, reason, worst_value=None) -> dict:
|
||||||
|
return {
|
||||||
|
"method": METHOD.key,
|
||||||
|
"position": [round(point[0], 4), round(point[1], 4)],
|
||||||
|
"worst_posterior_radius_m": worst_value,
|
||||||
|
"move_cost_s": math.dist(station, point) / config.cost.speed_mps,
|
||||||
|
"safe_disk_radius_m": safe_disk_radius(region, config),
|
||||||
|
"initial_radius_m": region.mec()[1],
|
||||||
|
"reason": reason,
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
"""问题二候选集 K1 的统一构造(三个方法共用同一输入,保证配对公平)。
|
||||||
|
|
||||||
|
K1 = Ω ∩ W(s, θ ± δ_eff) ∩ B(s, 1500),δ_eff = 题设 1° + 示向度舍入裕量。
|
||||||
|
生产实现用"圆盘外切多边形 ∩ 半平面"的保守外包(``CandidateRegion``),
|
||||||
|
圆盘用外切多边形严格外包,保证真源不丢。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from mcm26b.belief import CandidateRegion
|
||||||
|
from mcm26b.config import ProblemConfig, Vec2
|
||||||
|
|
||||||
|
from project.common import problem_config
|
||||||
|
|
||||||
|
|
||||||
|
def first_bearing_region(case: dict,
|
||||||
|
config: ProblemConfig | None = None) -> CandidateRegion:
|
||||||
|
config = config or problem_config()
|
||||||
|
field, search = config.field, config.search
|
||||||
|
half_angle = field.bearing_error_half_deg + search.bearing_rounding_margin_deg
|
||||||
|
station: Vec2 = (case["station"][0], case["station"][1])
|
||||||
|
region = CandidateRegion.initial(station, config)
|
||||||
|
region.clip_wedge(station, case["bearing_deg"], half_angle)
|
||||||
|
return region
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
"""问题三:全向源搜索—定位—清除闭环。
|
||||||
|
|
||||||
|
- ``optimal.py`` :贝叶斯网格粒子滤波 + 信息增益选点 + 滚动 2-opt(``bayes``);
|
||||||
|
- ``baseline.py``:49 点网格发现 + 就近服务(``grid``,历史基线),
|
||||||
|
以及七点解析覆盖批量扫频(``seven``,中间对照);
|
||||||
|
- ``compare.py`` :冻结基准集配对对比并落盘。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from . import baseline, optimal
|
||||||
|
|
||||||
|
__all__ = ["optimal", "baseline"]
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
"""问题三基线实现:两条不共享贝叶斯决策的对照策略。
|
||||||
|
|
||||||
|
- ``OmnidirectionalLoop``(grid):49 点(700 m 步长)网格保证发现,
|
||||||
|
发现后就近进入定位子循环;策略简单、无信息增益选点、无任务排序优化。
|
||||||
|
- ``SevenBatch``(seven):七点解析覆盖 + 同站批量扫频 + 最近候选服务队列,
|
||||||
|
是"覆盖证明 + 批量调度"的中间对照(官方演练 12/12 全清)。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from mcm26b.planner import OmnidirectionalLoop
|
||||||
|
from mcm26b.planner.seven import SevenBatch
|
||||||
|
|
||||||
|
from project.common import Method
|
||||||
|
|
||||||
|
GRID = Method(key="baseline", name="49 点网格 + 就近服务 (grid)",
|
||||||
|
kind="baseline", note="历史基线,无信息增益与滚动排序")
|
||||||
|
SEVEN = Method(key="seven", name="七点解析覆盖 + 批量扫频 (seven)",
|
||||||
|
kind="baseline", note="覆盖证明与批量调度的中间对照")
|
||||||
|
|
||||||
|
def build_grid(client, config):
|
||||||
|
"""工厂:49 点网格基线闭环。"""
|
||||||
|
return OmnidirectionalLoop(client, config)
|
||||||
|
|
||||||
|
|
||||||
|
def build_seven(client, config):
|
||||||
|
"""工厂:七点批量扫频中间对照。"""
|
||||||
|
return SevenBatch(client, config)
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
"""问题三冻结基准集配对对比:bayes(最优) vs grid(基线) vs seven(中间)。"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from project.common import (RESULTS_DIR, closed_loop_payload, episodes_markdown,
|
||||||
|
execute_variants, problem_config, write_json,
|
||||||
|
write_text)
|
||||||
|
from project.q3 import baseline, optimal
|
||||||
|
|
||||||
|
VARIANTS = (
|
||||||
|
("optimal", optimal.build_loop),
|
||||||
|
("seven", baseline.build_seven),
|
||||||
|
("baseline", baseline.build_grid),
|
||||||
|
)
|
||||||
|
LABELS = [label for label, _ in VARIANTS]
|
||||||
|
BASE_LABEL = "baseline"
|
||||||
|
|
||||||
|
|
||||||
|
def run(quick: bool = False) -> dict:
|
||||||
|
from mcm26b.harness.suites import suite
|
||||||
|
scenes = suite("core", 3)
|
||||||
|
if quick:
|
||||||
|
scenes = scenes[:6]
|
||||||
|
config = problem_config()
|
||||||
|
rows = execute_variants(scenes, VARIANTS, config)
|
||||||
|
payload = closed_loop_payload(3, "core", scenes, VARIANTS, rows,
|
||||||
|
LABELS, BASE_LABEL, config)
|
||||||
|
write_json(RESULTS_DIR / "q3.json", payload)
|
||||||
|
write_text(RESULTS_DIR / "q3.md",
|
||||||
|
episodes_markdown("问题三:全向闭环配对对比(core 基准集)",
|
||||||
|
rows, LABELS, BASE_LABEL))
|
||||||
|
return payload
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
"""问题三最优实现:``mcm26b.planner.bayes_loop.BayesLoop``(bayes 策略)。
|
||||||
|
|
||||||
|
要点:逐频道网格粒子滤波维护幸存集;以期望信息增益选探点、
|
||||||
|
滚动时域 2-opt 排序任务;证书(凸证书或 PF 幸存集 MEC)达到 20 m 即清除;
|
||||||
|
全频道"已清除或证明不存在"后立即 /exit(虚拟时间即成绩口径)。
|
||||||
|
自建消融相对 SevenBatch 再降约 30%(docs/algo-3-bayes.md)。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from mcm26b.planner.bayes_loop import BayesLoop
|
||||||
|
|
||||||
|
from project.common import Method
|
||||||
|
|
||||||
|
METHOD = Method(key="optimal", name="贝叶斯粒子滤波闭环 (bayes)",
|
||||||
|
kind="optimal", note="生产默认策略,虚拟时间口径")
|
||||||
|
def build_loop(client, config):
|
||||||
|
"""工厂:返回生产 bayes 闭环实例。"""
|
||||||
|
return BayesLoop(client, config)
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
"""问题四:含定向干扰源的检测与清除。
|
||||||
|
|
||||||
|
- ``optimal.py`` :``DirectionalExtension``(保证覆盖 + 定向观测推理 +
|
||||||
|
(位置×朝向) 联合信念 + ≤20 m 有限圆盘后备清除);
|
||||||
|
- ``baseline.py``:把问题三最优策略**直接搬用**的两条基线——
|
||||||
|
``bayes``(七站全向缺席证明)与 ``grid``(49 点全向覆盖,无后备清除);
|
||||||
|
- ``compare.py`` :core + boundary 基准集配对对比并落盘。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from . import baseline, optimal
|
||||||
|
|
||||||
|
__all__ = ["optimal", "baseline"]
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
"""问题四基线实现:把问题三策略原样搬用的两条对照。
|
||||||
|
|
||||||
|
- ``bayes``:问题三最优策略直接用于混合源。它用七站环的**全向**缺席证明,
|
||||||
|
对"定向源朝外发射"场景会错误地把真源判为 ABSENT,属于"快而错"的典型;
|
||||||
|
- ``grid``:49 点全向网格(对定向源发现是完备的),但缺少定向观测推理与
|
||||||
|
≤20 m 后备清除,定位停滞时只能停在 DETECTED,属于"稳而不全"。
|
||||||
|
两条基线都不共享最优实现的联合信念与后备清除逻辑。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from mcm26b.planner import OmnidirectionalLoop
|
||||||
|
from mcm26b.planner.bayes_loop import BayesLoop
|
||||||
|
|
||||||
|
from project.common import Method
|
||||||
|
|
||||||
|
BAYES = Method(key="baseline", name="问题三最优策略直接搬用 (bayes)",
|
||||||
|
kind="baseline", note="七站全向缺席证明在定向源上失效")
|
||||||
|
GRID = Method(key="baseline_grid", name="49 点网格全向策略 (grid)",
|
||||||
|
kind="baseline", note="覆盖完备但无定向推理与后备清除")
|
||||||
|
|
||||||
|
def build_bayes(client, config):
|
||||||
|
"""工厂:问题三 bayes 策略直接搬用。"""
|
||||||
|
return BayesLoop(client, config)
|
||||||
|
|
||||||
|
|
||||||
|
def build_grid(client, config):
|
||||||
|
"""工厂:49 点网格全向策略直接搬用。"""
|
||||||
|
return OmnidirectionalLoop(client, config)
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
"""问题四 core+boundary 基准集配对对比:directional(最优) vs 两条搬用基线。"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from project.common import (RESULTS_DIR, closed_loop_payload, episodes_markdown,
|
||||||
|
execute_variants, problem_config, write_json,
|
||||||
|
write_text)
|
||||||
|
from project.q4 import baseline, optimal
|
||||||
|
|
||||||
|
VARIANTS = (
|
||||||
|
("optimal", optimal.build_loop),
|
||||||
|
("baseline_grid", baseline.build_grid),
|
||||||
|
("baseline", baseline.build_bayes),
|
||||||
|
)
|
||||||
|
LABELS = [label for label, _ in VARIANTS]
|
||||||
|
BASE_LABEL = "baseline"
|
||||||
|
|
||||||
|
|
||||||
|
def run(quick: bool = False) -> dict:
|
||||||
|
from mcm26b.harness.suites import suite
|
||||||
|
scenes = suite("core", 4) + suite("boundary", 4)
|
||||||
|
if quick:
|
||||||
|
scenes = scenes[:4]
|
||||||
|
config = problem_config()
|
||||||
|
rows = execute_variants(scenes, VARIANTS, config)
|
||||||
|
payload = closed_loop_payload(4, "core+boundary", scenes, VARIANTS, rows,
|
||||||
|
LABELS, BASE_LABEL, config)
|
||||||
|
write_json(RESULTS_DIR / "q4.json", payload)
|
||||||
|
write_text(RESULTS_DIR / "q4.md",
|
||||||
|
episodes_markdown("问题四:混合源闭环配对对比(core+boundary 基准集)",
|
||||||
|
rows, LABELS, BASE_LABEL))
|
||||||
|
return payload
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
"""问题四最优实现:``mcm26b.planner.DirectionalExtension``(directional 策略)。
|
||||||
|
|
||||||
|
三点相对全向闭环的加装:
|
||||||
|
1. 发现覆盖改用经全向/定向双口径证伪的 31 点最小命中集
|
||||||
|
(``q4_mincover_v2``,15 m/72 档验证零缺口);
|
||||||
|
2. 保证接收盘内 ``no_signal`` 只可能由朝向遮挡解释:记为"观测到定向",
|
||||||
|
对 MEC 圆心取对跖点复核,并用 (位置×朝向) 联合可行集选可见性探点;
|
||||||
|
3. 定位停滞时对有界候选集构造 ≤20 m 网格圆盘覆盖,预算核对后逐格 /clear
|
||||||
|
(清除与朝向无关),是定向源不依赖可见性的完备后备。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from mcm26b.planner import DirectionalExtension
|
||||||
|
|
||||||
|
from project.common import Method
|
||||||
|
|
||||||
|
METHOD = Method(key="optimal", name="定向扩展闭环 (directional)",
|
||||||
|
kind="optimal", note="联合信念 + 后备清除 + 定向覆盖计划")
|
||||||
|
def build_loop(client, config):
|
||||||
|
"""工厂:返回定向扩展闭环实例。"""
|
||||||
|
return DirectionalExtension(client, config)
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,80 @@
|
|||||||
|
# 问题一:固定示例对比
|
||||||
|
|
||||||
|
参考解为独立的大框半平面裁剪 + 精确直径/MEC;
|
||||||
|
固定示例见 `examples/q1`,解析期望由 `tests/test_geometry*.py` 固化。
|
||||||
|
|
||||||
|
| 算例 | 来源 | 参考状态 | 最优状态 | 基线状态 | D参考(m) | D最优(m) | D基线(m) | |ΔD|最优 | |ΔD|基线 | 覆盖参考 | 覆盖最优 | 覆盖基线 |
|
||||||
|
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
| 三点交会 | examples/q1 | polygon | polygon | polygon | 26.99 | 26.99 | 21.21 | 0.00 | 5.77 | 否 | 否 | 是 |
|
||||||
|
| 直径圆反例 | examples/q1 | polygon | polygon | polygon | 40.00 | 40.00 | 50.00 | 0.00 | 10.00 | 否 | 否 | 是 |
|
||||||
|
| 近共线检测点 | examples/q1 | unbounded | unbounded | unbounded | — | — | — | — | — | — | — | — |
|
||||||
|
| 零误差退化线段 | examples/q1 | segment | segment | point | 100.00 | 100.00 | 0.00 | 0.00 | 100.00 | 是 | 是 | 是 |
|
||||||
|
| 不相容观测 | examples/q1 | empty | empty | empty | — | — | — | — | — | — | — | — |
|
||||||
|
|
||||||
|
## 汇总
|
||||||
|
|
||||||
|
| 指标 | 最优 | 基线 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 状态判定准确率 | 100.0% | 80.0% |
|
||||||
|
| 直径平均绝对误差 (m) | 0.000 | 7.886 |
|
||||||
|
| 直径最大误差 (m) | 0.000 | 10.000 |
|
||||||
|
| 覆盖判定准确率 | 100.0% | 33.3% |
|
||||||
|
| 平均耗时 (ms) | 0.064 | 14.202 |
|
||||||
|
|
||||||
|
# 问题一:随机交会算例对比
|
||||||
|
|
||||||
|
参考解为独立的大框半平面裁剪 + 精确直径/MEC;
|
||||||
|
固定示例见 `examples/q1`,解析期望由 `tests/test_geometry*.py` 固化。
|
||||||
|
|
||||||
|
| 算例 | 来源 | 参考状态 | 最优状态 | 基线状态 | D参考(m) | D最优(m) | D基线(m) | |ΔD|最优 | |ΔD|基线 | 覆盖参考 | 覆盖最优 | 覆盖基线 |
|
||||||
|
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 40.32 | 40.32 | 29.15 | 0.00 | 11.16 | 是 | 是 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 33.36 | 33.36 | 35.36 | 0.00 | 2.00 | 是 | 是 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 39.78 | 39.78 | 40.31 | 0.00 | 0.53 | 是 | 是 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 37.50 | 37.50 | 36.06 | 0.00 | 1.44 | 否 | 否 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 48.24 | 48.24 | 47.43 | 0.00 | 0.81 | 是 | 是 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 27.97 | 27.97 | 25.00 | 0.00 | 2.97 | 否 | 否 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 31.84 | 31.84 | 32.02 | 0.00 | 0.17 | 否 | 否 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 39.13 | 39.13 | 31.62 | 0.00 | 7.51 | 是 | 是 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 31.60 | 31.60 | 32.02 | 0.00 | 0.42 | 是 | 是 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 34.49 | 34.49 | 32.02 | 0.00 | 2.48 | 否 | 否 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 32.61 | 32.61 | 26.93 | 0.00 | 5.68 | 是 | 是 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 37.41 | 37.41 | 35.36 | 0.00 | 2.05 | 否 | 否 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 35.15 | 35.15 | 36.06 | 0.00 | 0.90 | 否 | 否 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 20.56 | 20.56 | 18.03 | 0.00 | 2.53 | 否 | 否 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 25.23 | 25.23 | 18.03 | 0.00 | 7.20 | 否 | 否 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 31.61 | 31.61 | 32.02 | 0.00 | 0.41 | 是 | 是 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 22.19 | 22.19 | 14.14 | 0.00 | 8.05 | 否 | 否 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 26.31 | 26.31 | 18.03 | 0.00 | 8.28 | 是 | 是 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 37.85 | 37.85 | 28.28 | 0.00 | 9.56 | 是 | 是 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 25.78 | 25.78 | 11.18 | 0.00 | 14.60 | 是 | 是 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 22.53 | 22.53 | 18.03 | 0.00 | 4.51 | 否 | 否 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 36.84 | 36.84 | 26.93 | 0.00 | 9.91 | 是 | 是 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 51.21 | 51.21 | 47.17 | 0.00 | 4.04 | 是 | 是 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 32.88 | 32.88 | 29.15 | 0.00 | 3.73 | 是 | 是 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 29.99 | 29.99 | 32.02 | 0.00 | 2.02 | 否 | 否 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 36.50 | 36.50 | 28.28 | 0.00 | 8.21 | 是 | 是 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 26.38 | 26.38 | 18.03 | 0.00 | 8.35 | 是 | 是 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 50.11 | 50.11 | 49.24 | 0.00 | 0.86 | 是 | 是 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 23.29 | 23.29 | 22.36 | 0.00 | 0.93 | 否 | 否 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 40.73 | 40.73 | 42.43 | 0.00 | 1.70 | 是 | 是 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 34.09 | 34.09 | 25.00 | 0.00 | 9.09 | 是 | 是 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 23.35 | 23.35 | 18.03 | 0.00 | 5.32 | 否 | 否 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 14.89 | 14.89 | 11.18 | 0.00 | 3.71 | 否 | 否 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 38.12 | 38.12 | 33.54 | 0.00 | 4.58 | 是 | 是 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 13.68 | 13.68 | 5.00 | 0.00 | 8.68 | 是 | 是 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 41.44 | 41.44 | 43.01 | 0.00 | 1.57 | 否 | 否 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 22.99 | 22.99 | 18.03 | 0.00 | 4.97 | 否 | 否 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 34.00 | 34.00 | 35.36 | 0.00 | 1.35 | 否 | 否 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 16.20 | 16.20 | 7.07 | 0.00 | 9.13 | 否 | 否 | 是 |
|
||||||
|
| 随机交会 | synthetic | polygon | polygon | polygon | 26.90 | 26.90 | 25.00 | 0.00 | 1.90 | 否 | 否 | 是 |
|
||||||
|
|
||||||
|
## 汇总
|
||||||
|
|
||||||
|
| 指标 | 最优 | 基线 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 状态判定准确率 | 100.0% | 100.0% |
|
||||||
|
| 直径平均绝对误差 (m) | 0.000 | 4.583 |
|
||||||
|
| 直径最大误差 (m) | 0.000 | 14.599 |
|
||||||
|
| 覆盖判定准确率 | 100.0% | 52.5% |
|
||||||
|
| 平均耗时 (ms) | 0.086 | 15.932 |
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,137 @@
|
|||||||
|
# 问题二:第二检测点选择对比
|
||||||
|
|
||||||
|
初始 ρ 为首次示向度候选集 K₁ 的最小包围半径;最坏后验按目标边界采样 × 读数区间端点统一评估。
|
||||||
|
|
||||||
|
| 算例 | 方法 | 初始 ρ(m) | 第二点 q | 保证接收 | q 到 K₁ 最远(m) | 最坏后验 ρ'(m) | 收缩(m) | 移动(s) |
|
||||||
|
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
| q2-4200 | optimal | 674.59 | (1257,89) | 是 | 907.09 | 54.45 | 620.13 | 181.42 |
|
||||||
|
| q2-4200 | baseline_perp | 674.59 | (194,462) | 否 | 1679.16 | 76.22 | 598.36 | 200.00 |
|
||||||
|
| q2-4200 | baseline_mec | 674.59 | (1173,-226) | 是 | 674.59 | 337.29 | 337.29 | 134.92 |
|
||||||
|
| q2-4200 | reference | 674.59 | (1173,100) | 是 | 850.11 | 57.70 | 616.89 | 170.02 |
|
||||||
|
| q2-4201 | optimal | 261.33 | (-1501,-348) | 是 | 461.91 | 13.55 | 247.78 | 92.38 |
|
||||||
|
| q2-4201 | baseline_perp | 261.33 | (-782,-715) | 否 | 1131.68 | 27.22 | 234.11 | 200.00 |
|
||||||
|
| q2-4201 | baseline_mec | 261.33 | (-1578,-63) | 是 | 261.33 | 130.67 | 130.67 | 52.27 |
|
||||||
|
| q2-4201 | reference | 261.33 | (-1393,-432) | 是 | 527.03 | 14.98 | 246.36 | 105.41 |
|
||||||
|
| q2-4202 | optimal | 591.09 | (-1126,-861) | 是 | 869.01 | 37.92 | 553.17 | 173.80 |
|
||||||
|
| q2-4202 | baseline_perp | 591.09 | (408,-1235) | 否 | 1561.73 | 64.57 | 526.52 | 200.00 |
|
||||||
|
| q2-4202 | baseline_mec | 591.09 | (-731,-967) | 是 | 591.09 | 295.54 | 295.54 | 118.22 |
|
||||||
|
| q2-4202 | reference | 591.09 | (-1038,-865) | 是 | 788.83 | 46.22 | 544.86 | 157.77 |
|
||||||
|
| q2-4203 | optimal | 326.92 | (1145,-1745) | 是 | 683.58 | 17.13 | 309.79 | 136.72 |
|
||||||
|
| q2-4203 | baseline_perp | 326.92 | (1837,-1523) | 否 | 1204.35 | 32.99 | 293.93 | 200.00 |
|
||||||
|
| q2-4203 | baseline_mec | 326.92 | (788,-1388) | 是 | 326.92 | 163.46 | 163.46 | 65.38 |
|
||||||
|
| q2-4203 | reference | 326.92 | (1125,-1725) | 是 | 657.98 | 16.90 | 310.02 | 131.60 |
|
||||||
|
| q2-4204 | optimal | 750.20 | (-401,208) | 是 | 876.74 | 91.60 | 658.60 | 175.35 |
|
||||||
|
| q2-4204 | baseline_perp | 750.20 | (-1541,302) | 否 | 1817.48 | 90.79 | 659.41 | 200.00 |
|
||||||
|
| q2-4204 | baseline_mec | 750.20 | (-336,-34) | 是 | 750.20 | 375.10 | 375.10 | 150.04 |
|
||||||
|
| q2-4204 | reference | 750.20 | (-336,216) | 是 | 923.80 | 92.25 | 657.95 | 184.76 |
|
||||||
|
| q2-4205 | optimal | 750.14 | (-503,21) | 是 | 881.59 | 91.10 | 659.04 | 176.32 |
|
||||||
|
| q2-4205 | baseline_perp | 750.14 | (-612,-1470) | 否 | 1817.36 | 90.78 | 659.36 | 200.00 |
|
||||||
|
| q2-4205 | baseline_mec | 750.14 | (-568,-220) | 是 | 750.14 | 375.08 | 375.06 | 150.03 |
|
||||||
|
| q2-4205 | reference | 750.14 | (-568,30) | 是 | 927.74 | 92.25 | 657.89 | 185.55 |
|
||||||
|
| q2-4206 | optimal | 750.12 | (216,677) | 是 | 892.00 | 90.96 | 659.16 | 178.40 |
|
||||||
|
| q2-4206 | baseline_perp | 750.12 | (1592,1266) | 否 | 1817.31 | 90.77 | 659.34 | 200.00 |
|
||||||
|
| q2-4206 | baseline_mec | 750.12 | (457,742) | 是 | 750.12 | 375.10 | 375.02 | 150.02 |
|
||||||
|
| q2-4206 | reference | 750.12 | (207,742) | 是 | 838.72 | 94.21 | 655.91 | 167.74 |
|
||||||
|
| q2-4207 | optimal | 750.12 | (1205,476) | 是 | 899.09 | 90.85 | 659.27 | 179.82 |
|
||||||
|
| q2-4207 | baseline_perp | 750.12 | (578,1465) | 否 | 1817.31 | 90.78 | 659.34 | 200.00 |
|
||||||
|
| q2-4207 | baseline_mec | 750.12 | (1028,299) | 是 | 750.12 | 375.09 | 375.03 | 150.02 |
|
||||||
|
| q2-4207 | reference | 750.12 | (1028,49) | 是 | 852.52 | 92.97 | 657.15 | 170.50 |
|
||||||
|
| q2-4208 | optimal | 750.19 | (-1409,-294) | 是 | 901.20 | 90.75 | 659.44 | 180.24 |
|
||||||
|
| q2-4208 | baseline_perp | 750.19 | (-8,239) | 否 | 1817.46 | 90.79 | 659.40 | 200.00 |
|
||||||
|
| q2-4208 | baseline_mec | 750.19 | (-1167,-229) | 是 | 750.19 | 375.10 | 375.09 | 150.04 |
|
||||||
|
| q2-4208 | reference | 750.19 | (-1417,-229) | 是 | 849.54 | 93.05 | 657.14 | 169.91 |
|
||||||
|
| q2-4209 | optimal | 750.20 | (463,849) | 是 | 849.70 | 93.03 | 657.17 | 169.94 |
|
||||||
|
| q2-4209 | baseline_perp | 750.20 | (-621,1100) | 否 | 1817.43 | 90.79 | 659.41 | 200.00 |
|
||||||
|
| q2-4209 | baseline_mec | 750.20 | (527,607) | 是 | 750.20 | 375.10 | 375.10 | 150.04 |
|
||||||
|
| q2-4209 | reference | 750.20 | (527,857) | 是 | 901.34 | 90.74 | 659.46 | 180.27 |
|
||||||
|
| q2-4210 | optimal | 706.03 | (-429,1151) | 是 | 915.53 | 63.69 | 642.34 | 183.11 |
|
||||||
|
| q2-4210 | baseline_perp | 706.03 | (-206,15) | 否 | 1722.00 | 80.64 | 625.39 | 200.00 |
|
||||||
|
| q2-4210 | baseline_mec | 706.03 | (-145,1227) | 是 | 706.03 | 353.02 | 353.02 | 141.21 |
|
||||||
|
| q2-4210 | reference | 706.03 | (-366,1080) | 是 | 830.65 | 72.40 | 633.63 | 166.13 |
|
||||||
|
| q2-4211 | optimal | 750.20 | (-619,-443) | 是 | 901.34 | 90.74 | 659.46 | 180.27 |
|
||||||
|
| q2-4211 | baseline_perp | 750.20 | (-379,1037) | 否 | 1817.47 | 90.79 | 659.41 | 200.00 |
|
||||||
|
| q2-4211 | baseline_mec | 750.20 | (-554,-202) | 是 | 750.20 | 375.10 | 375.10 | 150.04 |
|
||||||
|
| q2-4211 | reference | 750.20 | (-554,-451) | 是 | 943.43 | 94.71 | 655.49 | 188.69 |
|
||||||
|
| q2-4212 | optimal | 750.13 | (343,-10) | 是 | 905.38 | 90.90 | 659.23 | 181.08 |
|
||||||
|
| q2-4212 | baseline_perp | 750.13 | (1486,-980) | 否 | 1817.34 | 90.78 | 659.35 | 200.00 |
|
||||||
|
| q2-4212 | baseline_mec | 750.13 | (520,-187) | 是 | 750.13 | 375.09 | 375.04 | 150.03 |
|
||||||
|
| q2-4212 | reference | 750.13 | (520,-437) | 是 | 845.04 | 93.65 | 656.48 | 169.01 |
|
||||||
|
| q2-4213 | optimal | 750.12 | (-347,-795) | 是 | 859.45 | 92.44 | 657.68 | 171.89 |
|
||||||
|
| q2-4213 | baseline_perp | 750.12 | (-1650,-75) | 否 | 1817.31 | 90.77 | 659.34 | 200.00 |
|
||||||
|
| q2-4213 | baseline_mec | 750.12 | (-524,-618) | 是 | 750.12 | 375.09 | 375.03 | 150.02 |
|
||||||
|
| q2-4213 | reference | 750.12 | (-524,-368) | 是 | 893.01 | 90.88 | 659.24 | 178.60 |
|
||||||
|
| q2-4214 | optimal | 750.16 | (623,-1142) | 是 | 906.31 | 90.87 | 659.29 | 181.26 |
|
||||||
|
| q2-4214 | baseline_perp | 750.16 | (-445,-639) | 否 | 1817.36 | 90.78 | 659.38 | 200.00 |
|
||||||
|
| q2-4214 | baseline_mec | 750.16 | (559,-1383) | 是 | 750.16 | 375.09 | 375.07 | 150.03 |
|
||||||
|
| q2-4214 | reference | 750.16 | (559,-1134) | 是 | 855.60 | 92.58 | 657.58 | 171.12 |
|
||||||
|
| q2-4215 | optimal | 559.27 | (-1338,120) | 是 | 820.56 | 35.56 | 523.71 | 164.11 |
|
||||||
|
| q2-4215 | baseline_perp | 559.27 | (-731,-1350) | 否 | 1513.40 | 59.96 | 499.32 | 200.00 |
|
||||||
|
| q2-4215 | baseline_mec | 559.27 | (-1224,-306) | 是 | 559.27 | 279.64 | 279.64 | 111.85 |
|
||||||
|
| q2-4215 | reference | 559.27 | (-1444,-636) | 是 | 828.17 | 35.86 | 523.42 | 165.63 |
|
||||||
|
| q2-4216 | optimal | 725.32 | (-758,-1062) | 是 | 869.88 | 65.59 | 659.73 | 173.97 |
|
||||||
|
| q2-4216 | baseline_perp | 725.32 | (-669,40) | 否 | 1747.41 | 79.80 | 645.52 | 200.00 |
|
||||||
|
| q2-4216 | baseline_mec | 725.32 | (-1023,-1133) | 是 | 725.32 | 362.66 | 362.66 | 145.06 |
|
||||||
|
| q2-4216 | reference | 725.32 | (-749,-1133) | 是 | 919.99 | 63.67 | 661.65 | 184.00 |
|
||||||
|
| q2-4217 | optimal | 750.16 | (-1472,727) | 是 | 848.85 | 93.13 | 657.03 | 169.77 |
|
||||||
|
| q2-4217 | baseline_perp | 750.16 | (10,819) | 否 | 1817.41 | 90.78 | 659.38 | 200.00 |
|
||||||
|
| q2-4217 | baseline_mec | 750.16 | (-1231,663) | 是 | 750.16 | 375.09 | 375.07 | 150.03 |
|
||||||
|
| q2-4217 | reference | 750.16 | (-1481,663) | 是 | 900.62 | 90.77 | 659.39 | 180.12 |
|
||||||
|
| q2-4218 | optimal | 750.18 | (-332,-300) | 是 | 864.09 | 92.27 | 657.91 | 172.82 |
|
||||||
|
| q2-4218 | baseline_perp | 750.18 | (-1821,-299) | 否 | 1817.39 | 90.78 | 659.39 | 200.00 |
|
||||||
|
| q2-4218 | baseline_mec | 750.18 | (-573,-235) | 是 | 750.18 | 375.09 | 375.08 | 150.04 |
|
||||||
|
| q2-4218 | reference | 750.18 | (-323,-235) | 是 | 913.40 | 91.48 | 658.69 | 182.68 |
|
||||||
|
| q2-4219 | optimal | 750.13 | (-75,786) | 是 | 868.14 | 91.76 | 658.37 | 173.63 |
|
||||||
|
| q2-4219 | baseline_perp | 750.13 | (-100,-705) | 否 | 1817.34 | 90.78 | 659.35 | 200.00 |
|
||||||
|
| q2-4219 | baseline_mec | 750.13 | (-140,544) | 是 | 750.13 | 375.09 | 375.04 | 150.03 |
|
||||||
|
| q2-4219 | reference | 750.13 | (-140,794) | 是 | 916.75 | 91.35 | 658.78 | 183.35 |
|
||||||
|
| q2-4220 | optimal | 750.13 | (-142,237) | 是 | 865.60 | 92.06 | 658.07 | 173.12 |
|
||||||
|
| q2-4220 | baseline_perp | 750.13 | (-1191,-184) | 否 | 1817.34 | 90.78 | 659.35 | 200.00 |
|
||||||
|
| q2-4220 | baseline_mec | 750.13 | (35,60) | 是 | 750.13 | 375.09 | 375.04 | 150.03 |
|
||||||
|
| q2-4220 | reference | 750.13 | (285,60) | 是 | 887.39 | 90.95 | 659.18 | 177.48 |
|
||||||
|
| q2-4221 | optimal | 750.12 | (537,-636) | 是 | 859.18 | 92.47 | 657.65 | 171.84 |
|
||||||
|
| q2-4221 | baseline_perp | 750.12 | (-232,638) | 否 | 1817.32 | 90.78 | 659.35 | 200.00 |
|
||||||
|
| q2-4221 | baseline_mec | 750.12 | (472,-395) | 是 | 750.12 | 375.09 | 375.03 | 150.02 |
|
||||||
|
| q2-4221 | reference | 750.12 | (472,-644) | 是 | 801.14 | 98.78 | 651.34 | 160.23 |
|
||||||
|
| q2-4222 | optimal | 672.85 | (1340,149) | 是 | 896.12 | 46.69 | 626.15 | 179.22 |
|
||||||
|
| q2-4222 | baseline_perp | 672.85 | (461,-526) | 否 | 1662.43 | 70.95 | 601.90 | 200.00 |
|
||||||
|
| q2-4222 | baseline_mec | 672.85 | (1571,-82) | 是 | 672.85 | 336.42 | 336.42 | 134.57 |
|
||||||
|
| q2-4222 | reference | 672.85 | (1326,82) | 是 | 830.57 | 53.42 | 619.43 | 166.11 |
|
||||||
|
| q2-4223 | optimal | 395.70 | (-1736,-787) | 是 | 696.84 | 21.22 | 374.48 | 139.37 |
|
||||||
|
| q2-4223 | baseline_perp | 395.70 | (-1461,-1548) | 否 | 1286.11 | 39.72 | 355.98 | 200.00 |
|
||||||
|
| q2-4223 | baseline_mec | 395.70 | (-1416,-467) | 是 | 395.70 | 197.85 | 197.85 | 79.14 |
|
||||||
|
| q2-4223 | reference | 395.70 | (-1416,-14) | 是 | 716.03 | 20.83 | 374.87 | 143.21 |
|
||||||
|
| q2-4224 | optimal | 750.16 | (-899,669) | 是 | 888.47 | 90.87 | 659.29 | 177.69 |
|
||||||
|
| q2-4224 | baseline_perp | 750.16 | (467,1281) | 否 | 1817.40 | 90.78 | 659.38 | 200.00 |
|
||||||
|
| q2-4224 | baseline_mec | 750.16 | (-657,734) | 是 | 750.16 | 375.09 | 375.07 | 150.03 |
|
||||||
|
| q2-4224 | reference | 750.16 | (-907,734) | 是 | 834.61 | 94.50 | 655.66 | 166.92 |
|
||||||
|
| q2-4225 | optimal | 750.12 | (-305,-294) | 是 | 866.51 | 91.95 | 658.17 | 173.30 |
|
||||||
|
| q2-4225 | baseline_perp | 750.12 | (-1064,-1577) | 否 | 1817.31 | 90.77 | 659.34 | 200.00 |
|
||||||
|
| q2-4225 | baseline_mec | 750.12 | (-482,-470) | 是 | 750.12 | 375.10 | 375.02 | 150.02 |
|
||||||
|
| q2-4225 | reference | 750.12 | (-732,-470) | 是 | 886.54 | 91.02 | 659.10 | 177.31 |
|
||||||
|
| q2-4226 | optimal | 750.12 | (587,389) | 是 | 898.38 | 90.90 | 659.22 | 179.68 |
|
||||||
|
| q2-4226 | baseline_perp | 750.12 | (1757,433) | 否 | 1817.31 | 90.77 | 659.34 | 200.00 |
|
||||||
|
| q2-4226 | baseline_mec | 750.12 | (523,630) | 是 | 750.12 | 375.11 | 375.01 | 150.02 |
|
||||||
|
| q2-4226 | reference | 750.12 | (523,380) | 是 | 941.12 | 94.47 | 655.65 | 188.22 |
|
||||||
|
| q2-4227 | optimal | 527.91 | (711,-1627) | 是 | 843.99 | 29.98 | 497.92 | 168.80 |
|
||||||
|
| q2-4227 | baseline_perp | 527.91 | (1503,-1122) | 否 | 1466.90 | 55.63 | 472.28 | 200.00 |
|
||||||
|
| q2-4227 | baseline_mec | 527.91 | (377,-1293) | 是 | 527.91 | 263.95 | 263.95 | 105.58 |
|
||||||
|
| q2-4227 | reference | 527.91 | (-95,-1293) | 是 | 819.70 | 30.74 | 497.17 | 163.94 |
|
||||||
|
| q2-4228 | optimal | 750.12 | (-160,534) | 是 | 892.39 | 90.93 | 659.19 | 178.48 |
|
||||||
|
| q2-4228 | baseline_perp | 750.12 | (20,2020) | 否 | 1817.31 | 90.77 | 659.34 | 200.00 |
|
||||||
|
| q2-4228 | baseline_mec | 750.12 | (-96,775) | 是 | 750.12 | 375.11 | 375.01 | 150.02 |
|
||||||
|
| q2-4228 | reference | 750.12 | (-96,525) | 是 | 936.39 | 93.58 | 656.54 | 187.28 |
|
||||||
|
| q2-4229 | optimal | 750.20 | (455,-883) | 是 | 876.70 | 91.34 | 658.86 | 175.34 |
|
||||||
|
| q2-4229 | baseline_perp | 750.20 | (-684,-787) | 否 | 1817.44 | 90.79 | 659.41 | 200.00 |
|
||||||
|
| q2-4229 | baseline_mec | 750.20 | (519,-1124) | 是 | 750.20 | 375.10 | 375.10 | 150.04 |
|
||||||
|
| q2-4229 | reference | 750.20 | (519,-874) | 是 | 923.76 | 91.91 | 658.30 | 184.75 |
|
||||||
|
|
||||||
|
## 逐方法汇总
|
||||||
|
|
||||||
|
| 方法 | 保证接收率 | 最坏后验均值(m) | 中位(m) | 平均收缩(m) | 终值比 ρ'/ρ | 移动均值(s) |
|
||||||
|
| --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
| optimal | 100.0% | 73.89 | 90.90 | 607.58 | 0.104 | 170.44 |
|
||||||
|
| baseline_perp | 0.0% | 80.11 | 90.78 | 601.36 | 0.116 | 200.00 |
|
||||||
|
| baseline_mec | 100.0% | 340.75 | 375.09 | 340.72 | 0.500 | 136.29 |
|
||||||
|
| reference | 100.0% | 75.63 | 91.35 | 605.84 | 0.106 | 170.28 |
|
||||||
|
|
||||||
|
初始 ρ 均值:681.47 m
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,75 @@
|
|||||||
|
# 问题三:全向闭环配对对比(core 基准集)
|
||||||
|
|
||||||
|
## 逐场景明细
|
||||||
|
|
||||||
|
| scene | sources | variant | cleared | T(s) | 检测 | 请求 | 位移(m) | 后备清除 | 墙钟(s) | 有效 |
|
||||||
|
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
| q3-core-s100-n10 | 10 | optimal | 10/10 | 2563.0 | 109 | 119 | 9330.1 | 0 | 0.71 | ✔ |
|
||||||
|
| q3-core-s100-n10 | 10 | seven | 10/10 | 4465.5 | 102 | 112 | 19057.6 | 0 | 1.10 | ✔ |
|
||||||
|
| q3-core-s100-n10 | 10 | baseline | 10/10 | 15309.6 | 564 | 574 | 59457.9 | 0 | 1.16 | ✔ |
|
||||||
|
| q3-core-s100-n13 | 13 | optimal | 13/13 | 3120.8 | 114 | 127 | 11914.1 | 0 | 0.70 | ✔ |
|
||||||
|
| q3-core-s100-n13 | 13 | seven | 13/13 | 5216.0 | 96 | 109 | 22919.8 | 0 | 1.39 | ✔ |
|
||||||
|
| q3-core-s100-n13 | 13 | baseline | 13/13 | 16205.3 | 450 | 463 | 67311.6 | 0 | 1.47 | ✔ |
|
||||||
|
| q3-core-s100-n16 | 16 | optimal | 16/16 | 3618.1 | 113 | 129 | 14355.4 | 0 | 0.83 | ✔ |
|
||||||
|
| q3-core-s100-n16 | 16 | seven | 16/16 | 5978.8 | 96 | 112 | 26653.8 | 0 | 1.63 | ✔ |
|
||||||
|
| q3-core-s100-n16 | 16 | baseline | 16/16 | 13180.4 | 254 | 270 | 58011.9 | 0 | 1.82 | ✔ |
|
||||||
|
| q3-core-s101-n10 | 10 | optimal | 10/10 | 3101.7 | 119 | 129 | 11723.5 | 0 | 0.68 | ✔ |
|
||||||
|
| q3-core-s101-n10 | 10 | seven | 10/10 | 4498.1 | 107 | 117 | 19065.6 | 0 | 1.07 | ✔ |
|
||||||
|
| q3-core-s101-n10 | 10 | baseline | 10/10 | 15552.8 | 545 | 555 | 61248.9 | 0 | 1.10 | ✔ |
|
||||||
|
| q3-core-s101-n13 | 13 | optimal | 13/13 | 2943.3 | 112 | 125 | 11106.4 | 0 | 0.83 | ✔ |
|
||||||
|
| q3-core-s101-n13 | 13 | seven | 13/13 | 5390.3 | 101 | 114 | 23656.4 | 0 | 1.42 | ✔ |
|
||||||
|
| q3-core-s101-n13 | 13 | baseline | 13/13 | 16620.6 | 452 | 465 | 69338.1 | 0 | 1.60 | ✔ |
|
||||||
|
| q3-core-s101-n16 | 16 | optimal | 16/16 | 3560.0 | 99 | 115 | 14469.9 | 0 | 0.81 | ✔ |
|
||||||
|
| q3-core-s101-n16 | 16 | seven | 16/16 | 6462.4 | 92 | 108 | 29211.8 | 0 | 1.75 | ✔ |
|
||||||
|
| q3-core-s101-n16 | 16 | baseline | 16/16 | 13434.4 | 260 | 276 | 59121.8 | 0 | 1.84 | ✔ |
|
||||||
|
| q3-core-s102-n10 | 10 | optimal | 10/10 | 2916.0 | 125 | 135 | 10634.8 | 0 | 0.57 | ✔ |
|
||||||
|
| q3-core-s102-n10 | 10 | seven | 10/10 | 5411.3 | 118 | 128 | 23326.6 | 0 | 1.22 | ✔ |
|
||||||
|
| q3-core-s102-n10 | 10 | baseline | 10/10 | 15015.5 | 613 | 623 | 56517.5 | 0 | 1.13 | ✔ |
|
||||||
|
| q3-core-s102-n13 | 13 | optimal | 13/13 | 3039.5 | 122 | 135 | 11242.3 | 0 | 0.71 | ✔ |
|
||||||
|
| q3-core-s102-n13 | 13 | seven | 13/13 | 5209.5 | 112 | 125 | 22407.3 | 0 | 1.36 | ✔ |
|
||||||
|
| q3-core-s102-n13 | 13 | baseline | 13/13 | 16062.8 | 450 | 463 | 66603.8 | 0 | 1.43 | ✔ |
|
||||||
|
| q3-core-s102-n16 | 16 | optimal | 16/16 | 3266.2 | 119 | 135 | 12441.1 | 0 | 0.68 | ✔ |
|
||||||
|
| q3-core-s102-n16 | 16 | seven | 16/16 | 5549.9 | 97 | 113 | 24489.6 | 0 | 1.65 | ✔ |
|
||||||
|
| q3-core-s102-n16 | 16 | baseline | 16/16 | 13146.4 | 282 | 298 | 57012.1 | 0 | 1.72 | ✔ |
|
||||||
|
| q3-core-s103-n10 | 10 | optimal | 10/10 | 2847.8 | 112 | 122 | 10684.1 | 0 | 0.67 | ✔ |
|
||||||
|
| q3-core-s103-n10 | 10 | seven | 10/10 | 4877.3 | 110 | 120 | 20886.4 | 0 | 1.19 | ✔ |
|
||||||
|
| q3-core-s103-n10 | 10 | baseline | 10/10 | 14895.2 | 550 | 560 | 57815.9 | 0 | 1.20 | ✔ |
|
||||||
|
| q3-core-s103-n13 | 13 | optimal | 13/13 | 3339.9 | 114 | 127 | 13024.4 | 0 | 0.70 | ✔ |
|
||||||
|
| q3-core-s103-n13 | 13 | seven | 13/13 | 5344.6 | 102 | 115 | 23373.1 | 0 | 1.21 | ✔ |
|
||||||
|
| q3-core-s103-n13 | 13 | baseline | 13/13 | 16691.4 | 501 | 514 | 68212.0 | 0 | 1.47 | ✔ |
|
||||||
|
| q3-core-s103-n16 | 16 | optimal | 16/16 | 3378.8 | 99 | 115 | 13559.2 | 0 | 0.86 | ✔ |
|
||||||
|
| q3-core-s103-n16 | 16 | seven | 16/16 | 5526.4 | 87 | 103 | 24681.8 | 0 | 1.73 | ✔ |
|
||||||
|
| q3-core-s103-n16 | 16 | baseline | 16/16 | 12627.8 | 256 | 272 | 55189.0 | 0 | 1.81 | ✔ |
|
||||||
|
| q3-core-s104-n10 | 10 | optimal | 10/10 | 3317.0 | 115 | 125 | 12950.2 | 0 | 0.64 | ✔ |
|
||||||
|
| q3-core-s104-n10 | 10 | seven | 10/10 | 4915.9 | 105 | 115 | 21219.3 | 0 | 1.00 | ✔ |
|
||||||
|
| q3-core-s104-n10 | 10 | baseline | 10/10 | 15302.4 | 549 | 559 | 59887.2 | 0 | 1.18 | ✔ |
|
||||||
|
| q3-core-s104-n13 | 13 | optimal | 13/13 | 3085.2 | 99 | 112 | 12176.2 | 0 | 0.70 | ✔ |
|
||||||
|
| q3-core-s104-n13 | 13 | seven | 13/13 | 5437.5 | 97 | 110 | 24012.7 | 0 | 1.47 | ✔ |
|
||||||
|
| q3-core-s104-n13 | 13 | baseline | 13/13 | 16278.7 | 444 | 457 | 67868.5 | 0 | 1.55 | ✔ |
|
||||||
|
| q3-core-s104-n16 | 16 | optimal | 16/16 | 3057.8 | 85 | 101 | 12379.1 | 0 | 0.76 | ✔ |
|
||||||
|
| q3-core-s104-n16 | 16 | seven | 16/16 | 5340.9 | 85 | 101 | 23799.8 | 0 | 1.65 | ✔ |
|
||||||
|
| q3-core-s104-n16 | 16 | baseline | 16/16 | 12872.6 | 224 | 240 | 57378.0 | 0 | 1.83 | ✔ |
|
||||||
|
| q3-core-s105-n10 | 10 | optimal | 10/10 | 2933.1 | 108 | 118 | 11220.4 | 0 | 0.70 | ✔ |
|
||||||
|
| q3-core-s105-n10 | 10 | seven | 10/10 | 4370.8 | 107 | 117 | 18433.9 | 0 | 1.08 | ✔ |
|
||||||
|
| q3-core-s105-n10 | 10 | baseline | 10/10 | 15247.7 | 579 | 589 | 58688.4 | 0 | 1.12 | ✔ |
|
||||||
|
| q3-core-s105-n13 | 13 | optimal | 13/13 | 3645.3 | 122 | 135 | 14331.4 | 0 | 0.79 | ✔ |
|
||||||
|
| q3-core-s105-n13 | 13 | seven | 13/13 | 6311.3 | 104 | 117 | 28171.5 | 0 | 1.41 | ✔ |
|
||||||
|
| q3-core-s105-n13 | 13 | baseline | 13/13 | 16509.4 | 485 | 498 | 67791.8 | 0 | 1.51 | ✔ |
|
||||||
|
| q3-core-s105-n16 | 16 | optimal | 16/16 | 3663.7 | 114 | 130 | 14543.5 | 0 | 0.80 | ✔ |
|
||||||
|
| q3-core-s105-n16 | 16 | seven | 16/16 | 6204.3 | 97 | 113 | 27776.7 | 0 | 1.55 | ✔ |
|
||||||
|
| q3-core-s105-n16 | 16 | baseline | 16/16 | 13889.0 | 292 | 308 | 60450.1 | 0 | 1.85 | ✔ |
|
||||||
|
|
||||||
|
## 逐方法汇总
|
||||||
|
|
||||||
|
| 方法 | 有效局 | 全清率 | 全清对数 | 缺席误判 | T 均值(s) | T 中位(s) | T 最大(s) | 检测均值 | 请求均值 | 位移均值(m) | 后备清除均值 | 墙钟均值(s) |
|
||||||
|
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
| optimal | 18/18 | 1.00 | 18 | 0 | 3188.7 | 3111.3 | 3663.7 | 111.1 | 124.1 | 12338.1 | 0.00 | 0.73 |
|
||||||
|
| seven | 18/18 | 1.00 | 18 | 0 | 5361.7 | 5367.5 | 6462.4 | 100.8 | 113.8 | 23508.0 | 0.00 | 1.38 |
|
||||||
|
| baseline | 18/18 | 1.00 | 18 | 0 | 14935.7 | 15275.1 | 16691.4 | 430.6 | 443.6 | 61550.2 | 0.00 | 1.49 |
|
||||||
|
|
||||||
|
## 相对基线的配对变化(虚拟时间)
|
||||||
|
|
||||||
|
| 配对 | 均值变化 | 中位变化 | 更快场景 | 双方均有效 |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| optimal_vs_baseline | -78.4% | -80.0% | 18/18 | 18 |
|
||||||
|
| seven_vs_baseline | -63.7% | -66.9% | 18/18 | 18 |
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,56 @@
|
|||||||
|
# 问题四:混合源闭环配对对比(core+boundary 基准集)
|
||||||
|
|
||||||
|
## 逐场景明细
|
||||||
|
|
||||||
|
| scene | sources | variant | cleared | T(s) | 检测 | 请求 | 位移(m) | 后备清除 | 墙钟(s) | 有效 |
|
||||||
|
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
| q4-boundary-n10-d5 | 10 | optimal | 10/10 | 10269.5 | 488 | 540 | 36107.7 | 44 | 2.53 | ✔ |
|
||||||
|
| q4-boundary-n10-d5 | 10 | baseline_grid | 6/10 | 17632.7 | 735 | 741 | 66308.6 | 0 | 5.04 | ✘ |
|
||||||
|
| q4-boundary-n10-d5 | 10 | baseline | 5/10 | 3128.1 | 135 | 140 | 11530.4 | 0 | 0.48 | ✘ |
|
||||||
|
| q4-boundary-n16-d8 | 16 | optimal | 16/16 | 12081.1 | 421 | 626 | 44820.7 | 190 | 3.54 | ✔ |
|
||||||
|
| q4-boundary-n16-d8 | 16 | baseline_grid | 9/16 | 20457.9 | 623 | 632 | 83969.3 | 0 | 8.71 | ✘ |
|
||||||
|
| q4-boundary-n16-d8 | 16 | baseline | 8/16 | 3558.7 | 149 | 157 | 13173.5 | 0 | 0.51 | ✘ |
|
||||||
|
| q4-core-s200-n10-d3 | 10 | optimal | 10/10 | 8718.7 | 402 | 412 | 31508.6 | 0 | 7.61 | ✔ |
|
||||||
|
| q4-core-s200-n10-d3 | 10 | baseline_grid | 9/10 | 16546.8 | 576 | 585 | 65379.2 | 0 | 2.52 | ✘ |
|
||||||
|
| q4-core-s200-n10-d3 | 10 | baseline | 10/10 | 3354.1 | 116 | 126 | 13100.5 | 0 | 0.66 | ✘ |
|
||||||
|
| q4-core-s200-n13-d6 | 13 | optimal | 13/13 | 8808.4 | 332 | 347 | 33967.1 | 3 | 2.71 | ✔ |
|
||||||
|
| q4-core-s200-n13-d6 | 13 | baseline_grid | 7/13 | 18438.3 | 529 | 536 | 76651.4 | 0 | 8.12 | ✘ |
|
||||||
|
| q4-core-s200-n13-d6 | 13 | baseline | 11/13 | 3264.4 | 111 | 122 | 12761.9 | 0 | 0.69 | ✘ |
|
||||||
|
| q4-core-s200-n16-d8 | 16 | optimal | 16/16 | 7629.2 | 267 | 283 | 29970.8 | 0 | 2.77 | ✔ |
|
||||||
|
| q4-core-s200-n16-d8 | 16 | baseline_grid | 10/16 | 18871.3 | 526 | 536 | 78861.6 | 0 | 8.97 | ✘ |
|
||||||
|
| q4-core-s200-n16-d8 | 16 | baseline | 12/16 | 4026.1 | 143 | 155 | 15645.6 | 0 | 1.84 | ✘ |
|
||||||
|
| q4-core-s201-n10-d3 | 10 | optimal | 10/10 | 8514.0 | 380 | 391 | 31129.9 | 2 | 1.94 | ✔ |
|
||||||
|
| q4-core-s201-n10-d3 | 10 | baseline_grid | 7/10 | 16779.7 | 624 | 631 | 65293.4 | 0 | 5.42 | ✘ |
|
||||||
|
| q4-core-s201-n10-d3 | 10 | baseline | 7/10 | 2823.3 | 120 | 127 | 10391.4 | 0 | 0.61 | ✘ |
|
||||||
|
| q4-core-s201-n13-d6 | 13 | optimal | 13/13 | 9183.0 | 325 | 338 | 36095.1 | 0 | 2.47 | ✔ |
|
||||||
|
| q4-core-s201-n13-d6 | 13 | baseline_grid | 8/13 | 19390.8 | 584 | 592 | 79678.9 | 0 | 4.29 | ✘ |
|
||||||
|
| q4-core-s201-n13-d6 | 13 | baseline | 11/13 | 4965.2 | 143 | 165 | 20260.8 | 0 | 1.09 | ✘ |
|
||||||
|
| q4-core-s201-n16-d8 | 16 | optimal | 16/16 | 8129.4 | 263 | 279 | 32667.0 | 0 | 3.29 | ✔ |
|
||||||
|
| q4-core-s201-n16-d8 | 16 | baseline_grid | 9/16 | 19845.0 | 495 | 504 | 84765.2 | 0 | 10.53 | ✘ |
|
||||||
|
| q4-core-s201-n16-d8 | 16 | baseline | 13/16 | 4759.1 | 125 | 138 | 19835.5 | 0 | 1.85 | ✘ |
|
||||||
|
| q4-core-s202-n10-d3 | 10 | optimal | 10/10 | 8803.3 | 420 | 430 | 31426.3 | 0 | 2.16 | ✔ |
|
||||||
|
| q4-core-s202-n10-d3 | 10 | baseline_grid | 7/10 | 17220.0 | 669 | 676 | 66144.9 | 0 | 4.67 | ✘ |
|
||||||
|
| q4-core-s202-n10-d3 | 10 | baseline | 7/10 | 3646.2 | 135 | 142 | 14105.9 | 0 | 0.66 | ✘ |
|
||||||
|
| q4-core-s202-n13-d6 | 13 | optimal | 13/13 | 9630.4 | 347 | 367 | 37652.0 | 10 | 3.09 | ✔ |
|
||||||
|
| q4-core-s202-n13-d6 | 13 | baseline_grid | 10/13 | 16443.2 | 498 | 508 | 67316.3 | 0 | 1.92 | ✘ |
|
||||||
|
| q4-core-s202-n13-d6 | 13 | baseline | 13/13 | 4219.3 | 123 | 137 | 17136.7 | 0 | 0.75 | ✘ |
|
||||||
|
| q4-core-s202-n16-d8 | 16 | optimal | 16/16 | 8001.5 | 267 | 305 | 31602.7 | 24 | 3.52 | ✔ |
|
||||||
|
| q4-core-s202-n16-d8 | 16 | baseline_grid | 12/16 | 18345.2 | 452 | 464 | 78265.8 | 0 | 8.12 | ✘ |
|
||||||
|
| q4-core-s202-n16-d8 | 16 | baseline | 13/16 | 5054.4 | 152 | 165 | 20561.9 | 0 | 1.98 | ✘ |
|
||||||
|
|
||||||
|
## 逐方法汇总
|
||||||
|
|
||||||
|
| 方法 | 有效局 | 全清率 | 全清对数 | 缺席误判 | T 均值(s) | T 中位(s) | T 最大(s) | 检测均值 | 请求均值 | 位移均值(m) | 后备清除均值 | 墙钟均值(s) |
|
||||||
|
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
| optimal | 11/11 | 1.00 | 11 | 0 | 9069.9 | 8803.3 | 12081.1 | 355.6 | 392.5 | 34268.0 | 24.82 | 3.24 |
|
||||||
|
| baseline_grid | 0/11 | 0.67 | 0 | 0 | 18179.2 | 18345.2 | 20457.9 | 573.7 | 582.3 | 73875.9 | 0.00 | 6.21 |
|
||||||
|
| baseline | 0/11 | 0.77 | 2 | 24 | 3890.8 | 3646.2 | 5054.4 | 132.0 | 143.1 | 15318.6 | 0.00 | 1.01 |
|
||||||
|
|
||||||
|
## 相对基线的配对变化(虚拟时间)
|
||||||
|
|
||||||
|
| 配对 | 均值变化 | 中位变化 | 更快场景 | 双方均有效 |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| optimal_vs_baseline | +142.9% | +141.4% | 0/11 | 0 |
|
||||||
|
| baseline_grid_vs_baseline | +381.1% | +372.3% | 0/11 | 0 |
|
||||||
|
|
||||||
|
> 注:optimal_vs_baseline、baseline_grid_vs_baseline 存在未通过有效性判定的配对;其虚拟时间仅作参考,基线未完成全清任务时不构成「更快」证据(以「双方均有效」列为准)。
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# 1-4 问最优 vs 基线:对比总览
|
||||||
|
|
||||||
|
本节所有数字由 `uv run python -m project.run` 在同机同口径下生成,
|
||||||
|
逐问明细见 `q1.md` … `q4.md`。
|
||||||
|
|
||||||
|
| 问 | 核心指标 | 最优实现 | 基线实现 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 1 | 直径平均误差 / 覆盖准确率 | 0.00 m / 100% | 4.58 m / 52% |
|
||||||
|
| 2 | 保证接收率 / 最坏后验均值 | 100% / 73.9 m | 100% / 340.7 m |
|
||||||
|
| 3 | 全清率 / T 均值(相对基线) | 100% / 3189 s (-78%) | 100% / 14936 s |
|
||||||
|
| 4 | 全清率 / 缺席误判(问题三策略搬用) | 100% / 0 | 77% / 24 |
|
||||||
|
|
||||||
|
## 方法映射
|
||||||
|
|
||||||
|
| 问 | 最优实现 | 基线实现 | 独立参考 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 1 | 半平面交 + 顶点对直径 + MEC | 网格采样 + bbox 对角线代理 | 大框半平面裁剪 + 精确直径/MEC |
|
||||||
|
| 2 | 保证接收盘内最小最坏后验半径 | 垂直 1000 m / MEC 圆心 | 保证接收盘内网格枚举 |
|
||||||
|
| 3 | bayes(粒子滤波 + 信息增益 + 滚动调度) | grid(49 点网格)/ seven(七点批量) | 冻结基准集 + 逐微秒计时复算 |
|
||||||
|
| 4 | directional(联合信念 + 后备清除) | bayes / grid 直接搬用 | 冻结基准集 + 真值全清校验 |
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
```text
|
||||||
|
project/
|
||||||
|
├── common.py 公共层:方法描述/指标聚合/结果落盘/闭环装配
|
||||||
|
├── q1/ 问题一:直径与直径圆覆盖
|
||||||
|
│ ├── optimal.py 生产实现:半平面交 + 顶点对直径 + MEC
|
||||||
|
│ ├── baseline.py 基线:网格采样 + bbox 对角线代理
|
||||||
|
│ ├── reference.py 参考:大框半平面裁剪 + 精确直径/MEC
|
||||||
|
│ ├── cases.py 固定示例(examples/q1)+ 随机交会算例
|
||||||
|
│ └── compare.py 同案配对对比
|
||||||
|
├── q2/ 问题二:第二检测点选择
|
||||||
|
│ ├── optimal.py 生产实现:保证接收盘内最小最坏后验半径
|
||||||
|
│ ├── baseline.py 基线:垂直 1000 m / MEC 圆心
|
||||||
|
│ ├── reference.py 参考:保证接收盘内网格枚举
|
||||||
|
│ ├── region.py K1 统一构造
|
||||||
|
│ ├── cases.py 随机首次示向度算例
|
||||||
|
│ └── compare.py
|
||||||
|
├── q3/ 问题三:全向闭环
|
||||||
|
│ ├── optimal.py bayes(生产默认策略)
|
||||||
|
│ ├── baseline.py grid(49 点网格)/ seven(七点批量)
|
||||||
|
│ └── compare.py
|
||||||
|
├── q4/ 问题四:混合源闭环
|
||||||
|
│ ├── optimal.py directional(定向扩展)
|
||||||
|
│ ├── baseline.py bayes / grid 直接搬用
|
||||||
|
│ └── compare.py
|
||||||
|
├── run.py 统一入口(本文件)
|
||||||
|
└── results/ 自动生成:q1.md/json … q4.md/json + summary.md
|
||||||
|
```
|
||||||
+148
@@ -0,0 +1,148 @@
|
|||||||
|
"""统一入口:一次跑完 1-4 问的"最优 vs 基线"对比并生成 results/。
|
||||||
|
|
||||||
|
用法(仓库根目录)::
|
||||||
|
|
||||||
|
uv run python -m project.run # 四问全部
|
||||||
|
uv run python -m project.run --question 3 # 只跑第三问
|
||||||
|
uv run python -m project.run --quick # 缩小算例/基准集规模
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import time
|
||||||
|
|
||||||
|
from project.common import RESULTS_DIR, markdown_table, write_text
|
||||||
|
from project.q1 import compare as q1_compare
|
||||||
|
from project.q2 import compare as q2_compare
|
||||||
|
from project.q3 import compare as q3_compare
|
||||||
|
from project.q4 import compare as q4_compare
|
||||||
|
|
||||||
|
RUNNERS = {
|
||||||
|
1: q1_compare.run,
|
||||||
|
2: q2_compare.run,
|
||||||
|
3: q3_compare.run,
|
||||||
|
4: q4_compare.run,
|
||||||
|
}
|
||||||
|
|
||||||
|
STRUCTURE = """\
|
||||||
|
project/
|
||||||
|
├── common.py 公共层:方法描述/指标聚合/结果落盘/闭环装配
|
||||||
|
├── q1/ 问题一:直径与直径圆覆盖
|
||||||
|
│ ├── optimal.py 生产实现:半平面交 + 顶点对直径 + MEC
|
||||||
|
│ ├── baseline.py 基线:网格采样 + bbox 对角线代理
|
||||||
|
│ ├── reference.py 参考:大框半平面裁剪 + 精确直径/MEC
|
||||||
|
│ ├── cases.py 固定示例(examples/q1)+ 随机交会算例
|
||||||
|
│ └── compare.py 同案配对对比
|
||||||
|
├── q2/ 问题二:第二检测点选择
|
||||||
|
│ ├── optimal.py 生产实现:保证接收盘内最小最坏后验半径
|
||||||
|
│ ├── baseline.py 基线:垂直 1000 m / MEC 圆心
|
||||||
|
│ ├── reference.py 参考:保证接收盘内网格枚举
|
||||||
|
│ ├── region.py K1 统一构造
|
||||||
|
│ ├── cases.py 随机首次示向度算例
|
||||||
|
│ └── compare.py
|
||||||
|
├── q3/ 问题三:全向闭环
|
||||||
|
│ ├── optimal.py bayes(生产默认策略)
|
||||||
|
│ ├── baseline.py grid(49 点网格)/ seven(七点批量)
|
||||||
|
│ └── compare.py
|
||||||
|
├── q4/ 问题四:混合源闭环
|
||||||
|
│ ├── optimal.py directional(定向扩展)
|
||||||
|
│ ├── baseline.py bayes / grid 直接搬用
|
||||||
|
│ └── compare.py
|
||||||
|
├── run.py 统一入口(本文件)
|
||||||
|
└── results/ 自动生成:q1.md/json … q4.md/json + summary.md
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def _q1_headline(payload: dict) -> list[str]:
|
||||||
|
agg = payload["aggregate_random"]
|
||||||
|
return ["1", "直径平均误差 / 覆盖准确率",
|
||||||
|
f"{agg['diameter_mae_optimal_m']:.2f} m / "
|
||||||
|
f"{agg['coverage_accuracy_optimal']*100:.0f}%",
|
||||||
|
f"{agg['diameter_mae_baseline_m']:.2f} m / "
|
||||||
|
f"{agg['coverage_accuracy_baseline']*100:.0f}%"]
|
||||||
|
|
||||||
|
|
||||||
|
def _q2_headline(payload: dict) -> list[str]:
|
||||||
|
agg = payload["aggregate"]
|
||||||
|
opt = agg["optimal"]
|
||||||
|
base = agg["baseline_mec"]
|
||||||
|
return ["2", "保证接收率 / 最坏后验均值",
|
||||||
|
f"{opt['guaranteed_rate']*100:.0f}% / "
|
||||||
|
f"{opt['worst_posterior_mean_m']:.1f} m",
|
||||||
|
f"{base['guaranteed_rate']*100:.0f}% / "
|
||||||
|
f"{base['worst_posterior_mean_m']:.1f} m"]
|
||||||
|
|
||||||
|
|
||||||
|
def _q3_headline(payload: dict) -> list[str]:
|
||||||
|
agg = payload["aggregate"]["variants"]
|
||||||
|
opt, base = agg["optimal"], agg["baseline"]
|
||||||
|
paired = payload["aggregate"]["paired"].get("optimal_vs_baseline", {})
|
||||||
|
return ["3", "全清率 / T 均值(相对基线)",
|
||||||
|
f"{opt['cleared_ratio_mean']*100:.0f}% / "
|
||||||
|
f"{opt['virtual_time_mean_s']:.0f} s "
|
||||||
|
f"({paired.get('mean_relative_change', 0)*100:+.0f}%)",
|
||||||
|
f"{base['cleared_ratio_mean']*100:.0f}% / "
|
||||||
|
f"{base['virtual_time_mean_s']:.0f} s"]
|
||||||
|
|
||||||
|
|
||||||
|
def _q4_headline(payload: dict) -> list[str]:
|
||||||
|
agg = payload["aggregate"]["variants"]
|
||||||
|
opt, base = agg["optimal"], agg["baseline"]
|
||||||
|
return ["4", "全清率 / 缺席误判(问题三策略搬用)",
|
||||||
|
f"{opt['cleared_ratio_mean']*100:.0f}% / "
|
||||||
|
f"{opt['absent_misjudged_total']}",
|
||||||
|
f"{base['cleared_ratio_mean']*100:.0f}% / "
|
||||||
|
f"{base['absent_misjudged_total']}"]
|
||||||
|
|
||||||
|
|
||||||
|
def _write_summary(payloads: dict[int, dict]) -> None:
|
||||||
|
lines = ["# 1-4 问最优 vs 基线:对比总览", "",
|
||||||
|
"本节所有数字由 `uv run python -m project.run` 在同机同口径下生成,",
|
||||||
|
"逐问明细见 `q1.md` … `q4.md`。", ""]
|
||||||
|
rows = []
|
||||||
|
if 1 in payloads:
|
||||||
|
rows.append(_q1_headline(payloads[1]))
|
||||||
|
if 2 in payloads:
|
||||||
|
rows.append(_q2_headline(payloads[2]))
|
||||||
|
if 3 in payloads:
|
||||||
|
rows.append(_q3_headline(payloads[3]))
|
||||||
|
if 4 in payloads:
|
||||||
|
rows.append(_q4_headline(payloads[4]))
|
||||||
|
lines.append(markdown_table(["问", "核心指标", "最优实现", "基线实现"], rows))
|
||||||
|
lines += ["", "## 方法映射", "",
|
||||||
|
"| 问 | 最优实现 | 基线实现 | 独立参考 |",
|
||||||
|
"| --- | --- | --- | --- |",
|
||||||
|
"| 1 | 半平面交 + 顶点对直径 + MEC | 网格采样 + bbox 对角线代理 | 大框半平面裁剪 + 精确直径/MEC |",
|
||||||
|
"| 2 | 保证接收盘内最小最坏后验半径 | 垂直 1000 m / MEC 圆心 | 保证接收盘内网格枚举 |",
|
||||||
|
"| 3 | bayes(粒子滤波 + 信息增益 + 滚动调度) | grid(49 点网格)/ seven(七点批量) | 冻结基准集 + 逐微秒计时复算 |",
|
||||||
|
"| 4 | directional(联合信念 + 后备清除) | bayes / grid 直接搬用 | 冻结基准集 + 真值全清校验 |",
|
||||||
|
"", "## 目录结构", "", "```text", STRUCTURE.rstrip(), "```", ""]
|
||||||
|
write_text(RESULTS_DIR / "summary.md", "\n".join(lines))
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv=None) -> int:
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
||||||
|
parser.add_argument("--question", choices=("1", "2", "3", "4", "all"),
|
||||||
|
default="all")
|
||||||
|
parser.add_argument("--quick", action="store_true",
|
||||||
|
help="缩小随机算例数/冻结基准集规模")
|
||||||
|
args = parser.parse_args(argv)
|
||||||
|
|
||||||
|
selected = [1, 2, 3, 4] if args.question == "all" else [int(args.question)]
|
||||||
|
payloads: dict[int, dict] = {}
|
||||||
|
for question in selected:
|
||||||
|
start = time.perf_counter()
|
||||||
|
print(f"[Q{question}] 运行对比 …", flush=True)
|
||||||
|
payloads[question] = RUNNERS[question](quick=args.quick)
|
||||||
|
print(f"[Q{question}] 完成,用时 {time.perf_counter() - start:.1f}s",
|
||||||
|
flush=True)
|
||||||
|
if selected == [1, 2, 3, 4]:
|
||||||
|
_write_summary(payloads)
|
||||||
|
print(f"总览已写入 {RESULTS_DIR / 'summary.md'}")
|
||||||
|
print(f"结果目录:{RESULTS_DIR}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
Reference in New Issue
Block a user