Merge branch 'q3-bayes-no-ml' of git.simba-robotics.com:HelixCopex/mcm-26-b into q3-bayes-no-ml

This commit is contained in:
2026-09-13 10:55:45 +08:00
21 changed files with 905 additions and 40 deletions
+3 -3
View File
@@ -37,11 +37,11 @@
| 路径 | 内容 | | 路径 | 内容 |
| --- | --- | | --- | --- |
| [`paper/main.tex`](paper/main.tex) | 论文正文(`latexmk -xelatex main.tex`) | | [`paper/main.tex`](paper/main.tex) | 论文正文(`latexmk -xelatex main.tex`);插图经 `\graphicspath` 取自 [`figures/`](figures/README.md) |
| [`paper/README.md`](paper/README.md) | 论文编译方式、目录结构与待办标记 | | [`paper/README.md`](paper/README.md) | 论文编译方式、目录结构与待办标记 |
| [`figures/README.md`](figures/README.md) | 插图清单、配色与字体约定、数据来源与复现命令 | | [`figures/README.md`](figures/README.md) | 插图清单、配色与字体约定、数据来源与复现命令 |
| [`figures/manifest.json`](figures/manifest.json) | 每张图的数据来源与 sha256、生成命令 | | [`figures/manifest.json`](figures/manifest.json) | 每张图的数据来源与 sha256、生成命令 |
| [`../project/README.md`](../project/README.md) | 对比框架目录结构与运行方式 | | [`../project/README.md`](../project/README.md) | 对比框架目录结构与运行方式;结果表由 `scripts/build_paper_support.py` 转入论文第 11 节 |
## 口径登记 ## 口径登记
@@ -54,7 +54,7 @@
| 问题一数值(D、ρ、E) | `mcm26b.geometry.solve` 的返回,或 [`问题一解答.md`](问题一解答.md) | | 问题一数值(D、ρ、E) | `mcm26b.geometry.solve` 的返回,或 [`问题一解答.md`](问题一解答.md) |
| 覆盖计划与证书 | `src/mcm26b/guarantee/plans/*.json`(`q4_mincover_v2.json` 为默认) | | 覆盖计划与证书 | `src/mcm26b/guarantee/plans/*.json`(`q4_mincover_v2.json` 为默认) |
| 实测虚拟时间 | 行为日志 `virtual_time_s` 字段与 `evaluation.recompute_virtual_time` | | 实测虚拟时间 | 行为日志 `virtual_time_s` 字段与 `evaluation.recompute_virtual_time` |
| 冻结基准集对比结果 | `project/results/*.md`(由 `uv run python -m project.run` 生成) | | 冻结基准集对比结果 | `project/results/*.md`(由 `uv run python -m project.run` 生成);论文表格为 `docs/paper/data/project-*.tex`,由 `scripts/build_paper_support.py` 生成 |
已知的两处需要随环境更新的引用: 已知的两处需要随环境更新的引用:
+18 -13
View File
@@ -4,6 +4,11 @@
算好写入 `docs/figures/.data/*.mat`,再由 MATLAB(`scripts/matlab/*.m`)排版出图; 算好写入 `docs/figures/.data/*.mat`,再由 MATLAB(`scripts/matlab/*.m`)排版出图;
`manifest.json` 记录每张图的数据来源(路径 + sha256 前 12 位 + 行数)与生成命令。 `manifest.json` 记录每张图的数据来源(路径 + sha256 前 12 位 + 行数)与生成命令。
本目录的 11 张 PNG 全部进入论文正文:`docs/paper/main.tex` 用
`\graphicspath{{../figures/}}` 直接引用,不在论文里重绘,也不手抄图内数字。
其中回放画布两张与实测计时一张取自裁判机演练留档,
论文图注均标注「演练、非正式成绩」;问题一的三点交会算例取自演练观测。
```bash ```bash
uv run python scripts/build_figure_data.py # 全部 11 张(数据 + 出图) uv run python scripts/build_figure_data.py # 全部 11 张(数据 + 出图)
uv run python scripts/build_figure_data.py --prepare # 只写 .mat 数据 uv run python scripts/build_figure_data.py --prepare # 只写 .mat 数据
@@ -29,19 +34,19 @@ MATLAB 内手动出图:`cd scripts/matlab` 后执行 `run_all_figures`(每
## 图清单 ## 图清单
| 文件 | 内容 | 数据来源 | | 文件 | 内容 | 数据来源 | 论文位置 |
| --- | --- | --- | | --- | --- | --- | --- |
| `flow-overall-architecture.png` | 四层主线:题面抽象(L0)、在线三层(L1)、四问主线(L2)、三条闭环(L3) | `src/mcm26b/config.py`、`docs/B题建模分析与实施计划.md` | | `flow-overall-architecture.png` | 四层主线:题面抽象(L0)、在线三层(L1)、四问主线(L2)、三条闭环(L3) | `src/mcm26b/config.py`、`docs/B题建模分析与实施计划.md` | 图 1(问题分析) |
| `steps-state-machine.png` | 会话状态机(IDLE / 倒计时 / OPEN / CLOSED) | `docs/协议规则表与状态机.md`、`src/mcm26b/protocol/client.py` | | `steps-state-machine.png` | 会话状态机(IDLE / 倒计时 / OPEN / CLOSED) | `docs/协议规则表与状态机.md`、`src/mcm26b/protocol/client.py` | 图 2(计时模型) |
| `timing-attachment-example-105-111-194-199.png` | 附件计时示例:逐动作耗时构成与单步合计 | `docs/协议规则表与状态机.md` | | `timing-attachment-example-105-111-194-199.png` | 附件计时示例:逐动作耗时构成与单步合计 | `docs/协议规则表与状态机.md` | 图 3(计时模型) |
| `timing-measured-q3-10-sources.png` | 实测一局:累计虚拟时间按动作类型的堆叠构成 | `referee-data/sessions/BBM9-HZG2-77N5-NKKC/log.jsonl` | | `timing-measured-q3-10-sources.png` | 实测一局:累计虚拟时间按动作类型的堆叠构成 | `referee-data/sessions/BBM9-HZG2-77N5-NKKC/log.jsonl` | 图 11(时间分解,演练局) |
| `geo-q1-convex-intersection-zoom.png` | 问题一:角域交会总览 + 定位区域放大(直径圆与最小包围圆) | `output/q1/practice/U5ZW-P9KN-TCAG-R7ST_observations.json` | | `geo-q1-convex-intersection-zoom.png` | 问题一:角域交会总览 + 定位区域放大(直径圆与最小包围圆) | `output/q1/practice/U5ZW-P9KN-TCAG-R7ST_observations.json` | 图 4(a)(问题一,演练观测) |
| `geo-q1-diameter-circle-counterexample.png` | 问题一:直径圆不覆盖定位区域的反例(等边三角形布站) | `examples/q1/triangle.json` | | `geo-q1-diameter-circle-counterexample.png` | 问题一:直径圆不覆盖定位区域的反例(等边三角形布站) | `examples/q1/triangle.json` | 图 4(b)(问题一解析反例) |
| `geo-q2-guaranteed-reception-disk.png` | 问题二:截断角域外接圆 `B(c, a)` 与安全候选盘 `B(c, 1000 − a)`,右侧为半径分解 | `src/mcm26b/config.py`、`docs/algo-2.md` | | `geo-q2-guaranteed-reception-disk.png` | 问题二:截断角域外接圆 `B(c, a)` 与安全候选盘 `B(c, 1000 − a)`,右侧为半径分解 | `src/mcm26b/config.py`、`docs/algo-2.md` | 图 5(问题二) |
| `geo-q3-seven-point-coverage.png` | 问题三:七个 1000 m 接收盘覆盖 Ω,标出最差点 | `src/mcm26b/planner/seven.py`、`docs/algo-3.md` | | `geo-q3-seven-point-coverage.png` | 问题三:七个 1000 m 接收盘覆盖 Ω,标出最差点 | `src/mcm26b/planner/seven.py`、`docs/algo-3.md` | 图 6(问题三) |
| `geo-q4-minimum-cover-31-points.png` | 问题四:31 点最小命中集与 ±90° 前向半盘语义 | `src/mcm26b/guarantee/plans/q4_mincover_v2.json` | | `geo-q4-minimum-cover-31-points.png` | 问题四:31 点最小命中集与 ±90° 前向半盘语义 | `src/mcm26b/guarantee/plans/q4_mincover_v2.json` | 图 8(问题四) |
| `replay-webui-q3-omnidirectional.png` | 回放画布(点运动图,全向局):世界视图 + 事件流水 + 虚拟时间轴 | `referee-data/sessions/BBM9-HZG2-77N5-NKKC/log.jsonl` | | `replay-webui-q3-omnidirectional.png` | 回放画布(点运动图,全向局):世界视图 + 事件流水 + 虚拟时间轴 | `referee-data/sessions/BBM9-HZG2-77N5-NKKC/log.jsonl` | 图 7(问题三,演练局) |
| `replay-webui-q4-directional.png` | 回放画布(点运动图,定向源局):世界视图(含定向源 ±90° 前向半盘)+ 事件流水 + 虚拟时间轴 | `referee-data/sessions/GHSN-M38E-74GD-YLUD/log.jsonl` | | `replay-webui-q4-directional.png` | 回放画布(点运动图,定向源局):世界视图(含定向源 ±90° 前向半盘)+ 事件流水 + 虚拟时间轴 | `referee-data/sessions/GHSN-M38E-74GD-YLUD/log.jsonl` | 图 9(问题四,演练局) |
后两张是唯一保留自定义画布的图:把回放器画布上的 Ω 与真值源、移动轨迹、 后两张是唯一保留自定义画布的图:把回放器画布上的 Ω 与真值源、移动轨迹、
检测示向度射线、定向源朝向与清除标记,连同右侧事件流水与底部虚拟时间轴一起导出; 检测示向度射线、定向源朝向与清除标记,连同右侧事件流水与底部虚拟时间轴一起导出;
@@ -1,7 +1,9 @@
"""Build vector figure sources, complete source listings, and a compact support ZIP. """Build vector figure sources, comparison tables, source listings, and a support ZIP.
Formal figures use the audited snapshot only. Geometric figures use declared Formal figures use the audited snapshot only. Geometric figures use declared
algorithm parameters, not fabricated observations or hidden simulator data. algorithm parameters, not fabricated observations or hidden simulator data.
The paired optimal-versus-baseline tables are generated from project/results/*.json
so that no number in the comparison section is hand copied.
""" """
from __future__ import annotations from __future__ import annotations
@@ -9,11 +11,12 @@ import argparse
import hashlib import hashlib
import json import json
import zipfile import zipfile
from decimal import Decimal from decimal import Decimal, ROUND_HALF_UP
from pathlib import Path from pathlib import Path
ROOT = Path(__file__).resolve().parents[1] ROOT = Path(__file__).resolve().parents[1]
PAPER = ROOT / "docs/paper" PAPER = ROOT / "docs/paper"
PROJECT_RESULTS = ROOT / "project/results"
# Display copies only: executable Python files remain byte-for-byte unchanged. # Display copies only: executable Python files remain byte-for-byte unchanged.
ESCAPE_OPEN = "(" + "*@" ESCAPE_OPEN = "(" + "*@"
@@ -101,6 +104,164 @@ def figures(rows: list[dict]) -> None:
(out / "formal-time-bars.tex").write_text("\n".join(bars) + "\n", "utf-8") (out / "formal-time-bars.tex").write_text("\n".join(bars) + "\n", "utf-8")
def fixed(value: float | None, digits: int = 3) -> str:
if value is None:
return "--"
return str(Decimal(str(value)).quantize(Decimal(1).scaleb(-digits), rounding=ROUND_HALF_UP))
def percent(value: float | None, digits: int = 1) -> str:
if value is None:
return "--"
return fixed(100 * value, digits) + r"\%"
def flag(value: bool | None) -> str:
return "--" if value is None else ("是" if value else "否")
def project_tables() -> None:
"""Generate the optimal-versus-baseline comparison tables from project/results."""
out = PAPER / "data"
out.mkdir(parents=True, exist_ok=True)
data = {name: json.loads((PROJECT_RESULTS / f"{name}.json").read_text("utf-8"))
for name in ("q1", "q2", "q3", "q4")}
examples, random = data["q1"]["aggregate_examples"], data["q1"]["aggregate_random"]
two = data["q2"]["aggregate"]
three, paired3 = data["q3"]["aggregate"]["variants"], data["q3"]["aggregate"]["paired"]
four, paired4 = data["q4"]["aggregate"]["variants"], data["q4"]["aggregate"]["paired"]
head = "% Generated by scripts/build_paper_support.py from project/results/*.json."
macros = [
("ProjQOneExampleCases", str(examples["cases"])),
("ProjQOneRandomCases", str(random["cases"])),
("ProjQOneFixedStatusBase", percent(examples["status_accuracy_baseline"])),
("ProjQOneFixedMaeBase", fixed(examples["diameter_mae_baseline_m"])),
("ProjQOneFixedMaeMaxBase", fixed(examples["diameter_max_err_baseline_m"])),
("ProjQOneFixedCoverBase", percent(examples["coverage_accuracy_baseline"])),
("ProjQOneFixedMsOpt", fixed(examples["runtime_optimal_mean_ms"])),
("ProjQOneFixedMsBase", fixed(examples["runtime_baseline_mean_ms"])),
("ProjQOneRandStatusBase", percent(random["status_accuracy_baseline"])),
("ProjQOneRandMaeBase", fixed(random["diameter_mae_baseline_m"])),
("ProjQOneRandMaeMaxBase", fixed(random["diameter_max_err_baseline_m"])),
("ProjQOneRandCoverBase", percent(random["coverage_accuracy_baseline"])),
("ProjQOneRandMsOpt", fixed(random["runtime_optimal_mean_ms"])),
("ProjQOneRandMsBase", fixed(random["runtime_baseline_mean_ms"])),
("ProjQTwoCases", str(two["optimal"]["cases"])),
("ProjQTwoInitialRadius", fixed(two["_initial_radius_mean_m"])),
("ProjQTwoGuaranteeOpt", percent(two["optimal"]["guaranteed_rate"])),
("ProjQTwoGuaranteeMec", percent(two["baseline_mec"]["guaranteed_rate"])),
("ProjQTwoGuaranteePerp", percent(two["baseline_perp"]["guaranteed_rate"])),
("ProjQTwoWorstOpt", fixed(two["optimal"]["worst_posterior_mean_m"])),
("ProjQTwoWorstRef", fixed(two["reference"]["worst_posterior_mean_m"])),
("ProjQTwoRatioOpt", fixed(two["optimal"]["final_ratio_mean"])),
("ProjQTwoRatioMec", fixed(two["baseline_mec"]["final_ratio_mean"])),
("ProjQTwoWorstPerp", fixed(two["baseline_perp"]["worst_posterior_mean_m"])),
("ProjQTwoWorstMec", fixed(two["baseline_mec"]["worst_posterior_mean_m"])),
("ProjQThreeScenes", str(three["optimal"]["episodes"])),
("ProjQThreeMeanOpt", fixed(three["optimal"]["virtual_time_mean_s"])),
("ProjQThreeMeanSeven", fixed(three["seven"]["virtual_time_mean_s"])),
("ProjQThreeMeanBase", fixed(three["baseline"]["virtual_time_mean_s"])),
("ProjQThreeDropOpt", percent(-paired3["optimal_vs_baseline"]["mean_relative_change"])),
("ProjQThreeDropMedianOpt",
percent(-paired3["optimal_vs_baseline"]["median_relative_change"])),
("ProjQThreeDropSeven", percent(-paired3["seven_vs_baseline"]["mean_relative_change"])),
("ProjQThreeFasterOpt",
f"{paired3['optimal_vs_baseline']['faster_scenes']}/{paired3['optimal_vs_baseline']['compared']}"),
("ProjQThreeMeasuresOpt", fixed(three["optimal"]["measures_mean"])),
("ProjQThreeMeasuresBase", fixed(three["baseline"]["measures_mean"])),
("ProjQThreeMoveOpt", fixed(three["optimal"]["move_distance_mean_m"] / 1000)),
("ProjQThreeMoveBase", fixed(three["baseline"]["move_distance_mean_m"] / 1000)),
("ProjQThreeWallOpt", fixed(three["optimal"]["compute_wall_mean_s"])),
("ProjQFourScenes", str(four["optimal"]["episodes"])),
("ProjQFourMeanOpt", fixed(four["optimal"]["virtual_time_mean_s"])),
("ProjQFourMeanGrid", fixed(four["baseline_grid"]["virtual_time_mean_s"])),
("ProjQFourMeanBase", fixed(four["baseline"]["virtual_time_mean_s"])),
("ProjQFourClearOpt", percent(four["optimal"]["cleared_ratio_mean"])),
("ProjQFourClearGrid", percent(four["baseline_grid"]["cleared_ratio_mean"])),
("ProjQFourClearBase", percent(four["baseline"]["cleared_ratio_mean"])),
("ProjQFourAbsentBase", str(four["baseline"]["absent_misjudged_total"])),
("ProjQFourMeasuresOpt", fixed(four["optimal"]["measures_mean"])),
("ProjQFourMeasuresBase", fixed(four["baseline"]["measures_mean"])),
("ProjQFourFallbackOpt", fixed(four["optimal"]["fallback_clear_attempts_mean"])),
("ProjQFourValidOpt",
f"{four['optimal']['valid_episodes']}/{four['optimal']['episodes']}"),
("ProjQFourValidGrid",
f"{four['baseline_grid']['valid_episodes']}/{four['baseline_grid']['episodes']}"),
("ProjQFourRiseOpt", percent(paired4["optimal_vs_baseline"]["mean_relative_change"])),
("ProjQFourMoveOpt", fixed(four["optimal"]["move_distance_mean_m"] / 1000)),
("ProjQFourMoveBase", fixed(four["baseline"]["move_distance_mean_m"] / 1000)),
]
lines = [head] + [f"\\newcommand{{\\{name}}}{{{value}}}" for name, value in macros]
(out / "project-macros.tex").write_text("\n".join(lines) + "\n", "utf-8")
def write(stem: str, rows: list[str]) -> None:
(out / stem).write_text("\n".join([head, *rows]) + "\n", "utf-8")
write("project-overview-rows.tex", [
"问题 & 核心指标 & 最优实现 & 基线实现 " + r"\\",
f"1 & 直径平均绝对误差 / 覆盖判定准确率 & {fixed(random['diameter_mae_optimal_m'])} m / "
f"{percent(random['coverage_accuracy_optimal'])} & {fixed(random['diameter_mae_baseline_m'])} m / "
f"{percent(random['coverage_accuracy_baseline'])} " + r"\\",
f"2 & 保证接收率 / 最坏后验半径均值 & {percent(two['optimal']['guaranteed_rate'])} / "
f"{fixed(two['optimal']['worst_posterior_mean_m'])} m & {percent(two['baseline_mec']['guaranteed_rate'])} / "
f"{fixed(two['baseline_mec']['worst_posterior_mean_m'])} m " + r"\\",
f"3 & 全清率 / 平均虚拟耗时 & {percent(three['optimal']['cleared_ratio_mean'])} / "
f"{fixed(three['optimal']['virtual_time_mean_s'])} s & {percent(three['baseline']['cleared_ratio_mean'])} / "
f"{fixed(three['baseline']['virtual_time_mean_s'])} s " + r"\\",
f"4 & 全清率 / 缺席误判总数 & {percent(four['optimal']['cleared_ratio_mean'])} / "
f"{four['optimal']['absent_misjudged_total']} & {percent(four['baseline']['cleared_ratio_mean'])} / "
f"{four['baseline']['absent_misjudged_total']} " + r"\\",
])
write("project-q1-example-rows.tex", [
f"{row['name']} & {fixed(row['diameter_ref_m'])} & {fixed(row['diameter_opt_m'])} & "
f"{fixed(row['diameter_base_m'])} & {fixed(row['abs_err_base_m'])} & "
f"{flag(row['covers_ref'])} & {flag(row['covers_opt'])} & {flag(row['covers_base'])} " + r"\\"
for row in data["q1"]["examples"]
])
write("project-q1-summary-rows.tex", [
f"最优(固定 {examples['cases']} 例) & {percent(examples['status_accuracy_optimal'])} & "
f"{fixed(examples['diameter_mae_optimal_m'])} & {fixed(examples['diameter_max_err_optimal_m'])} & "
f"{percent(examples['coverage_accuracy_optimal'])} & {fixed(examples['runtime_optimal_mean_ms'])} " + r"\\",
f"最优(随机 {random['cases']} 例) & {percent(random['status_accuracy_optimal'])} & "
f"{fixed(random['diameter_mae_optimal_m'])} & {fixed(random['diameter_max_err_optimal_m'])} & "
f"{percent(random['coverage_accuracy_optimal'])} & {fixed(random['runtime_optimal_mean_ms'])} " + r"\\",
f"基线(固定 {examples['cases']} 例) & {percent(examples['status_accuracy_baseline'])} & "
f"{fixed(examples['diameter_mae_baseline_m'])} & {fixed(examples['diameter_max_err_baseline_m'])} & "
f"{percent(examples['coverage_accuracy_baseline'])} & {fixed(examples['runtime_baseline_mean_ms'])} " + r"\\",
f"基线(随机 {random['cases']} 例) & {percent(random['status_accuracy_baseline'])} & "
f"{fixed(random['diameter_mae_baseline_m'])} & {fixed(random['diameter_max_err_baseline_m'])} & "
f"{percent(random['coverage_accuracy_baseline'])} & {fixed(random['runtime_baseline_mean_ms'])} " + r"\\",
])
write("project-q2-rows.tex", [
f"{label} & {percent(two[key]['guaranteed_rate'])} & {fixed(two[key]['worst_posterior_mean_m'])} & "
f"{fixed(two[key]['worst_posterior_median_m'])} & {fixed(two[key]['improvement_mean_m'])} & "
f"{fixed(two[key]['final_ratio_mean'])} & {fixed(two[key]['move_cost_mean_s'], 2)} " + r"\\"
for key, label in (("optimal", "算法2(默认选点)"), ("reference", "参考:盘内网格枚举"),
("baseline_perp", "基线:垂直 1000 m"), ("baseline_mec", "基线:候选圆心"))
])
def variant_rows(table: dict, mapping: list[tuple[str, str]]) -> list[str]:
return [
f"{label} & {table[key]['valid_episodes']}/{table[key]['episodes']} & "
f"{percent(table[key]['cleared_ratio_mean'])} & {table[key]['absent_misjudged_total']} & "
f"{fixed(table[key]['virtual_time_mean_s'])} & {fixed(table[key]['virtual_time_median_s'])} & "
f"{fixed(table[key]['virtual_time_max_s'])} & {fixed(table[key]['measures_mean'], 1)} & "
f"{fixed(table[key]['move_distance_mean_m'] / 1000)} & {fixed(table[key]['fallback_clear_attempts_mean'], 1)} "
+ r"\\"
for key, label in mapping
]
write("project-q3-rows.tex", variant_rows(three, [
("optimal", "bayes(默认策略)"), ("seven", "seven(七点批量)"), ("baseline", "grid(49 点网格)")]))
write("project-q4-rows.tex", variant_rows(four, [
("optimal", "directional(默认策略)"), ("baseline_grid", "grid(问题三网格)"),
("baseline", "bayes(问题三策略)")]))
def listings(rows: list[dict]) -> None: def listings(rows: list[dict]) -> None:
data = PAPER / "data" data = PAPER / "data"
files = sorted((ROOT / "src/mcm26b").rglob("*.py")) files = sorted((ROOT / "src/mcm26b").rglob("*.py"))
@@ -185,6 +346,11 @@ def package(rows: list[dict]) -> Path:
selected.update((PAPER / "data").rglob("*")) selected.update((PAPER / "data").rglob("*"))
selected.update((PAPER / "figures").glob("*.tex")) selected.update((PAPER / "figures").glob("*.tex"))
selected.update((PAPER / "sections").glob("*.tex")) selected.update((PAPER / "sections").glob("*.tex"))
selected.update((ROOT / "docs/figures").glob("*.png"))
selected.update((ROOT / "docs/figures").glob("*.json"))
selected.add(ROOT / "docs/figures/README.md")
selected.update(path for path in (ROOT / "project").rglob("*")
if path.is_file() and "__pycache__" not in path.parts)
selected.update([PAPER / "main.tex", PAPER / "mcm26b-paper.cls", selected.update([PAPER / "main.tex", PAPER / "mcm26b-paper.cls",
PAPER / "README.md", PAPER / "REWRITE_NOTES.md", PAPER / "README.md", PAPER / "REWRITE_NOTES.md",
ROOT / "scripts/audit_paper_formal.py", ROOT / "scripts/audit_paper_formal.py",
@@ -217,6 +383,7 @@ def main() -> None:
args = parser.parse_args() args = parser.parse_args()
rows = json.loads((PAPER / "data/formal-results.json").read_text("utf-8"))["records"] rows = json.loads((PAPER / "data/formal-results.json").read_text("utf-8"))["records"]
figures(rows) figures(rows)
project_tables()
listings(rows) listings(rows)
if args.package: if args.package:
package(rows) package(rows)
+55
View File
@@ -0,0 +1,55 @@
% Generated by scripts/build_paper_support.py from project/results/*.json.
\newcommand{\ProjQOneExampleCases}{5}
\newcommand{\ProjQOneRandomCases}{40}
\newcommand{\ProjQOneFixedStatusBase}{80.0\%}
\newcommand{\ProjQOneFixedMaeBase}{7.886}
\newcommand{\ProjQOneFixedMaeMaxBase}{10.000}
\newcommand{\ProjQOneFixedCoverBase}{33.3\%}
\newcommand{\ProjQOneFixedMsOpt}{0.064}
\newcommand{\ProjQOneFixedMsBase}{14.202}
\newcommand{\ProjQOneRandStatusBase}{100.0\%}
\newcommand{\ProjQOneRandMaeBase}{4.583}
\newcommand{\ProjQOneRandMaeMaxBase}{14.599}
\newcommand{\ProjQOneRandCoverBase}{52.5\%}
\newcommand{\ProjQOneRandMsOpt}{0.086}
\newcommand{\ProjQOneRandMsBase}{15.933}
\newcommand{\ProjQTwoCases}{30}
\newcommand{\ProjQTwoInitialRadius}{681.467}
\newcommand{\ProjQTwoGuaranteeOpt}{100.0\%}
\newcommand{\ProjQTwoGuaranteeMec}{100.0\%}
\newcommand{\ProjQTwoGuaranteePerp}{0.0\%}
\newcommand{\ProjQTwoWorstOpt}{73.890}
\newcommand{\ProjQTwoWorstRef}{75.627}
\newcommand{\ProjQTwoRatioOpt}{0.104}
\newcommand{\ProjQTwoRatioMec}{0.500}
\newcommand{\ProjQTwoWorstPerp}{80.111}
\newcommand{\ProjQTwoWorstMec}{340.747}
\newcommand{\ProjQThreeScenes}{18}
\newcommand{\ProjQThreeMeanOpt}{3188.734}
\newcommand{\ProjQThreeMeanSeven}{5361.707}
\newcommand{\ProjQThreeMeanBase}{14935.659}
\newcommand{\ProjQThreeDropOpt}{78.4\%}
\newcommand{\ProjQThreeDropMedianOpt}{80.0\%}
\newcommand{\ProjQThreeDropSeven}{63.7\%}
\newcommand{\ProjQThreeFasterOpt}{18/18}
\newcommand{\ProjQThreeMeasuresOpt}{111.111}
\newcommand{\ProjQThreeMeasuresBase}{430.556}
\newcommand{\ProjQThreeMoveOpt}{12.338}
\newcommand{\ProjQThreeMoveBase}{61.550}
\newcommand{\ProjQThreeWallOpt}{0.767}
\newcommand{\ProjQFourScenes}{11}
\newcommand{\ProjQFourMeanOpt}{9069.871}
\newcommand{\ProjQFourMeanGrid}{18179.173}
\newcommand{\ProjQFourMeanBase}{3890.801}
\newcommand{\ProjQFourClearOpt}{100.0\%}
\newcommand{\ProjQFourClearGrid}{66.6\%}
\newcommand{\ProjQFourClearBase}{77.0\%}
\newcommand{\ProjQFourAbsentBase}{24}
\newcommand{\ProjQFourMeasuresOpt}{355.636}
\newcommand{\ProjQFourMeasuresBase}{132.000}
\newcommand{\ProjQFourFallbackOpt}{24.818}
\newcommand{\ProjQFourValidOpt}{11/11}
\newcommand{\ProjQFourValidGrid}{0/11}
\newcommand{\ProjQFourRiseOpt}{142.9\%}
\newcommand{\ProjQFourMoveOpt}{34.268}
\newcommand{\ProjQFourMoveBase}{15.319}
@@ -0,0 +1,6 @@
% Generated by scripts/build_paper_support.py from project/results/*.json.
问题 & 核心指标 & 最优实现 & 基线实现 \\
1 & 直径平均绝对误差 / 覆盖判定准确率 & 0.000 m / 100.0\% & 4.583 m / 52.5\% \\
2 & 保证接收率 / 最坏后验半径均值 & 100.0\% / 73.890 m & 100.0\% / 340.747 m \\
3 & 全清率 / 平均虚拟耗时 & 100.0\% / 3188.734 s & 100.0\% / 14935.659 s \\
4 & 全清率 / 缺席误判总数 & 100.0\% / 0 & 77.0\% / 24 \\
@@ -0,0 +1,6 @@
% Generated by scripts/build_paper_support.py from project/results/*.json.
三点交会 & 26.986 & 26.986 & 21.213 & 5.773 & 否 & 否 & 是 \\
直径圆反例 & 40.000 & 40.000 & 50.000 & 10.000 & 否 & 否 & 是 \\
近共线检测点 & -- & -- & -- & -- & -- & -- & -- \\
零误差退化线段 & 100.000 & 100.000 & 0.000 & 100.000 & 是 & 是 & 是 \\
不相容观测 & -- & -- & -- & -- & -- & -- & -- \\
@@ -0,0 +1,5 @@
% Generated by scripts/build_paper_support.py from project/results/*.json.
最优(固定 5 例) & 100.0\% & 0.000 & 0.000 & 100.0\% & 0.064 \\
最优(随机 40 例) & 100.0\% & 0.000 & 0.000 & 100.0\% & 0.086 \\
基线(固定 5 例) & 80.0\% & 7.886 & 10.000 & 33.3\% & 14.202 \\
基线(随机 40 例) & 100.0\% & 4.583 & 14.599 & 52.5\% & 15.933 \\
+5
View File
@@ -0,0 +1,5 @@
% Generated by scripts/build_paper_support.py from project/results/*.json.
算法2(默认选点) & 100.0\% & 73.890 & 90.899 & 607.577 & 0.104 & 170.44 \\
参考:盘内网格枚举 & 100.0\% & 75.627 & 91.349 & 605.841 & 0.106 & 170.28 \\
基线:垂直 1000 m & 0.0\% & 80.111 & 90.775 & 601.356 & 0.116 & 200.00 \\
基线:候选圆心 & 100.0\% & 340.747 & 375.088 & 340.721 & 0.500 & 136.29 \\
+4
View File
@@ -0,0 +1,4 @@
% Generated by scripts/build_paper_support.py from project/results/*.json.
bayes(默认策略) & 18/18 & 100.0\% & 0 & 3188.734 & 3111.256 & 3663.707 & 111.1 & 12.338 & 0.0 \\
seven(七点批量) & 18/18 & 100.0\% & 0 & 5361.707 & 5367.455 & 6462.351 & 100.8 & 23.508 & 0.0 \\
grid(49 点网格) & 18/18 & 100.0\% & 0 & 14935.659 & 15275.057 & 16691.406 & 430.6 & 61.550 & 0.0 \\
+4
View File
@@ -0,0 +1,4 @@
% Generated by scripts/build_paper_support.py from project/results/*.json.
directional(默认策略) & 11/11 & 100.0\% & 0 & 9069.871 & 8803.261 & 12081.148 & 355.6 & 34.268 & 24.8 \\
grid(问题三网格) & 0/11 & 66.6\% & 0 & 18179.173 & 18345.164 & 20457.858 & 573.7 & 73.876 & 0.0 \\
bayes(问题三策略) & 0/11 & 77.0\% & 24 & 3890.801 & 3646.178 & 5054.382 & 132.0 & 15.319 & 0.0 \\
+52 -2
View File
@@ -2,6 +2,21 @@
\documentclass[zihao=-4,a4paper]{mcm26b-paper} \documentclass[zihao=-4,a4paper]{mcm26b-paper}
\usepackage{amsthm,tabularx,float,tikz,xurl} \usepackage{amsthm,tabularx,float,tikz,xurl}
\usetikzlibrary{arrows.meta,positioning,calc,patterns} \usetikzlibrary{arrows.meta,positioning,calc,patterns}
% Raster figures are rendered by scripts/build_figure_data.py + scripts/matlab/*.m
% into docs/figures; this file only includes them, it does not redraw them.
\graphicspath{{../figures/}}
% The paper carries several small tables and figures; let them share pages
% instead of being pushed onto float-only pages.
\setcounter{topnumber}{3}
\setcounter{bottomnumber}{2}
\setcounter{totalnumber}{4}
\renewcommand{\topfraction}{0.92}
\renewcommand{\bottomfraction}{0.75}
\renewcommand{\textfraction}{0.06}
\renewcommand{\floatpagefraction}{0.72}
\setlength{\floatsep}{9pt plus 2pt minus 2pt}
\setlength{\textfloatsep}{11pt plus 2pt minus 4pt}
\setlength{\intextsep}{9pt plus 2pt minus 2pt}
\definecolor{inkblue}{RGB}{35,76,112} \definecolor{inkblue}{RGB}{35,76,112}
\definecolor{warmgray}{RGB}{103,103,103} \definecolor{warmgray}{RGB}{103,103,103}
\newtheorem{proposition}{命题} \newtheorem{proposition}{命题}
@@ -19,6 +34,7 @@
\hypersetup{pdftitle={基于集合估计与覆盖优化的无线电干扰源自动定位清除}, \hypersetup{pdftitle={基于集合估计与覆盖优化的无线电干扰源自动定位清除},
pdfauthor={},pdfsubject={2026年全国大学生数学建模竞赛B题},pdfkeywords={交会定位,最小包围圆,主动测量,覆盖搜索}} pdfauthor={},pdfsubject={2026年全国大学生数学建模竞赛B题},pdfkeywords={交会定位,最小包围圆,主动测量,覆盖搜索}}
\input{data/formal-macros} \input{data/formal-macros}
\input{data/project-macros}
\begin{document} \begin{document}
\setcounter{page}{1} \setcounter{page}{1}
\begin{center} \begin{center}
@@ -57,10 +73,26 @@
三次正式测试分别清除 $13$、$10$、$14$ 个源,平均定位清除时间为 三次正式测试分别清除 $13$、$10$、$14$ 个源,平均定位清除时间为
$720.171$、$927.130$、$614.884$ s/个。 $720.171$、$927.130$、$614.884$ s/个。
<<<<<<< HEAD
实验数据取自官方模拟器 \texttt{test} 目录的六份正式导出及对应统计, 实验数据取自官方模拟器 \texttt{test} 目录的六份正式导出及对应统计,
累计清除 $\FormalClearCount$ 个源,清除失败计数为零。 累计清除 $\FormalClearCount$ 个源,清除失败计数为零。
两问按清除数量加权的平均耗时分别为 $\QThreeWeighted$ 与 $\QFourWeighted$ s/个, 两问按清除数量加权的平均耗时分别为 $\QThreeWeighted$ 与 $\QFourWeighted$ s/个,
移动时间占各次总耗时的 $\FormalMovementMin\%$ 至 $\FormalMovementMax\%$。 移动时间占各次总耗时的 $\FormalMovementMin\%$ 至 $\FormalMovementMax\%$。
=======
上述四次测试均主动退出且清除失败计数为零。
正式成绩仅取自官方模拟器 \texttt{test} 目录所选正式导出包及其匹配统计;
正式数据未公开源总数,故不据此宣称全清率,问题三、四的第三次正式记录均未提供。
\textbf{对比实验}以仓库内的配对基准集复核上述方法:
问题一朴素基线在随机算例上直径平均绝对误差 $\ProjQOneRandMaeBase$ m、
覆盖判定准确率 $\ProjQOneRandCoverBase$,最大误差 $\ProjQOneRandMaeMaxBase$ m;
问题二忽略接收保证的基线保证接收率为 $\ProjQTwoGuaranteePerp$,
只保证接收而缺少横向基线的基线最坏后验半径为 $\ProjQTwoWorstMec$ m;
问题三默认策略相对 49 点网格的平均虚拟时间下降 $\ProjQThreeDropOpt$;
问题四直接搬用问题三策略时出现 $\ProjQFourAbsentBase$ 次缺席误判。
该组数据来自自建冻结基准集与演练局留档,只用于机制说明和相对行为比较,
与正式成绩分节陈述,不合并统计。
>>>>>>> 78e4420a3e496c43db8e06556e539fd7d2aaf231
\vspace{0.4em} \vspace{0.4em}
\noindent{\heiti 关键词:}有界误差;交会定位;最小包围圆;主动测量;定向覆盖 \noindent{\heiti 关键词:}有界误差;交会定位;最小包围圆;主动测量;定向覆盖
@@ -102,6 +134,23 @@ $720.171$、$927.130$、$614.884$ s/个。
报告各次清除数量、平均定位清除时间和程序运行时间。 报告各次清除数量、平均定位清除时间和程序运行时间。
\section{问题分析} \section{问题分析}
四问共用一条从题面抽象到在线执行的链路,如图~\ref{fig:flow}。
最上层是题面给定的区域、运动与动作耗时、源与频道范围;
中间是在线系统的证据层、决策层与执行层,
分别维护候选集与幸存集、生成选点与任务表、串行执行动作;
其下是四问各自的主线,最下层是发现、定位与清除三个闭环。
后文的模型与算法都落在这条链路的某一层上:
问题一、二给出证据层与决策层的几何判据,
问题三、四给出三个闭环在时间约束下的组织方式。
\begin{figure}[htbp]
\centering
\includegraphics[width=0.88\linewidth]{flow-overall-architecture.png}
\caption{四问共用的模型链路:题面抽象(L0)、在线三层(L1)、
四问主线(L2)与发现、定位、清除三个闭环(L3)。}
\label{fig:flow}
\end{figure}
四问构成由单源几何分析到多源在线决策的递进关系。 四问构成由单源几何分析到多源在线决策的递进关系。
问题一的核心是将带误差的射线交会改写为凸集合计算。 问题一的核心是将带误差的射线交会改写为凸集合计算。
单次示向度只限制方向,不能给出唯一位置;即使定位区域的直径较小, 单次示向度只限制方向,不能给出唯一位置;即使定位区域的直径较小,
@@ -186,15 +235,16 @@ N_m,N_s,N_c^+,N_c^- & 检测、换频、成功清除、失败清除次数 & 次\
数学推导中三角函数的自变量采用弧度;带 $^\circ$ 的量先换算为弧度。 数学推导中三角函数的自变量采用弧度;带 $^\circ$ 的量先换算为弧度。
记 $[a,b]=a_xb_y-a_yb_x$ 为二维叉积,$\B(c,r)$ 为闭圆盘。 记 $[a,b]=a_xb_y-a_yb_x$ 为二维叉积,$\B(c,r)$ 为闭圆盘。
\input{sections/timing}
\input{sections/q1} \input{sections/q1}
\input{sections/q2} \input{sections/q2}
\input{sections/q3} \input{sections/q3}
\input{sections/q4} \input{sections/q4}
\input{sections/results} \input{sections/results}
\input{sections/comparison}
\input{sections/evaluation} \input{sections/evaluation}
\clearpage
\begin{thebibliography}{99} \begin{thebibliography}{99}
\small \small\setlength{\itemsep}{1pt}
\bibitem{problem} 全国大学生数学建模竞赛组委会. \bibitem{problem} 全国大学生数学建模竞赛组委会.
2026年高教社杯全国大学生数学建模竞赛B题:无线电干扰源的快速自动定位与清除[Z]. 2026. 2026年高教社杯全国大学生数学建模竞赛B题:无线电干扰源的快速自动定位与清除[Z]. 2026.
\bibitem{protocol} 赛题配套材料. 模拟器使用说明;模拟器通信接口说明及编程指南[Z]. 2026. \bibitem{protocol} 赛题配套材料. 模拟器使用说明;模拟器通信接口说明及编程指南[Z]. 2026.
+23
View File
@@ -16,6 +16,15 @@ python scripts/audit_paper_formal.py --snapshot docs/paper/data/formal-results.j
第二条根据已有快照重新计算指标,并核对现有正式包的校验值。 第二条根据已有快照重新计算指标,并核对现有正式包的校验值。
耗时图与汇总量由同一统计快照生成。 耗时图与汇总量由同一统计快照生成。
第~\ref{sec:comparison} 节的配对对比表由 \pathtext{scripts/build_paper_support.py}
从 \pathtext{project/results/*.json} 生成,复跑对比框架本身执行
\texttt{uv run python -m project.run}。
正文插图由 \pathtext{scripts/build_figure_data.py} 准备数据、
\pathtext{scripts/matlab/run_all_figures.m} 出图,逐图来源与生成命令见
\pathtext{docs/figures/manifest.json}。
其中回放画布与实测计时两张插图取自评测机演练留档,
只用于说明机制与时间构成,不参与正式统计。
\section{支撑材料与运行方法} \section{支撑材料与运行方法}
\begin{table}[H] \begin{table}[H]
\centering\small \centering\small
@@ -24,6 +33,7 @@ python scripts/audit_paper_formal.py --snapshot docs/paper/data/formal-results.j
\toprule \toprule
文件或目录 & 用途\\ 文件或目录 & 用途\\
\midrule \midrule
<<<<<<< HEAD
\pathtext{src/mcm26b/} & 几何计算、集合估计、覆盖优化、任务排序与通信实现\\ \pathtext{src/mcm26b/} & 几何计算、集合估计、覆盖优化、任务排序与通信实现\\
\pathtext{src/mcm26b/guarantee/plans/} & 第四问站点方案与离散验证参数\\ \pathtext{src/mcm26b/guarantee/plans/} & 第四问站点方案与离散验证参数\\
\pathtext{scripts/audit_paper_formal.py} & 正式文件核对、统计计算与表格生成\\ \pathtext{scripts/audit_paper_formal.py} & 正式文件核对、统计计算与表格生成\\
@@ -32,6 +42,19 @@ python scripts/audit_paper_formal.py --snapshot docs/paper/data/formal-results.j
\pathtext{docs/paper/sections/} & 分章论文源文件\\ \pathtext{docs/paper/sections/} & 分章论文源文件\\
\pathtext{docs/paper/figures/} & 几何示意图与正式耗时图的矢量源文件\\ \pathtext{docs/paper/figures/} & 几何示意图与正式耗时图的矢量源文件\\
\pathtext{Jammers-simulator-full/test/} & 六份原名官方正式日志\\ \pathtext{Jammers-simulator-full/test/} & 六份原名官方正式日志\\
=======
\pathtext{src/mcm26b/} & 四问几何、信念、覆盖、调度与串行通信实现\\
\pathtext{src/mcm26b/guarantee/plans/} & 第四问已实现的覆盖计划及验证参数\\
\pathtext{project/} & 最优与基线配对对比框架及其冻结基准集结果\\
\pathtext{scripts/audit_paper_formal.py} & 正式文件对应核对、匿名快照及表格复算\\
\pathtext{scripts/build_paper_support.py} & 对比表、矢量图、源码附录与支撑包生成\\
\pathtext{scripts/build_figure_data.py}、\pathtext{scripts/matlab/} & 插图数据准备与 MATLAB 出图脚本\\
\pathtext{docs/paper/data/} & 仅四个所选正式案例的可读统计、表格及哈希\\
\pathtext{docs/paper/sections/} & 分章论文源文件\\
\pathtext{docs/paper/figures/} & 正式累计耗时图的矢量源文件\\
\pathtext{docs/figures/} & 正文插图(PNG)及其数据清单与生成命令\\
\pathtext{Jammers-simulator-full/test/} & 四份原名官方加密正式日志\\
>>>>>>> 78e4420a3e496c43db8e06556e539fd7d2aaf231
\pathtext{support-manifest.json} & 支撑包文件清单及逐文件 SHA-256\\ \pathtext{support-manifest.json} & 支撑包文件清单及逐文件 SHA-256\\
\bottomrule \bottomrule
\end{tabularx} \end{tabularx}
+173
View File
@@ -0,0 +1,173 @@
\section{算法对比实验:最优实现与基线}\label{sec:comparison}
\subsection{对比口径与有效性判定}
本节数据来自仓库内的配对对比框架 \pathtext{project/},
用于回答上述几何判据与闭环策略相对朴素做法改进在哪里;
它与第~\ref{sec:formal} 节的四份官方正式导出完全分开,两类数字不合并、不相加。
框架设三种角色:\textbf{最优实现}直接复用 \pathtext{src/mcm26b} 中已由测试固化的生产代码,
只增加输入输出适配;\textbf{基线实现}是同题下更朴素但能独立跑通的对照,
不共享最优实现的关键步骤;\textbf{独立参考}与分析算法正交,用于判定谁对谁错。
问题三、四在内存回环评测机上运行,与官方接口使用同一请求与响应边界:
源真值只在评测机一侧,策略端只读响应,因此全清率与缺席误判可以核查。
一局有效必须同时满足全清、无缺席误判、正常 \texttt{/exit}、零异常与逐微秒计时复算一致,
否则记为无效,失败样本不静默丢弃;所有对比按场景配对,并给出均值、中位与最大值。
源位置均匀、误差场逐点固定等分布属于自建测试假设,题面并未规定,
本节结论只描述各方法的相对行为。
\begin{table}[htbp]
\centering\small
\caption{四问最优实现与基线的核心指标总览(自建基准集)}
\begin{tabularx}{\linewidth}{c X l l}
\toprule
问 & 核心指标 & 最优实现 & 基线实现\\
\midrule
\csname @@input\endcsname data/project-overview-rows.tex
\bottomrule
\end{tabularx}
\label{tab:projoverview}
\end{table}
表~\ref{tab:projoverview} 中问题二、四的基线分别取“候选圆心”与
“问题三策略直接搬用”,它们是各自问中最需要区分的对照,
其余各表的配对数字同样如此选择。
本节全部数字由 \pathtext{scripts/build_paper_support.py} 从
\pathtext{project/results/*.json} 生成,正文与表格均不手抄。
\subsection{问题一:直径与覆盖判定}
\begin{table}[htbp]
\centering\small
\caption{问题一固定示例的直径与覆盖判定(单位:m,$\Delta D$ 为相对参考解的误差)}
\setlength{\tabcolsep}{4.5pt}
\begin{tabular}{lrrrrccc}
\toprule
算例 & $D$参考 & $D$最优 & $D$基线 & $|\Delta D|$基线 & 覆盖参考 & 覆盖最优 & 覆盖基线\\
\midrule
\csname @@input\endcsname data/project-q1-example-rows.tex
\bottomrule
\end{tabular}
\label{tab:projq1case}
\end{table}
\begin{table}[htbp]
\centering\small
\caption{问题一的汇总指标(直径误差单位:m,耗时单位:ms)}
\setlength{\tabcolsep}{5pt}
\begin{tabular}{lrrrrr}
\toprule
数据子集/方法 & 状态判定 & 直径平均误差 & 直径最大误差 & 覆盖判定 & 平均耗时\\
\midrule
\csname @@input\endcsname data/project-q1-summary-rows.tex
\bottomrule
\end{tabular}
\label{tab:projq1sum}
\end{table}
表~\ref{tab:projq1case} 显示基线误差的来源是代理方式而非数值精度:
它以轴对齐包围盒的对角线代替区域直径,把边长 $40$ m 的等边三角形判成 $50$ m,
把零误差退化线段判成 $0$ m;覆盖判定以“包围盒中心加代理直径圆”代替逐顶点检查,
于是对任意包围盒几乎恒判覆盖。
在 $\ProjQOneExampleCases$ 个固定示例上,基线的状态判定准确率为 $\ProjQOneFixedStatusBase$,
平均直径误差 $\ProjQOneFixedMaeBase$ m,最大误差 $\ProjQOneFixedMaeMaxBase$ m,
覆盖判定准确率仅 $\ProjQOneFixedCoverBase$;
在 $\ProjQOneRandomCases$ 个随机交会算例上状态判定回到 $\ProjQOneRandStatusBase$,
但平均直径误差仍为 $\ProjQOneRandMaeBase$ m,最大误差 $\ProjQOneRandMaeMaxBase$ m,
覆盖判定准确率 $\ProjQOneRandCoverBase$。
最优实现与独立参考解在全部算例上的状态、直径与覆盖判定一致;
平均耗时为 $\ProjQOneRandMsOpt$ 与 $\ProjQOneRandMsBase$ ms(随机算例),
说明精确几何并未以计算量为代价。
\subsection{问题二:第二检测点选择}
\begin{table}[htbp]
\centering\small
\caption{问题二各方法在 $\ProjQTwoCases$ 个随机首次示向度算例上的汇总
(半径与收缩单位:m,移动时间单位:s)}
\setlength{\tabcolsep}{5pt}
\begin{tabular}{lrrrrrr}
\toprule
方法 & 保证接收率 & 最坏后验均值 & 中位 & 平均收缩 & 终值比 & 移动均值\\
\midrule
\csname @@input\endcsname data/project-q2-rows.tex
\bottomrule
\end{tabular}
\label{tab:projq2}
\end{table}
表~\ref{tab:projq2} 的算例由随机首次示向度生成,
首次候选集的平均包围半径为 $\ProjQTwoInitialRadius$ m。
垂直 $1000$ m 的基线交会角最大,却忽略接收保证,保证接收率为 $\ProjQTwoGuaranteePerp$:
真源位于角域远端时第二点必然无信号,其最坏后验均值 $\ProjQTwoWorstPerp$ m
是在丢失样本上算出的,不能与其它方法直接比较。
候选圆心基线保证接收($\ProjQTwoGuaranteeMec$),但不增加横向基线,
最坏后验半径均值仍为 $\ProjQTwoWorstMec$ m,终值比 $\ProjQTwoRatioMec$,只收缩一半。
默认选点器在保证接收下取得最坏后验均值 $\ProjQTwoWorstOpt$ m、终值比 $\ProjQTwoRatioOpt$,
与保证接收盘内 $9\times9$ 网格枚举的参考解($\ProjQTwoWorstRef$ m)相差约 $2.3\%$,
说明 $37$ 点环形候选已接近该目标函数下的网格最优。
这里的最坏后验是对候选外包多边形的采样评估,不是连续极小极大的全局上界;
“保证接收”一项由安全盘独立成立。
\subsection{问题三:全向闭环}
\begin{table}[htbp]
\centering\footnotesize
\caption{问题三三种方法在 $\ProjQThreeScenes$ 个冻结场景上的汇总
(时间单位:s,位移单位:km)}
\setlength{\tabcolsep}{3.5pt}
\begin{tabular*}{\linewidth}{@{\extracolsep{\fill}}lrrrrrrrrr}
\toprule
方法 & 有效局 & 全清率 & 缺席误判 & $T$均值/s & $T$中位/s & $T$最大/s & 检测 & 位移/km & 后备清除\\
\midrule
\csname @@input\endcsname data/project-q3-rows.tex
\bottomrule
\end{tabular*}
\label{tab:projq3}
\end{table}
三种方法在 $\ProjQThreeScenes$ 个冻结场景(源数 $10$、$13$、$16$,种子与源数版本化)
上全部完成全清,缺席误判均为零,差别只在时间:
默认 \texttt{bayes} 平均 $\ProjQThreeMeanOpt$ s,
七点批量 \texttt{seven} $\ProjQThreeMeanSeven$ s,$49$ 点网格 \texttt{grid} $\ProjQThreeMeanBase$ s。
按场景配对,\texttt{bayes} 相对 \texttt{grid} 的平均虚拟时间下降 $\ProjQThreeDropOpt$、
中位下降 $\ProjQThreeDropMedianOpt$,在 $\ProjQThreeFasterOpt$ 场景上更快,
\texttt{seven} 相对 \texttt{grid} 下降 $\ProjQThreeDropSeven$。
分项上,默认策略检测 $\ProjQThreeMeasuresOpt$ 次、位移 $\ProjQThreeMoveOpt$ km,
网格基线需要 $\ProjQThreeMeasuresBase$ 次检测、$\ProjQThreeMoveBase$ km 位移;
单局在线计算均在 $1.6$ s 以内(默认策略 $\ProjQThreeWallOpt$ s),不构成瓶颈。
七点方案用解析覆盖保证发现,站点最少,但按站点批量扫频需要绕行,位移约为默认策略的两倍。
可见三者的差别不是“能否完成”,而是覆盖证明与信息增益选点各自省下了多少移动与检测。
\subsection{问题四:混合源闭环}
\begin{table}[htbp]
\centering\footnotesize
\caption{问题四三种方法在 $\ProjQFourScenes$ 个场景上的汇总
(时间单位:s,位移单位:km)}
\setlength{\tabcolsep}{3.5pt}
\begin{tabular*}{\linewidth}{@{\extracolsep{\fill}}lrrrrrrrrr}
\toprule
方法 & 有效局 & 全清率 & 缺席误判 & $T$均值/s & $T$中位/s & $T$最大/s & 检测 & 位移/km & 后备清除\\
\midrule
\csname @@input\endcsname data/project-q4-rows.tex
\bottomrule
\end{tabular*}
\label{tab:projq4}
\end{table}
$\ProjQFourScenes$ 个场景取自 core 与 boundary 两组,
boundary 全部是背离圆心发射的边界定向源,属最不利工况。
\texttt{directional} 在 $\ProjQFourValidOpt$ 局上全部有效,全清率 $\ProjQFourClearOpt$,
平均后备清除 $\ProjQFourFallbackOpt$ 次,对应遮挡导致的定位停滞。
把问题三的 \texttt{bayes} 直接搬用时全清率降到 $\ProjQFourClearBase$,
并出现 $\ProjQFourAbsentBase$ 次缺席误判:七站全向缺席证明把朝外发射的定向源判为不存在,
因此它用时最短($\ProjQFourMeanBase$ s),但结论是错的。
\texttt{grid} 的 $49$ 点覆盖对定向源发现完备,全清率 $\ProjQFourClearGrid$、
平均虚拟时间 $\ProjQFourMeanGrid$ s,却没有朝向推理与后备清除,
定位停滞时只能停在“已发现”状态,$\ProjQFourValidGrid$ 局通过有效性判定。
\texttt{directional} 平均 $\ProjQFourMeanOpt$ s,比 \texttt{bayes} 高 $\ProjQFourRiseOpt$,
位移 $\ProjQFourMoveOpt$ km 对 $\ProjQFourMoveBase$ km;由于基线 $11$ 局全部无效,
这一比值不构成“默认策略更慢”的证据,在定向源上应先保证判对、再比较快慢。
\subsection{对比实验的结论与边界}
四组配对比较支持的结论是:问题一的误差来自代理近似,改用精确顶点枚举与最小包围圆后消失;
问题二要求同时满足接收保证与横向基线,只取其一都会被表~\ref{tab:projq2} 的某一列暴露;
问题三的三种方法都能全清,默认策略的收益来自信息增益选点与滚动排序而非覆盖证明本身;
问题四的差别在正确性,搬用全向缺席证明会“快而错”,
只扩大覆盖而不补朝向推理与后备清除则“稳而不全”。
边界有四条。随机分布是自建测试假设,场景数、源数与种子见
\pathtext{project/results/*.json},不能据此推断官方测试的分布。
基线不是稻草人:候选圆心、七点批量与网格覆盖都能独立跑通并在部分场景有效,
它们暴露的是各自缺少的那一步。
问题二的最坏后验与问题四的可见性评分都是启发式评估量,不是全局最优值或校准概率。
本节指标与第~\ref{sec:formal} 节的官方统计口径不同,两组数字不可互相换算。
+7
View File
@@ -59,6 +59,13 @@ $990.051+15/\sqrt2\approx1000.658$ m,超过最小接收半径;
未来可在可重复场景中开展配对试验, 未来可在可重复场景中开展配对试验,
同时保存策略版本、源数量和轨迹等信息,以估计置信区间并比较各模块的贡献。 同时保存策略版本、源数量和轨迹等信息,以估计置信区间并比较各模块的贡献。
\textbf{第六,配对对比依赖自建基准集。}
第~\ref{sec:comparison} 节的最优与基线对比运行在内存回环评测机上,
源位置均匀、误差场逐点固定等分布是测试假设,题面并未规定;
场景数有限,基线在部分场景同样有效。
该节结论说明的是各方法缺少哪一步,而不是普适的性能排序,
也不能与官方正式统计互相换算。
\subsection{参数影响的解析讨论} \subsection{参数影响的解析讨论}
主要参数的影响可由模型公式分析。 主要参数的影响可由模型公式分析。
由 $a(\delta)=1500/(2\cos\delta)$ 得 由 $a(\delta)=1500/(2\cos\delta)$ 得
+28 -1
View File
@@ -95,7 +95,19 @@ $D\le\norm{a-z}+\norm{z-b}\le D$。
等号迫使 $z=o$。反之,圆盘为凸集,包含全部顶点即包含它们的凸包。 等号迫使 $z=o$。反之,圆盘为凸集,包含全部顶点即包含它们的凸包。
\end{proof} \end{proof}
若条件不成立,输出圆外顶点及超出量 若条件不成立,输出圆外顶点及超出量
<<<<<<< HEAD
$e=\max_v\norm{v-o}-D/2$,用于量化该圆盘的覆盖误差。 $e=\max_v\norm{v-o}-D/2$,用于量化该圆盘的覆盖误差。
=======
$e=\max_v\norm{v-o}-D/2$,即得到可独立检查的否定证据。
不能仅以直径端点在圆周上判断整个区域被覆盖。
图~\ref{fig:q1geometry}(a) 给出一组三点交会算例:左图为三条前向角域及其交集,
右图为定位区域的放大,虚线为直径圆、实线为最小包围圆。
该算例的定位区域由四个顶点确定,直径为 $23.997$ m,
以该直径为直径的圆半径为 $11.998$ m,
而最远顶点到圆心的距离为 $12.602$ m,故直径圆并不覆盖定位区域;
其最小包围圆半径为 $12.069$ m,满足 $20$ m 的清除判据。
这说明直径不超过 $40$ m 与能否清除是两个不同的判据,覆盖判定必须逐顶点检查。
>>>>>>> 78e4420a3e496c43db8e06556e539fd7d2aaf231
\subsection{反例与可清除半径} \subsection{反例与可清除半径}
构造等边三角形 构造等边三角形
@@ -106,10 +118,11 @@ A=(0,0),\qquad B=(40,0),\qquad C=(20,20\sqrt3).
但 $C$ 到圆心的距离为 $20\sqrt3$ m,位于圆外。 但 $C$ 到圆心的距离为 $20\sqrt3$ m,位于圆外。
其最小包围圆圆心为 $(20,20/\sqrt3)$,半径 其最小包围圆圆心为 $(20,20/\sqrt3)$,半径
$40/\sqrt3\approx23.094$ m,同样大于清除半径。 $40/\sqrt3\approx23.094$ m,同样大于清除半径。
图~\ref{fig:triangle} 展示两类圆的差别。 图~\ref{fig:q1geometry}(b) 展示两类圆的差别。
\begin{figure}[htbp] \begin{figure}[htbp]
\centering \centering
<<<<<<< HEAD
\begin{tikzpicture}[x=0.095cm,y=0.095cm,font=\small] \begin{tikzpicture}[x=0.095cm,y=0.095cm,font=\small]
\fill[inkblue!10] (0,0)--(40,0)--(20,34.641)--cycle; \fill[inkblue!10] (0,0)--(40,0)--(20,34.641)--cycle;
\draw[thick] (0,0)--(40,0)--(20,34.641)--cycle; \draw[thick] (0,0)--(40,0)--(20,34.641)--cycle;
@@ -128,6 +141,20 @@ $40/\sqrt3\approx23.094$ m,同样大于清除半径。
\end{tikzpicture} \end{tikzpicture}
\caption{等边三角形的直径圆与最小包围圆。} \caption{等边三角形的直径圆与最小包围圆。}
\label{fig:triangle} \label{fig:triangle}
=======
\begin{minipage}[t]{0.49\linewidth}\centering
\includegraphics[width=\linewidth]{geo-q1-convex-intersection-zoom.png}
\end{minipage}\hfill
\begin{minipage}[t]{0.49\linewidth}\centering
\includegraphics[width=\linewidth]{geo-q1-diameter-circle-counterexample.png}
\end{minipage}
\caption{问题一的两组几何。(a) 三点交会算例:定位区域及其直径圆与最小包围圆,
观测取自官方演练案例 \texttt{U5ZW-P9KN-TCAG-R7ST} 的频道 $9$,
几何量由生产求解器 \texttt{mcm26b.geometry.solve} 现算;
(b) 等边三角形解析反例,虚线直径圆 $r=20$ m 不覆盖顶点 $C$,
实线最小包围圆 $\rho=40/\sqrt3\approx23.094$ m 覆盖全部顶点,不是模拟器观测。}
\label{fig:q1geometry}
>>>>>>> 78e4420a3e496c43db8e06556e539fd7d2aaf231
\end{figure} \end{figure}
该形状可由题设的 $\pm1^\circ$ 角域构造出来。例如检测点 该形状可由题设的 $\pm1^\circ$ 角域构造出来。例如检测点
+5 -16
View File
@@ -104,22 +104,11 @@ $1.005^\circ$ & 750.115391 & 249.884609\\
\begin{figure}[htbp] \begin{figure}[htbp]
\centering \centering
\begin{tikzpicture}[x=0.006cm,y=0.006cm,font=\small,>=Stealth] \includegraphics[width=0.70\linewidth]{geo-q2-guaranteed-reception-disk.png}
\fill[inkblue!8] (0,0)--(1499.77,-26.18) arc[start angle=-1,end angle=1,radius=1500]--cycle; \caption{第二点解析候选区域。左图为首次截断角域 $S$、
\draw[inkblue!45] (750.114,0) circle (750.114); 其最小包围圆 $\B(c,a)$ 与保证接收的候选盘 $\B(c,1000-a)$;
\filldraw[fill=inkblue!12,draw=inkblue,thick] (750.114,0) circle (249.885); 右图为半径分解,$a=1500/(2\cos\delta)$。取 $\delta=1^\circ$ 时
\draw[thick] (0,0)--(1499.77,26.18); $a\approx750.114$ m、$1000-a\approx249.886$ m,与表~\ref{tab:safeanalytic} 一致。}
\draw[thick] (0,0)--(1499.77,-26.18);
\draw[->,dashed] (0,0)--(1650,0) node[right] {$u(\theta)$};
\fill (0,0) circle (8) node[below left] {$s$};
\fill[inkblue] (750.114,0) circle (8) node[below] {$c$};
\draw[->,inkblue] (1010,345)--(890,155);
\node[anchor=west,inkblue] at (990,390) {保证接收候选盘};
\node at (750,-850) {圆域先验 $\Omega$ 可进一步收紧候选集};
\node[anchor=east] at (1570,-125) {首次截断角域 $S$};
\node[inkblue!70] at (750,830) {截断角域的最小包围圆};
\end{tikzpicture}
\caption{第二点解析候选区域的几何关系,采用 $1^\circ$ 半角、同一坐标比例绘制。}
\label{fig:q2disk} \label{fig:q2disk}
\end{figure} \end{figure}
+33
View File
@@ -1,5 +1,6 @@
\section{问题三:全向多源的覆盖搜索与滚动调度} \section{问题三:全向多源的覆盖搜索与滚动调度}
\subsection{统一时间目标与任务状态} \subsection{统一时间目标与任务状态}
<<<<<<< HEAD
设整个会话移动距离为 $L$,检测、换频、成功和失败清除次数分别为 设整个会话移动距离为 $L$,检测、换频、成功和失败清除次数分别为
$N_m,N_s,N_c^+,N_c^-$。忽略每段移动时间的微秒量化时,总虚拟耗时为 $N_m,N_s,N_c^+,N_c^-$。忽略每段移动时间的微秒量化时,总虚拟耗时为
\begin{equation} \begin{equation}
@@ -10,6 +11,13 @@ T=\frac{L}{5}+5N_m+N_s+5N_c^++3N_c^-.
题目统计的平均定位清除时间为 $\bar T=T/N_c^+$,包含搜索、定位与清除的全部耗时。 题目统计的平均定位清除时间为 $\bar T=T/N_c^+$,包含搜索、定位与清除的全部耗时。
清除请求中的频道指定操作对象,测向机保持原频道; 清除请求中的频道指定操作对象,测向机保持原频道;
进入与退出的虚拟耗时为零,因此采用终点自由的开放路径模型。 进入与退出的虚拟耗时为零,因此采用终点自由的开放路径模型。
=======
按式~\eqref{eq:time} 的计时记账,优化目标是在全部源清除的约束下最小化虚拟耗时 $T$。
题目统计的平均定位清除时间是 $\bar T=T/N_c^+$;
不能用每次清除动作的固定 $5$ s 代替。
清除请求中的频道是目标频道,不改变测向机频道;
进入与退出均不增加虚拟时间,因此路径终点自由,无需返回原点。
>>>>>>> 78e4420a3e496c43db8e06556e539fd7d2aaf231
各频道的状态按“未知、已发现、可清除、已清除、已证无源”管理。 各频道的状态按“未知、已发现、可清除、已清除、已证无源”管理。
首次获得 $\direction$ 或 $\near$ 即确立该频道存在源。 首次获得 $\direction$ 或 $\near$ 即确立该频道存在源。
@@ -49,6 +57,7 @@ $r^2+a^2-\sqrt3ar$。
\begin{figure}[htbp] \begin{figure}[htbp]
\centering \centering
<<<<<<< HEAD
\begin{tikzpicture}[x=0.0019cm,y=0.0019cm,font=\small] \begin{tikzpicture}[x=0.0019cm,y=0.0019cm,font=\small]
\begin{scope} \begin{scope}
\clip (0,0) circle (1800); \clip (0,0) circle (1800);
@@ -73,6 +82,11 @@ $r^2+a^2-\sqrt3ar$。
{$a=1159.7$ m\\$R=1800$ m\\最坏距离 $\le984.539$ m}; {$a=1159.7$ m\\$R=1800$ m\\最坏距离 $\le984.539$ m};
\end{tikzpicture} \end{tikzpicture}
\caption{七点全向覆盖的站点构造与接收圆盘。} \caption{七点全向覆盖的站点构造与接收圆盘。}
=======
\includegraphics[width=0.42\linewidth]{geo-q3-seven-point-coverage.png}
\caption{七点全向覆盖构造。站点取自配置参数 $a=1159.7$ m,
圆圈为 $1000$ m 接收盘,最差点位于 $(0,-1825)$ 附近,最坏距离 $\le984.539$ m。}
>>>>>>> 78e4420a3e496c43db8e06556e539fd7d2aaf231
\label{fig:seven} \label{fig:seven}
\end{figure} \end{figure}
@@ -195,4 +209,23 @@ C(\pi)=\frac{\norm{p-q_{\pi_1}}}{5}
当前每轮追加有效定位测量上限为 $8$; 当前每轮追加有效定位测量上限为 $8$;
停滞、协议异常或预算耗尽时,记录各频道状态并终止。 停滞、协议异常或预算耗尽时,记录各频道状态并终止。
程序以接口返回的现实预算为准,并预留退出时间, 程序以接口返回的现实预算为准,并预留退出时间,
<<<<<<< HEAD
虚拟动作耗时以整数微秒累计,程序运行时间由官方统计单独记录。 虚拟动作耗时以整数微秒累计,程序运行时间由官方统计单独记录。
=======
实际计时在整数微秒状态上累计,避免用实机运行秒数代替虚拟动作耗时。
\subsection{运行过程示例}
图~\ref{fig:replay3} 回放一局全向演练(案例 \texttt{BBM9-HZG2-77N5-NKKC},$10$ 个源):
左图为移动轨迹、检测示向度与已清除频道,右上方为事件流水,
下方为虚拟时间轴上的检测与清除时刻。
该局说明覆盖扫描、定位测量与清除三类任务的交替关系,
以及为接收向外发射的源而短时越出 $\Omega$ 边界的路径形态;它属于演练留档,不计入正式成绩。
\begin{figure}[htbp]
\centering
\includegraphics[width=0.85\linewidth]{replay-webui-q3-omnidirectional.png}
\caption{全向源演练局(\texttt{BBM9-HZG2-77N5-NKKC})的运行回放。
该局为演练案例,非正式测试记录。}
\label{fig:replay3}
\end{figure}
>>>>>>> 78e4420a3e496c43db8e06556e539fd7d2aaf231
+32 -1
View File
@@ -62,7 +62,14 @@ A_{ij}=\mathbf1\{
验证结果覆盖全部 $3\,256\,200$ 个离散状态, 验证结果覆盖全部 $3\,256\,200$ 个离散状态,
最坏覆盖距离约为 $990.051$ m。 最坏覆盖距离约为 $990.051$ m。
\input{figures/q4-plan} \begin{figure}[htbp]
\centering
\includegraphics[width=0.70\linewidth]{geo-q4-minimum-cover-31-points.png}
\caption{31 点最小命中集的几何分布(左)与定向源的前向半盘语义(右)。
站点取自计划文件,部分站点位于 $\Omega$ 之外,用于接收向外发射的边界源;
离散校验不等同于连续覆盖证明。}
\label{fig:q4plan}
\end{figure}
从离散覆盖推广到连续覆盖时,需同时计入位置与朝向的离散化误差。 从离散覆盖推广到连续覆盖时,需同时计入位置与朝向的离散化误差。
设任意实际位置距某采样位置不超过 $\varepsilon$, 设任意实际位置距某采样位置不超过 $\varepsilon$,
@@ -175,6 +182,30 @@ $N_{\mathrm{cell}}\,t_{\mathrm{RTT},95}+t_{\mathrm{reserve}}$。
\item 覆盖任务结束后处理未解决频道;主动退出时报告清除数及停止依据。 \item 覆盖任务结束后处理未解决频道;主动退出时报告清除数及停止依据。
\end{enumerate} \end{enumerate}
\end{algorithmblock} \end{algorithmblock}
<<<<<<< HEAD
问题四采用站内批量检测与最近邻任务排序。 问题四采用站内批量检测与最近邻任务排序。
算法结合位置集合、朝向约束和剩余预算确定测点及清除顺序, 算法结合位置集合、朝向约束和剩余预算确定测点及清除顺序,
并以各频道的处理状态作为终止时的输出。 并以各频道的处理状态作为终止时的输出。
=======
问题四使用“本站批量扫频、最近候选圆心服务”的调度骨架,
并未直接复用问题三的统一信息增益任务表。
若要求严格的混合源全清证据,除采用连续覆盖计划外,
还须补全类型联合表示或限制联合集排空的适用范围,并确保全部定位清除任务完成。
本文区分当前实现、可证明条件与尚需完善之处,不把后者追溯为正式测试时已执行的功能。
\subsection{运行过程示例}
图~\ref{fig:replay4} 回放一局含定向源的演练(案例 \texttt{GHSN-M38E-74GD-YLUD}):
左图在轨迹、检测示向度与已清除频道之外,画出每个定向源的前向半盘
($\pm90^\circ$,半径取该源的 $r_{\mathrm{eff}}$),箭头方向即朝向。
图中存在落在接收半径之内、却因位于背向半平面而无信号的检测点,
对应式~\eqref{eq:visibility} 的可见性条件,保留这一解释正是问题四与问题三
在候选更新上的区别;该局同样不计入正式成绩。
\begin{figure}[htbp]
\centering
\includegraphics[width=0.85\linewidth]{replay-webui-q4-directional.png}
\caption{含定向源演练局(\texttt{GHSN-M38E-74GD-YLUD})的运行回放,
紫色半盘为定向源的前向可见区域。该局为演练案例,非正式测试记录。}
\label{fig:replay4}
\end{figure}
>>>>>>> 78e4420a3e496c43db8e06556e539fd7d2aaf231
+35
View File
@@ -1,9 +1,21 @@
<<<<<<< HEAD
\section{官方正式测试结果与时间分解} \section{官方正式测试结果与时间分解}
\subsection{实验数据与评价指标} \subsection{实验数据与评价指标}
实验采用官方模拟器 \pathtext{Jammers-simulator-full/test} 目录中的六份正式导出, 实验采用官方模拟器 \pathtext{Jammers-simulator-full/test} 目录中的六份正式导出,
包括问题三、四各三次测试。各文件经 SHA-256 校验后,与官方原始导出、 包括问题三、四各三次测试。各文件经 SHA-256 校验后,与官方原始导出、
结果侧文件及正式统计记录建立对应关系\cite{formal}。 结果侧文件及正式统计记录建立对应关系\cite{formal}。
案例编码与文件校验值列于附录~\ref{app:data}。 案例编码与文件校验值列于附录~\ref{app:data}。
=======
\section{官方正式测试结果与时间分解}\label{sec:formal}
\subsection{数据选择及对应关系核对}
本节实验样本严格限定为官方模拟器
\pathtext{Jammers-simulator-full/test} 下四个正式导出包。
问题三、四各有正式序号 $1$、$2$,文件名及案例编码见结果表和附录。
本节表格不采用 \texttt{project/results} 中的自建评测数据,
也不将历史演练案例当作正式测试补入;
自建配对对比与演练局示例分别在第~\ref{sec:comparison} 节和各问的运行过程示例中单独陈述,
两类数据不合并统计。
>>>>>>> 78e4420a3e496c43db8e06556e539fd7d2aaf231
评价指标包括清除数量 $N_c^+$、平均定位清除时间 $\bar T$、程序运行时间 评价指标包括清除数量 $N_c^+$、平均定位清除时间 $\bar T$、程序运行时间
$T_{\mathrm{real}}$,以及检测、换频和清除的动作次数。 $T_{\mathrm{real}}$,以及检测、换频和清除的动作次数。
@@ -99,11 +111,34 @@ T_{\mathrm{move}}
\end{table} \end{table}
\input{figures/formal-time-bars} \input{figures/formal-time-bars}
<<<<<<< HEAD
六次测试中,移动时间占总虚拟耗时的 $\FormalMovementMin\%$ 至 $\FormalMovementMax\%$, 六次测试中,移动时间占总虚拟耗时的 $\FormalMovementMin\%$ 至 $\FormalMovementMax\%$,
是主要时间成本。问题三的检测次数为 $105$、$117$、$117$, 是主要时间成本。问题三的检测次数为 $105$、$117$、$117$,
问题四为 $349$、$361$、$264$; 问题四为 $349$、$361$、$264$;
混合源案例中,朝向约束增加了观测与位置选择的复杂度。 混合源案例中,朝向约束增加了观测与位置选择的复杂度。
因此,后续优化可同时考虑减少移动距离与提高单次观测的信息增益。 因此,后续优化可同时考虑减少移动距离与提高单次观测的信息增益。
=======
图~\ref{fig:timingmeasured} 按同一记账口径给出一局全向演练的累计时间构成:
横轴为动作序号,堆叠高度为累计虚拟时间,
移动项在整局中占主要部分,清除阶段表现为阶梯状跃升。
该图用于说明式~\eqref{eq:time} 的分项在同一局内如何随时间累积,
其数据来自演练留档,不进入正式统计。
\begin{figure}[htbp]
\centering
\includegraphics[width=0.58\linewidth]{timing-measured-q3-10-sources.png}
\caption{一局全向演练(\texttt{BBM9-HZG2-77N5-NKKC},$10$ 个源)的累计虚拟时间构成。
该局为演练案例,不计入正式测试结果。}
\label{fig:timingmeasured}
\end{figure}
四局移动时间占总虚拟时间的 $76.59\%$ 至 $78.02\%$。
从这一组正式样本看,减少移动是影响总耗时的主要方向,
检测与换频仍应同时计入路径选择。
由 $L_{\mathrm{eq}}=5T_{\mathrm{move}}$ 得四局等效距离分别约为
$12.045$、$13.627$、$36.386$、$35.507$ km。
它们由已量化的移动时间反推,不是独立轨迹测得的长度。
>>>>>>> 78e4420a3e496c43db8e06556e539fd7d2aaf231
由 $L_{\mathrm{eq}}=5T_{\mathrm{move}}$ 得问题三的等效移动距离依次为 由 $L_{\mathrm{eq}}=5T_{\mathrm{move}}$ 得问题三的等效移动距离依次为
$12.045$、$13.627$、$10.824$ km,问题四依次为 $12.045$、$13.627$、$10.824$ km,问题四依次为
+70
View File
@@ -0,0 +1,70 @@
\section{计时模型与会话状态机}
\subsection{会话状态与合法动作}
题目接口只有 \texttt{/enter}、\texttt{/measure}、\texttt{/clear} 与 \texttt{/exit}
四条指令。会话状态依次为未进入、进入倒计时、接口开放与断连,
进入倒计时为 $5$ s,如图~\ref{fig:statemachine}。
只有 \texttt{accepted=true} 的响应推进虚拟时钟;被拒绝的请求返回的虚拟时刻为零,
不能当作当前时刻使用,也不改变位置与测向机频道。
一次合法 \texttt{/measure} 结束后,测向机频道更新为本次检测频道;
\texttt{/clear} 给出的是目标频道,既不改变测向机频道,也不产生换频耗时。
因此清除之后原地再测的耗时取决于上一次 \texttt{/measure} 的频道,而不是清除的频道。
\begin{figure}[htbp]
\centering
\includegraphics[width=0.92\linewidth]{steps-state-machine.png}
\caption{会话状态机。进入后经 $5$ s 倒计时接口开放,主动退出或断连后进入关闭状态。}
\label{fig:statemachine}
\end{figure}
\subsection{动作耗时与累计虚拟时间}
题面将全部动作折算为虚拟耗时,见表~\ref{tab:actioncost}。
检测无论返回无信号、近距离还是示向度,均计 $5$ s;
清除成功计 $5$ s,其中光学定位 $3$ s、激光清除 $2$ s,
未命中目标时只支付光学检查的 $3$ s。
移动与换频没有独立指令,由相邻动作的位置与频道变化推断。
\begin{table}[htbp]
\centering\small
\caption{动作耗时规则。移动耗时等于直线距离除以 $5$ m/s。}
\begin{tabularx}{\linewidth}{l l l X}
\toprule
动作 & 指令 & 虚拟耗时/s & 说明\\
\midrule
进入 & \texttt{/enter} & 0 & 初始位置 $(0,0)$、初始频道 $1$\\
检测 & \texttt{/measure} & $5+$移动$+$换频 & 三种检测结果耗时相同\\
清除 & \texttt{/clear} & $5+$移动/$3+$移动 & 成功/未命中目标,均不切换测向频道\\
退出 & \texttt{/exit} & 0 & 路径终点自由,无需返回原点\\
换频 & 由检测推断 & 1 & 相邻 \texttt{/measure} 的频道变化\\
移动 & 由动作推断 & 距离$/5$ & 相邻动作的位置变化\\
\bottomrule
\end{tabularx}
\label{tab:actioncost}
\end{table}
设整个会话的移动距离为 $L$,检测、换频、成功清除与失败清除的次数分别为
$N_m,N_s,N_c^+,N_c^-$。忽略每段移动时间的微秒量化时,总虚拟耗时为
\begin{equation}
T=\frac{L}{5}+5N_m+N_s+5N_c^++3N_c^-.
\label{eq:time}
\end{equation}
式~\eqref{eq:time} 是后文任务排序的服务时间来源,
也是由官方累计量反推移动时间(式~\eqref{eq:residual})的依据。
图~\ref{fig:timingexample} 用附件的五步示例说明记账方式:
首次检测含移动 $100$ s 与检测 $5$ s,单步 $105$ s;
原地换到频道 $2$ 检测单步 $6$ s,累计 $111$ s;
清除目标频道 $3$ 未命中,移动 $80$ s 加光学检查 $3$ s,单步 $83$ s,累计 $194$ s;
清除不改变测向机频道,故随后原地以频道 $2$ 复测只需 $5$ s,累计 $199$ s,与附件一致。
\begin{figure}[htbp]
\centering
\includegraphics[width=0.55\linewidth]{timing-attachment-example-105-111-194-199.png}
\caption{附件计时示例的逐动作构成。移动项为距离除以 $5$ m/s,标题数字为该步合计。}
\label{fig:timingexample}
\end{figure}
\subsection{虚拟时间、现实预算与终止}
虚拟时间度量任务完成速度,现实预算约束程序能否继续发出请求,两者不能混用。
程序以 \texttt{/enter} 返回的 \texttt{remaining\_real\_duration\_s} 为准,
在每个动作前预留退出时间;虚拟时间以整数微秒累计,在线预测只用于规划与一致性核对。
因此后文的“耗时”一律指式~\eqref{eq:time} 的虚拟时间,程序运行时间在结果表中单列。
策略在预算不足或协议异常时主动退出并报告未解决频道,不以程序正常结束代替任务完成。
+172 -2
View File
@@ -1,7 +1,9 @@
"""Build vector figure sources, complete source listings, and a compact support ZIP. """Build vector figure sources, comparison tables, source listings, and a support ZIP.
Formal figures use the audited snapshot only. Geometric figures use declared Formal figures use the audited snapshot only. Geometric figures use declared
algorithm parameters, not fabricated observations or hidden simulator data. algorithm parameters, not fabricated observations or hidden simulator data.
The paired optimal-versus-baseline tables are generated from project/results/*.json
so that no number in the comparison section is hand copied.
""" """
from __future__ import annotations from __future__ import annotations
@@ -9,11 +11,12 @@ import argparse
import hashlib import hashlib
import json import json
import zipfile import zipfile
from decimal import Decimal from decimal import Decimal, ROUND_HALF_UP
from pathlib import Path from pathlib import Path
ROOT = Path(__file__).resolve().parents[1] ROOT = Path(__file__).resolve().parents[1]
PAPER = ROOT / "docs/paper" PAPER = ROOT / "docs/paper"
PROJECT_RESULTS = ROOT / "project/results"
# Display copies only: executable Python files remain byte-for-byte unchanged. # Display copies only: executable Python files remain byte-for-byte unchanged.
ESCAPE_OPEN = "(" + "*@" ESCAPE_OPEN = "(" + "*@"
@@ -46,6 +49,7 @@ def digest(data: bytes) -> str:
def figures(rows: list[dict]) -> None: def figures(rows: list[dict]) -> None:
out = PAPER / "figures" out = PAPER / "figures"
out.mkdir(parents=True, exist_ok=True) out.mkdir(parents=True, exist_ok=True)
<<<<<<< HEAD
plan = json.loads((ROOT / "src/mcm26b/guarantee/plans/q4_mincover_v2.json").read_text("utf-8")) plan = json.loads((ROOT / "src/mcm26b/guarantee/plans/q4_mincover_v2.json").read_text("utf-8"))
lines = [ lines = [
r"\begin{figure}[htbp]\centering", r"\begin{figure}[htbp]\centering",
@@ -68,6 +72,8 @@ def figures(rows: list[dict]) -> None:
r"\label{fig:q4plan}\end{figure}", r"\label{fig:q4plan}\end{figure}",
] ]
(out / "q4-plan.tex").write_text("\n".join(lines) + "\n", "utf-8") (out / "q4-plan.tex").write_text("\n".join(lines) + "\n", "utf-8")
=======
>>>>>>> 78e4420a3e496c43db8e06556e539fd7d2aaf231
colors = ["inkblue", "inkblue!50", "warmgray!70", "black!18"] colors = ["inkblue", "inkblue!50", "warmgray!70", "black!18"]
bars = [ bars = [
r"\begin{figure}[htbp]\centering", r"\begin{figure}[htbp]\centering",
@@ -101,6 +107,164 @@ def figures(rows: list[dict]) -> None:
(out / "formal-time-bars.tex").write_text("\n".join(bars) + "\n", "utf-8") (out / "formal-time-bars.tex").write_text("\n".join(bars) + "\n", "utf-8")
def fixed(value: float | None, digits: int = 3) -> str:
if value is None:
return "--"
return str(Decimal(str(value)).quantize(Decimal(1).scaleb(-digits), rounding=ROUND_HALF_UP))
def percent(value: float | None, digits: int = 1) -> str:
if value is None:
return "--"
return fixed(100 * value, digits) + r"\%"
def flag(value: bool | None) -> str:
return "--" if value is None else ("是" if value else "否")
def project_tables() -> None:
"""Generate the optimal-versus-baseline comparison tables from project/results."""
out = PAPER / "data"
out.mkdir(parents=True, exist_ok=True)
data = {name: json.loads((PROJECT_RESULTS / f"{name}.json").read_text("utf-8"))
for name in ("q1", "q2", "q3", "q4")}
examples, random = data["q1"]["aggregate_examples"], data["q1"]["aggregate_random"]
two = data["q2"]["aggregate"]
three, paired3 = data["q3"]["aggregate"]["variants"], data["q3"]["aggregate"]["paired"]
four, paired4 = data["q4"]["aggregate"]["variants"], data["q4"]["aggregate"]["paired"]
head = "% Generated by scripts/build_paper_support.py from project/results/*.json."
macros = [
("ProjQOneExampleCases", str(examples["cases"])),
("ProjQOneRandomCases", str(random["cases"])),
("ProjQOneFixedStatusBase", percent(examples["status_accuracy_baseline"])),
("ProjQOneFixedMaeBase", fixed(examples["diameter_mae_baseline_m"])),
("ProjQOneFixedMaeMaxBase", fixed(examples["diameter_max_err_baseline_m"])),
("ProjQOneFixedCoverBase", percent(examples["coverage_accuracy_baseline"])),
("ProjQOneFixedMsOpt", fixed(examples["runtime_optimal_mean_ms"])),
("ProjQOneFixedMsBase", fixed(examples["runtime_baseline_mean_ms"])),
("ProjQOneRandStatusBase", percent(random["status_accuracy_baseline"])),
("ProjQOneRandMaeBase", fixed(random["diameter_mae_baseline_m"])),
("ProjQOneRandMaeMaxBase", fixed(random["diameter_max_err_baseline_m"])),
("ProjQOneRandCoverBase", percent(random["coverage_accuracy_baseline"])),
("ProjQOneRandMsOpt", fixed(random["runtime_optimal_mean_ms"])),
("ProjQOneRandMsBase", fixed(random["runtime_baseline_mean_ms"])),
("ProjQTwoCases", str(two["optimal"]["cases"])),
("ProjQTwoInitialRadius", fixed(two["_initial_radius_mean_m"])),
("ProjQTwoGuaranteeOpt", percent(two["optimal"]["guaranteed_rate"])),
("ProjQTwoGuaranteeMec", percent(two["baseline_mec"]["guaranteed_rate"])),
("ProjQTwoGuaranteePerp", percent(two["baseline_perp"]["guaranteed_rate"])),
("ProjQTwoWorstOpt", fixed(two["optimal"]["worst_posterior_mean_m"])),
("ProjQTwoWorstRef", fixed(two["reference"]["worst_posterior_mean_m"])),
("ProjQTwoRatioOpt", fixed(two["optimal"]["final_ratio_mean"])),
("ProjQTwoRatioMec", fixed(two["baseline_mec"]["final_ratio_mean"])),
("ProjQTwoWorstPerp", fixed(two["baseline_perp"]["worst_posterior_mean_m"])),
("ProjQTwoWorstMec", fixed(two["baseline_mec"]["worst_posterior_mean_m"])),
("ProjQThreeScenes", str(three["optimal"]["episodes"])),
("ProjQThreeMeanOpt", fixed(three["optimal"]["virtual_time_mean_s"])),
("ProjQThreeMeanSeven", fixed(three["seven"]["virtual_time_mean_s"])),
("ProjQThreeMeanBase", fixed(three["baseline"]["virtual_time_mean_s"])),
("ProjQThreeDropOpt", percent(-paired3["optimal_vs_baseline"]["mean_relative_change"])),
("ProjQThreeDropMedianOpt",
percent(-paired3["optimal_vs_baseline"]["median_relative_change"])),
("ProjQThreeDropSeven", percent(-paired3["seven_vs_baseline"]["mean_relative_change"])),
("ProjQThreeFasterOpt",
f"{paired3['optimal_vs_baseline']['faster_scenes']}/{paired3['optimal_vs_baseline']['compared']}"),
("ProjQThreeMeasuresOpt", fixed(three["optimal"]["measures_mean"])),
("ProjQThreeMeasuresBase", fixed(three["baseline"]["measures_mean"])),
("ProjQThreeMoveOpt", fixed(three["optimal"]["move_distance_mean_m"] / 1000)),
("ProjQThreeMoveBase", fixed(three["baseline"]["move_distance_mean_m"] / 1000)),
("ProjQThreeWallOpt", fixed(three["optimal"]["compute_wall_mean_s"])),
("ProjQFourScenes", str(four["optimal"]["episodes"])),
("ProjQFourMeanOpt", fixed(four["optimal"]["virtual_time_mean_s"])),
("ProjQFourMeanGrid", fixed(four["baseline_grid"]["virtual_time_mean_s"])),
("ProjQFourMeanBase", fixed(four["baseline"]["virtual_time_mean_s"])),
("ProjQFourClearOpt", percent(four["optimal"]["cleared_ratio_mean"])),
("ProjQFourClearGrid", percent(four["baseline_grid"]["cleared_ratio_mean"])),
("ProjQFourClearBase", percent(four["baseline"]["cleared_ratio_mean"])),
("ProjQFourAbsentBase", str(four["baseline"]["absent_misjudged_total"])),
("ProjQFourMeasuresOpt", fixed(four["optimal"]["measures_mean"])),
("ProjQFourMeasuresBase", fixed(four["baseline"]["measures_mean"])),
("ProjQFourFallbackOpt", fixed(four["optimal"]["fallback_clear_attempts_mean"])),
("ProjQFourValidOpt",
f"{four['optimal']['valid_episodes']}/{four['optimal']['episodes']}"),
("ProjQFourValidGrid",
f"{four['baseline_grid']['valid_episodes']}/{four['baseline_grid']['episodes']}"),
("ProjQFourRiseOpt", percent(paired4["optimal_vs_baseline"]["mean_relative_change"])),
("ProjQFourMoveOpt", fixed(four["optimal"]["move_distance_mean_m"] / 1000)),
("ProjQFourMoveBase", fixed(four["baseline"]["move_distance_mean_m"] / 1000)),
]
lines = [head] + [f"\\newcommand{{\\{name}}}{{{value}}}" for name, value in macros]
(out / "project-macros.tex").write_text("\n".join(lines) + "\n", "utf-8")
def write(stem: str, rows: list[str]) -> None:
(out / stem).write_text("\n".join([head, *rows]) + "\n", "utf-8")
write("project-overview-rows.tex", [
"问题 & 核心指标 & 最优实现 & 基线实现 " + r"\\",
f"1 & 直径平均绝对误差 / 覆盖判定准确率 & {fixed(random['diameter_mae_optimal_m'])} m / "
f"{percent(random['coverage_accuracy_optimal'])} & {fixed(random['diameter_mae_baseline_m'])} m / "
f"{percent(random['coverage_accuracy_baseline'])} " + r"\\",
f"2 & 保证接收率 / 最坏后验半径均值 & {percent(two['optimal']['guaranteed_rate'])} / "
f"{fixed(two['optimal']['worst_posterior_mean_m'])} m & {percent(two['baseline_mec']['guaranteed_rate'])} / "
f"{fixed(two['baseline_mec']['worst_posterior_mean_m'])} m " + r"\\",
f"3 & 全清率 / 平均虚拟耗时 & {percent(three['optimal']['cleared_ratio_mean'])} / "
f"{fixed(three['optimal']['virtual_time_mean_s'])} s & {percent(three['baseline']['cleared_ratio_mean'])} / "
f"{fixed(three['baseline']['virtual_time_mean_s'])} s " + r"\\",
f"4 & 全清率 / 缺席误判总数 & {percent(four['optimal']['cleared_ratio_mean'])} / "
f"{four['optimal']['absent_misjudged_total']} & {percent(four['baseline']['cleared_ratio_mean'])} / "
f"{four['baseline']['absent_misjudged_total']} " + r"\\",
])
write("project-q1-example-rows.tex", [
f"{row['name']} & {fixed(row['diameter_ref_m'])} & {fixed(row['diameter_opt_m'])} & "
f"{fixed(row['diameter_base_m'])} & {fixed(row['abs_err_base_m'])} & "
f"{flag(row['covers_ref'])} & {flag(row['covers_opt'])} & {flag(row['covers_base'])} " + r"\\"
for row in data["q1"]["examples"]
])
write("project-q1-summary-rows.tex", [
f"最优(固定 {examples['cases']} 例) & {percent(examples['status_accuracy_optimal'])} & "
f"{fixed(examples['diameter_mae_optimal_m'])} & {fixed(examples['diameter_max_err_optimal_m'])} & "
f"{percent(examples['coverage_accuracy_optimal'])} & {fixed(examples['runtime_optimal_mean_ms'])} " + r"\\",
f"最优(随机 {random['cases']} 例) & {percent(random['status_accuracy_optimal'])} & "
f"{fixed(random['diameter_mae_optimal_m'])} & {fixed(random['diameter_max_err_optimal_m'])} & "
f"{percent(random['coverage_accuracy_optimal'])} & {fixed(random['runtime_optimal_mean_ms'])} " + r"\\",
f"基线(固定 {examples['cases']} 例) & {percent(examples['status_accuracy_baseline'])} & "
f"{fixed(examples['diameter_mae_baseline_m'])} & {fixed(examples['diameter_max_err_baseline_m'])} & "
f"{percent(examples['coverage_accuracy_baseline'])} & {fixed(examples['runtime_baseline_mean_ms'])} " + r"\\",
f"基线(随机 {random['cases']} 例) & {percent(random['status_accuracy_baseline'])} & "
f"{fixed(random['diameter_mae_baseline_m'])} & {fixed(random['diameter_max_err_baseline_m'])} & "
f"{percent(random['coverage_accuracy_baseline'])} & {fixed(random['runtime_baseline_mean_ms'])} " + r"\\",
])
write("project-q2-rows.tex", [
f"{label} & {percent(two[key]['guaranteed_rate'])} & {fixed(two[key]['worst_posterior_mean_m'])} & "
f"{fixed(two[key]['worst_posterior_median_m'])} & {fixed(two[key]['improvement_mean_m'])} & "
f"{fixed(two[key]['final_ratio_mean'])} & {fixed(two[key]['move_cost_mean_s'], 2)} " + r"\\"
for key, label in (("optimal", "算法2(默认选点)"), ("reference", "参考:盘内网格枚举"),
("baseline_perp", "基线:垂直 1000 m"), ("baseline_mec", "基线:候选圆心"))
])
def variant_rows(table: dict, mapping: list[tuple[str, str]]) -> list[str]:
return [
f"{label} & {table[key]['valid_episodes']}/{table[key]['episodes']} & "
f"{percent(table[key]['cleared_ratio_mean'])} & {table[key]['absent_misjudged_total']} & "
f"{fixed(table[key]['virtual_time_mean_s'])} & {fixed(table[key]['virtual_time_median_s'])} & "
f"{fixed(table[key]['virtual_time_max_s'])} & {fixed(table[key]['measures_mean'], 1)} & "
f"{fixed(table[key]['move_distance_mean_m'] / 1000)} & {fixed(table[key]['fallback_clear_attempts_mean'], 1)} "
+ r"\\"
for key, label in mapping
]
write("project-q3-rows.tex", variant_rows(three, [
("optimal", "bayes(默认策略)"), ("seven", "seven(七点批量)"), ("baseline", "grid(49 点网格)")]))
write("project-q4-rows.tex", variant_rows(four, [
("optimal", "directional(默认策略)"), ("baseline_grid", "grid(问题三网格)"),
("baseline", "bayes(问题三策略)")]))
def listings(rows: list[dict]) -> None: def listings(rows: list[dict]) -> None:
data = PAPER / "data" data = PAPER / "data"
files = sorted((ROOT / "src/mcm26b").rglob("*.py")) files = sorted((ROOT / "src/mcm26b").rglob("*.py"))
@@ -185,6 +349,11 @@ def package(rows: list[dict]) -> Path:
selected.update((PAPER / "data").rglob("*")) selected.update((PAPER / "data").rglob("*"))
selected.update((PAPER / "figures").glob("*.tex")) selected.update((PAPER / "figures").glob("*.tex"))
selected.update((PAPER / "sections").glob("*.tex")) selected.update((PAPER / "sections").glob("*.tex"))
selected.update((ROOT / "docs/figures").glob("*.png"))
selected.update((ROOT / "docs/figures").glob("*.json"))
selected.add(ROOT / "docs/figures/README.md")
selected.update(path for path in (ROOT / "project").rglob("*")
if path.is_file() and "__pycache__" not in path.parts)
selected.update([PAPER / "main.tex", PAPER / "mcm26b-paper.cls", selected.update([PAPER / "main.tex", PAPER / "mcm26b-paper.cls",
PAPER / "README.md", PAPER / "REWRITE_NOTES.md", PAPER / "README.md", PAPER / "REWRITE_NOTES.md",
ROOT / "scripts/audit_paper_formal.py", ROOT / "scripts/audit_paper_formal.py",
@@ -217,6 +386,7 @@ def main() -> None:
args = parser.parse_args() args = parser.parse_args()
rows = json.loads((PAPER / "data/formal-results.json").read_text("utf-8"))["records"] rows = json.loads((PAPER / "data/formal-results.json").read_text("utf-8"))["records"]
figures(rows) figures(rows)
project_tables()
listings(rows) listings(rows)
if args.package: if args.package:
package(rows) package(rows)