Files
mcm-26-b/project/q1/baseline.py
2026-09-12 16:58:25 +08:00

99 lines
3.4 KiB
Python

"""问题一基线实现:网格采样 + 轴对齐包围盒(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,
}