yes fuking shit
This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
"""第四问覆盖证书与最小命中集搜索(CLI;核心实现见 mcm26b.guarantee.cover)。
|
||||
|
||||
第四问的定向源在"读点位于源的前向半平面内"时才可能被检出,因此
|
||||
"某频道在所有被访点都 no_signal ⟹ 该频道不存在源"的缺席证明,等价于要求
|
||||
被访点集合 S 满足
|
||||
|
||||
∀ p ∈ Ω, ∀ h ∈ [0,360): ∃ c ∈ S,
|
||||
|c − p| ≤ r_eff_min(1000) 且 (c − p)·u(h) ≥ 0
|
||||
|
||||
(题面规定朝向覆盖"两侧各 90 度,含边界",故取 ≥ 0)。现役 49 点方格的
|
||||
"单元对角 ≤ 1000"是**充分**条件但远非必要;本脚本用贪心击中集求等价覆盖的
|
||||
更小点集,并输出可复现证书。
|
||||
|
||||
uv run python scripts/verify_q4_cover.py # 校核 + 搜索 + 打印
|
||||
uv run python scripts/verify_q4_cover.py --spacing 200 --write-plan q4_mincover_v1
|
||||
uv run python scripts/verify_q4_cover.py --check-json my_points.json
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from mcm26b.config import ProblemConfig
|
||||
from mcm26b.guarantee import (CERTIFY_HEADINGS, CERTIFY_STEP_M, R_EFF_MIN_M,
|
||||
CoverageCertificate, CoveragePlan,
|
||||
candidate_lattice, coverage_masks, forward_targets,
|
||||
greedy_cover, omega_positions, plan_names,
|
||||
square_49_plan, verify_full_disk,
|
||||
verify_forward_half_disk, verify_plan, write_plan)
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
DEFAULT_PLAN_DIR = ROOT / "src/mcm26b/guarantee/plans"
|
||||
|
||||
|
||||
def _report(points, kind: str, step_m: float, headings: int,
|
||||
r_eff: float = R_EFF_MIN_M,
|
||||
heading_margin_deg: float = 0.0) -> dict:
|
||||
"""按覆盖语义校核点集,返回证书字典。"""
|
||||
if kind == "forward_half_disk":
|
||||
certificate = verify_forward_half_disk(points, step_m, headings,
|
||||
r_eff=r_eff,
|
||||
heading_margin_deg=heading_margin_deg)
|
||||
else:
|
||||
certificate = verify_full_disk(points, step_m, r_eff)
|
||||
return certificate.as_dict()
|
||||
|
||||
|
||||
def prune_redundant(masks: list[int], chosen: list[int]) -> list[int]:
|
||||
"""贪心结果冗余剪枝:删掉任何"其余点已完全覆盖其目标"的点。"""
|
||||
full = 0
|
||||
for index in chosen:
|
||||
full |= masks[index]
|
||||
pruned = list(chosen)
|
||||
changed = True
|
||||
while changed:
|
||||
changed = False
|
||||
for index in list(pruned):
|
||||
rest = 0
|
||||
for other in pruned:
|
||||
if other != index:
|
||||
rest |= masks[other]
|
||||
if rest == full:
|
||||
pruned.remove(index)
|
||||
changed = True
|
||||
return pruned
|
||||
|
||||
|
||||
def fine_prune(points, step_m: float, headings: int, r_eff: float,
|
||||
heading_margin_deg: float):
|
||||
"""证书分辨率上的冗余剪枝:删掉任何点后校核仍 0 缺口才真删。
|
||||
|
||||
粗网格(搜索用)上的冗余判据偏保守,会在证书分辨率下留下可删的点;
|
||||
本步每个候选删除都用**证书同一口径**复核,因此不削弱证书。
|
||||
"""
|
||||
points = list(points)
|
||||
improved = True
|
||||
while improved:
|
||||
improved = False
|
||||
for index in range(len(points)):
|
||||
trial = points[:index] + points[index + 1:]
|
||||
certificate = verify_forward_half_disk(
|
||||
trial, step_m, headings, r_eff=r_eff,
|
||||
heading_margin_deg=heading_margin_deg)
|
||||
if certificate.uncovered == 0:
|
||||
points = trial
|
||||
improved = True
|
||||
print(f" 精细剪枝:删点后仍 0 缺口,剩 {len(points)} 点", flush=True)
|
||||
break
|
||||
return points
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
||||
parser.add_argument("--spacing", type=float, default=200.0,
|
||||
help="贪心候选点网格间距(米)")
|
||||
parser.add_argument("--radius", type=float, default=2850.0,
|
||||
help="候选点所在圆盘半径(米)")
|
||||
parser.add_argument("--target-step", type=float, default=CERTIFY_STEP_M,
|
||||
help="搜索用采样位置步长(米)")
|
||||
parser.add_argument("--headings", type=int, default=CERTIFY_HEADINGS,
|
||||
help="搜索用采样朝向档数")
|
||||
parser.add_argument("--verify-step", type=float, default=None,
|
||||
help="证书校核位置步长(默认等于 --target-step)")
|
||||
parser.add_argument("--verify-headings", type=int, default=None,
|
||||
help="证书校核朝向档数(默认等于 --headings)")
|
||||
parser.add_argument("--design-radius", type=float, default=None,
|
||||
help="搜索时的设计距离(默认 1000;<1000 即留裕量)")
|
||||
parser.add_argument("--heading-margin", type=float, default=0.0,
|
||||
help="搜索时的前向角裕量(度)")
|
||||
parser.add_argument("--verify-radius", type=float, default=None,
|
||||
help="证书校核距离(默认 1000;<1000 即留裕量)")
|
||||
parser.add_argument("--verify-heading-margin", type=float, default=0.0,
|
||||
help="证书校核前向角裕量(度);应 ≥ 180/verify_headings/2")
|
||||
parser.add_argument("--no-prune", action="store_true", help="关闭冗余剪枝")
|
||||
parser.add_argument("--fine-prune", action="store_true",
|
||||
help="在证书分辨率上再做一轮冗余剪枝(删点后仍 0 缺口才删)")
|
||||
parser.add_argument("--coverage", choices=("forward_half_disk", "full_disk"),
|
||||
default="forward_half_disk", help="覆盖语义")
|
||||
parser.add_argument("--check-json", type=Path, default=None,
|
||||
help="只校核给定点集([[x,y],...] 或 {\"points\":[...]})")
|
||||
parser.add_argument("--write-plan", default=None,
|
||||
help="把贪心最小集写成覆盖计划(名称,落 src/mcm26b/guarantee/plans/)")
|
||||
parser.add_argument("--out", type=Path, default=None, help="证书 JSON 输出路径")
|
||||
args = parser.parse_args()
|
||||
|
||||
verify_step = (args.verify_step if args.verify_step is not None
|
||||
else args.target_step)
|
||||
verify_headings = (args.verify_headings if args.verify_headings is not None
|
||||
else args.headings)
|
||||
design_radius = (args.design_radius if args.design_radius is not None
|
||||
else R_EFF_MIN_M)
|
||||
verify_radius = (args.verify_radius if args.verify_radius is not None
|
||||
else R_EFF_MIN_M)
|
||||
|
||||
if args.check_json is not None:
|
||||
payload = json.loads(args.check_json.read_text(encoding="utf-8"))
|
||||
raw = payload.get("points") if isinstance(payload, dict) else payload
|
||||
points = [tuple(map(float, p)) for p in raw]
|
||||
report = _report(points, args.coverage, verify_step, verify_headings,
|
||||
verify_radius, args.verify_heading_margin)
|
||||
print(json.dumps(report, ensure_ascii=False, indent=2))
|
||||
return 0 if report["uncovered"] == 0 else 1
|
||||
|
||||
shipped = square_49_plan()
|
||||
shipped_report = _report(shipped.points, "forward_half_disk",
|
||||
verify_step, verify_headings,
|
||||
verify_radius, args.verify_heading_margin)
|
||||
print(f"目标空间:{shipped_report['targets']} 个 (p,h)"
|
||||
f"(校核位置步长 {verify_step} m,朝向 {verify_headings} 档,"
|
||||
f"r_verify={verify_radius:.0f} m,角裕量 "
|
||||
f"{args.verify_heading_margin}°)")
|
||||
print(f"现役 {shipped.name}:{len(shipped.points)} 点,"
|
||||
f"缺口 {shipped_report['uncovered']},"
|
||||
f"NN 巡回 {shipped_report['nn_tour_m'] / 1000:.1f} km"
|
||||
f"({shipped_report['nn_tour_virtual_s']:.0f} s 移动)", flush=True)
|
||||
|
||||
targets = forward_targets(args.target_step, args.headings)
|
||||
candidates = candidate_lattice(args.spacing, args.radius)
|
||||
print(f"候选点 {len(candidates)}({args.spacing} m 网格,半径 {args.radius} m);"
|
||||
f"设计裕量 r_design={design_radius:.0f} m,前向角裕量 "
|
||||
f"{args.heading_margin}°", flush=True)
|
||||
masks = coverage_masks(targets, candidates, r_eff=design_radius,
|
||||
heading_margin_deg=args.heading_margin)
|
||||
chosen = greedy_cover(masks, len(targets))
|
||||
if not args.no_prune:
|
||||
chosen = prune_redundant(masks, chosen)
|
||||
best_points = [candidates[i] for i in chosen]
|
||||
if args.fine_prune:
|
||||
best_points = fine_prune(best_points, verify_step, verify_headings,
|
||||
verify_radius, args.verify_heading_margin)
|
||||
best_report = _report(best_points, "forward_half_disk",
|
||||
verify_step, verify_headings,
|
||||
verify_radius, args.verify_heading_margin)
|
||||
print(f"设计裕量最小命中集:{len(best_points)} 点,缺口 {best_report['uncovered']},"
|
||||
f"NN 巡回 {best_report['nn_tour_m'] / 1000:.1f} km"
|
||||
f"({best_report['nn_tour_virtual_s']:.0f} s 移动)", flush=True)
|
||||
|
||||
if best_report["uncovered"] != 0:
|
||||
print("贪心结果未通过证书校核,拒绝生成计划。")
|
||||
return 1
|
||||
|
||||
generator = {"script": "scripts/verify_q4_cover.py",
|
||||
"spacing_m": args.spacing, "radius_m": args.radius,
|
||||
"target_step_m": args.target_step, "headings": args.headings,
|
||||
"design_r_eff_m": design_radius,
|
||||
"heading_margin_deg": args.heading_margin,
|
||||
"pruned": not args.no_prune,
|
||||
"verify_step_m": verify_step,
|
||||
"verify_headings": verify_headings,
|
||||
"verify_r_eff_m": verify_radius,
|
||||
"verify_heading_margin_deg": args.verify_heading_margin,
|
||||
"code_sha256": hashlib.sha256(
|
||||
Path(__file__).read_bytes()).hexdigest()}
|
||||
certificate = CoverageCertificate(
|
||||
kind=best_report["kind"], target_step_m=verify_step,
|
||||
headings=verify_headings, r_eff_min_m=verify_radius,
|
||||
targets=best_report["targets"], uncovered=best_report["uncovered"],
|
||||
worst_hit_distance_m=best_report["worst_hit_distance_m"],
|
||||
nn_tour_m=best_report["nn_tour_m"],
|
||||
generator_sha256=generator["code_sha256"],
|
||||
verify_heading_margin_deg=args.verify_heading_margin)
|
||||
plan = CoveragePlan(
|
||||
name=args.write_plan or "q4_mincover_draft", problem=4,
|
||||
kind="certified", coverage="forward_half_disk",
|
||||
note=(f"设计裕量最小命中集:{len(best_points)} 点,"
|
||||
f"r_design={design_radius:.0f} m / 前向角裕量 {args.heading_margin}°;"
|
||||
f"证书按 {verify_step:.0f} m/{verify_headings} 档、r_verify="
|
||||
f"{verify_radius:.0f} m、角裕量 {args.verify_heading_margin}° "
|
||||
f"校核缺口 0(同一证书口径复核,含精细剪枝);最坏命中 "
|
||||
f"{best_report['worst_hit_distance_m']:.0f} m,"
|
||||
f"NN 巡回 {best_report['nn_tour_m'] / 1000:.1f} km"),
|
||||
generator=generator, certificate=certificate,
|
||||
points=tuple(best_points))
|
||||
payload = {"model": {"r_omega_m": 1800.0, "r_eff_min_m": 1000.0},
|
||||
"design": {"r_design_m": design_radius,
|
||||
"heading_margin_deg": args.heading_margin},
|
||||
"verify": {"r_eff_m": verify_radius,
|
||||
"heading_margin_deg": args.verify_heading_margin,
|
||||
"step_m": verify_step, "headings": verify_headings},
|
||||
"shipped": shipped_report, "mincover": best_report}
|
||||
if args.out is not None:
|
||||
out = args.out if args.out.is_absolute() else ROOT / args.out
|
||||
out.parent.mkdir(parents=True, exist_ok=True)
|
||||
out.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n",
|
||||
encoding="utf-8")
|
||||
print(f"证书已写入 {out}")
|
||||
if args.write_plan:
|
||||
target = DEFAULT_PLAN_DIR / f"{args.write_plan}.json"
|
||||
write_plan(plan, target)
|
||||
verified = verify_plan(plan)
|
||||
print(f"计划已写入 {target};运行期分辨率复核缺口 {verified.uncovered}")
|
||||
print("已注册计划:" + ",".join(plan_names()))
|
||||
_ = (ProblemConfig(), omega_positions) # 保持公开入口可发现性
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user