审查并精简 ChatGPT 的建库建议后落地的第一版骨架,只定接口不写实现。 - 建议审查:14 个包 → 7 个;约 250 个函数 → 约 40 个;5 个 Result 类 → 1 个 (docs/01-suggestion-review.md) - docs/:架构、路线图(含永久不做清单)、环境、Git 工作流、函数卡片模板 - src/mcm_toolkit/:core / data / stats / models / solve / decision / viz 共 23 个模块,均为签名 + docstring + NotImplementedError - tests/:9 个文件,其中 test_api_surface.py 已通过(20 passed) - examples/ problems/ notebooks/ figures/ tables/ 骨架 - pyproject.toml(核心依赖 4 个 + 6 组 extras)、environment.yml - scripts/:setup-venv.ps1(ASCII + LF)、setup-conda.ps1、doctor.ps1 - 环境:.venv(system-site-packages)与 conda 环境 mcm-toolkit 均已建好, 数值栈版本与 base 完全对齐(numpy 2.4.6 / scipy 1.18.1 / pandas 3.0.6,MKL 后端)
13 KiB
ChatGPT 建库建议 · 审查与精简
被审查对象:仓库根目录
chatGPT_Suggestion.md(33 节,约 2.4 万字) 审查日期:2026-10-01
一、总裁决
方向对,量级错。
方向对的地方(保留):把比赛的即用代码沉淀成"竞赛层薄封装 + 高频算法自己实现 + 统一结果接口",而不是攒一堆题解。这个定位是全文最有价值的部分,也是这个仓库存在的理由。
量级错的地方(砍):原文给出 14 个子包、约 250 个函数、每个函数强制 10 项文档。这是一个人 6~12 个月的个人项目体量,不是支撑 4 天比赛的体量。按原文执行,最可能的结局是:前两周写完 core 和 data,然后在 optimization/ 的 6 个文件面前失去动力,仓库停在半成品——这比"只有一个 20 个函数的 mcm_toolkit.py"更糟,因为半成品既不能用于比赛,也不好看。
精简后的目标:
| 维度 | ChatGPT 版 | 精简版 |
|---|---|---|
| 子包 | 14 | 7 |
| 函数(v0.1) | ~250 | ~40(其中第一周只做 12 个) |
| Result 类 | 5 | 1 |
| 单函数文档要求 | 10 项,全体强制 | 2 档,只有 ~15 个"招牌算法"做全卡片 |
| 预计完成周期 | —— | 4~6 周出可用 v0.1 |
二、必须保留的(原文的干货)
| # | 原文主张 | 为什么留 |
|---|---|---|
| 1 | 「不要重新造轮子。mcm_toolkit 做竞赛层封装,而不是替代 SciPy/sklearn/NetworkX」 |
全文最重要的一句。它同时是设计原则和防膨胀闸门 |
| 2 | 统一结果对象(result.params / metrics / success) |
真实痛点。比赛时最烦的就是每个函数返回不一样的东西 |
| 3 | 依赖分层:核心 4 个包 + [project.optional-dependencies] |
符合轻量化,且让 CI 和赛场安装都能可控 |
| 4 | 顶层 __init__ 不 import *,只导出子包 + 少量高频快捷函数 |
保留 |
| 5 | 「__all__ 是公开 API 清单,不是导出按钮」 |
保留,这条纪律要从第一版养成 |
| 6 | problems/ 沉淀历年真题、notebooks/ 做探索 |
"每做一题反向沉淀一个通用函数"是这套东西能长大的唯一机制 |
| 7 | save_figure(fig, path, dpi=300, formats=("png","pdf")) |
美赛送稿硬需求,原文提了但严重低估——见第四节 |
| 8 | 参考库定位:SALib / NetworkX / statsmodels / pymoo / SymPy / sklearn | 六条定位全部准确,可直接当"别自己写"的白名单 |
| 9 | A / B / C 三级(自己写 / 自己封装 / 直接调用)的分类框架 | 框架留,边界要下调(见第三节第 10 条) |
三、要砍的
3.1 结构层:14 包 → 7 包
| 原文的包 | 处置 | 理由 |
|---|---|---|
core/ |
留(4 → 3 文件) | 基础设施,必须有 |
data/ |
留(4 文件) | 最高频 |
statistics/ |
留,改名 stats/(5 → 3 文件) |
S 级模块 |
regression/ + ml/ |
合并为 models/ |
原文这两块 fit_linear_regression 与 fit_linear 明显重复,一套 fit/predict/score 协议就够了 |
optimization/ + simulation/ + sensitivity/ |
合并为 solve/ |
这三者是同一件事的三种形态:"把模型反复算"。分开建三个包,共享的 Result、rng、并行约定要在三处重复 |
dynamics/ |
降级为 solve/dynamics.py,进 v0.2 |
原文自己承认"不一定主攻 MCM A"。MCM A 一年一题,且 SymPy+SciPy 封装只需 4 个函数,不值得一个独立包 |
network/ |
降级为 solve/graph.py,进 v0.2 |
只有 ICM D 用;NetworkX 已全部提供。原文列了 5 个子模块 20+ 函数,其中 degree_centrality 之类就是一行转发 |
decision/ |
留但砍半:8 → 4 文件 | AHP / TOPSIS / 熵权 / 灰色是中文赛高频,值得留;dea、fuzzy 见下 |
visualization/ |
改 viz/,13 个 plot_* → 1 个 plot(kind=...) + 3 个专用图 |
见 3.3 |
utils/metrics.py |
取消,指标并入 models/metrics.py |
原文第 26 节又建议单列 analysis/ 放同样的函数,三处重复 |
第 26 节的 analysis/ |
取消 | 与 utils/metrics.py 功能完全重叠 |
docs/api/ docs/tutorials/ |
保留占位,不写内容 | 自动 API 文档用 mkdocs/sphinx 生成,手写必然过期 |
3.2 设计层
| 砍什么 | 理由 | 替代 |
|---|---|---|
| 5 个 Result 类 | 第一版没有任何一个函数需要区分这五种;过早分类会导致大量样板代码 | 1 个 Result:name / params / metrics / data / success / message,后续真需要时再派生 |
| 每个函数 10 项文档(数学定义/适用场景/参数/实现/示例/测试/复杂度/数值稳定性/常见错误/MCM 案例) | 对一个一行转发的封装函数写 10 项文档,写作成本是实现的 20 倍。这是最可能导致项目停滞的一条 | 两档制,见第七节 |
「__init__.py 里再导出少量高频函数」的具体选择 |
原文选的是 solve_ode——但 ODE 已被降级 |
改成 monte_carlo / bootstrap_ci / minimize / parameter_sweep / topsis / compare_models |
3.3 函数层:要删的清单
判断标准只有两条:美赛近 10 年题面出现过吗? / 成熟库是不是一行就够? 两条都不满足就删。
| 删除项 | 理由 | 替代 |
|---|---|---|
maximize() |
就是 minimize 取负 |
minimize 加 sense="max" 参数 |
kendall_correlation / partial_correlation |
美赛几乎不用;df.corr(method="kendall") 一行 |
pandas |
modified_zscore_outliers / zscore_outliers |
IQR 和 z-score 两种覆盖 95% 场景 | 只留 detect_outliers(method="iqr"|"zscore") |
distribution_pdf/cdf/ppf 三个独立函数 |
纯转发 scipy | fit_distribution 返回的对象上挂 .pdf/.cdf/.ppf |
sample_without_replacement / sample_with_replacement |
rng.choice(..., replace=) 一行 |
numpy |
brownian_motion / geometric_brownian_motion |
niche,只有金融题偶尔用 | 用到再写;random_walk 已覆盖 |
mmc_metrics / simulate_queue |
排队论在美赛出现频率极低,且 simulate_queue 是个完整的离散事件仿真引擎,成本极高 |
只留 mm1_metrics(闭式解,10 行) |
crowding_distance / hypervolume |
NSGA-II 内部件,属于原文自己划的 C 类 | pymoo |
dea_efficiency |
低频;且要解一堆 LP,实现成本不低 | 真要用,现场用 scipy.optimize.linprog 拼 |
fuzzy_comprehensive_evaluation |
低频,且"隶属度函数"没有通用形式,做不了通用封装 | 删 |
value_iteration + policy_iteration + dynamic_programming 三个 |
三者是同一族的三种写法,第一版同时做属于过度设计 | 只留 dynamic_programming(通用)+ knapsack_dp(模板) |
weighted_moving_average / holt_trend / seasonal_decompose |
statsmodels 一行。原文自己也在第 7 节说"做薄封装" | 只留 exponential_smoothing 一个包装 |
grid_sweep |
parameter_sweep 的特例 |
合入 parameter_sweep |
13 个 plot_* |
每个都是 ax 上加两行;拆成 13 个函数后,改一次全局样式要改 13 处 |
plot(x, y, kind="line"|"scatter"|"hist"|"box"|"heatmap") + plot_correlation_matrix / plot_residuals / plot_convergence 三个有真实逻辑的 |
normalize 与 minmax_scale 并存 |
是同一个东西的两个名字 | 只留 normalize |
standardize 与 zscore 并存(一个在 data、一个在 stats) |
同上 | 只留 data.standardize |
higher-level:sample_distribution、distribution_pdf 系列 |
转发 | 删 |
净效果:约 250 → 约 40 个函数。
3.4 下调 A/B/C 的边界
原文的 A 类("强烈建议自己实现")里有 dynamic programming、genetic algorithm、sensitivity analysis 等。用于学习时这个清单没问题;但比赛现场要反过来:
- 比赛时:能用库就用库(SALib 的
sobol_analyze一定比自己写的对)。 - 平时:自己实现,目的是"读得懂、改得动、能解释给队友"。
- 所以自己实现的代码要单独标
_from_scratch或放在mcm_toolkit/algo/,默认走库版本。
原文把这两个场景混在一起了。
四、ChatGPT 漏掉的(真正决定比赛成败的)
原文 33 节几乎全是"代码结构",但美赛是论文比赛,不是代码比赛。以下六条原文完全没提或严重低估:
-
论文产出链路(最严重的缺失) 工具箱的最终消费者是论文。原文只给了一个
save_figure。至少要补:save_table(df, path, fmt=("csv","md","latex"))—— 结果表直接出 LaTeX,粘贴进论文- 全局图样式
mcm_style():字号 ≥ 9pt、线宽 ≥ 1.5、dpi=300、色盲友好调色板、双栏宽度 - 图注(
fig.caption)与"图号—正文引用"的约定
-
量纲与方向
normalize没区分"无量纲化"和"标准化",更没提效益型 / 成本型指标方向。评价类题目(TOPSIS/熵权)翻车基本都翻在这里。 -
时间预算 4 天比赛,函数的价值 = 正确性 ÷ 上手时间。原文没有一条关于"够快"的约定:MC 必须向量化、
n_resamples默认值要能在一分钟内跑完、不要引入编译开销。 -
可复现性 提了
get_rng,但没提"提交要能复现"。约定:所有带随机的公开函数第一个参数是random_state,论文附录必须写 seed。 -
测试测什么 说了 pytest,没说测什么。应测
已知解析解:MC 积分 vs 解析积分、正态 CI vs 公式值、bootstrap_ci大样本下收敛到 CLT 值。不要测实现细节。 -
离线约束 原文的 extras 设计假设比赛机器能联网。现场装不上包是真实风险,必须有
environment.yml锁版本 + 一份"最小可用依赖"清单。
另外两条工程问题:
- 原文第 32 节把
problems/notebooks/和src/混在一个仓库——这是对的,但要注意problems/会带来大量数据文件,.gitignore要提前写好(见仓库根目录的.gitignore)。 - 没有"不做"清单。原文用"建议不要",但真正能防膨胀的是写死的"本仓库永远不实现 X"。已补进
docs/03-roadmap.md。
五、精简后的结构
MCM&ICM_Toolkit/
├── src/mcm_toolkit/
│ ├── core/ 基础设施:Result / rng / 校验 ← 第一个做
│ ├── data/ 读、洗、变换、采样 ← 最高频
│ ├── stats/ 描述、推断、bootstrap ← S 级
│ ├── models/ 回归 + 时序 + ML 薄封装(统一 fit/predict/score)
│ ├── solve/ 优化、模拟、敏感性、图、动力学 ← 竞赛主力
│ ├── decision/ 熵权 / AHP / TOPSIS / 灰色
│ └── viz/ 统一绘图 + 图表导出
├── tests/ tests/test_*.py(按包一一对应)
├── examples/ 每个模块一个能跑的最小例子
├── problems/ 历年真题(2025_C/ ...)
├── notebooks/ 探索用
├── docs/ 本目录
├── scripts/ setup-conda.ps1 / setup-venv.ps1
├── pyproject.toml
├── environment.yml
└── STATUS.md 进度
包结构详情与每个包的边界见 02-architecture.md。
六、功能清单分级
见 03-roadmap.md。摘要:
- L0(第一周,12 个):
Resultget_rngload_tableclean_missingdetect_outliersnormalizestandardizedescribecorrelation_matrixbootstrap_cimonte_carlosave_figure - L1(v0.1,共约 40 个):L0 + 回归/时序/优化/敏感性/评价/绘图
- L2(v0.2):
dynamics、graph - 永久不做:见
03-roadmap.md末节
七、开发纪律:文档从 10 项降到 2 档
原文要求每个函数都写 10 项。改成两档:
S 档(招牌算法,约 15 个) —— 用 docs/dev/function-card-template.md 的完整卡片:
数学定义 / 适用场景 / 参数 / 复杂度 / 数值稳定性 / 常见错误 / 最小示例 / 单测 / 赛题用法。
覆盖:bootstrap_ci monte_carlo simulated_annealing dynamic_programming parameter_sweep sobol_analyze topsis entropy_weights ahp_weights gm11 detect_outliers local_sensitivity 等。
A 档(其余,薄封装) —— 只要求:
- docstring 里一句话数学含义 + 一句话什么时候用;
- 一条
pytest(对齐库结果或已知值)。
不单独写文档。
判断标准:我会不会去改它的内部实现? 会 → S 档;只当转发器用 → A 档。
八、执行顺序(时间盒)
| 周 | 目标 | 交付 |
|---|---|---|
| 1 | core + data + stats 骨架;S 档先写 bootstrap_ci monte_carlo normalize detect_outliers |
L0 全部可跑通 |
| 2–3 | models + solve/optimize + solve/simulate |
compare_models 能跑 |
| 4 | solve/sensitivity + decision + viz |
一套端到端 example |
| 5–6 | 拿一道真题(如 2025 C)从头走一遍,反向补函数 | problems/2025_C/ 完成 |
| 之后 | 每做一题 → 沉淀 1~3 个函数 + 对应 test | 仓库自然长大 |
这个顺序的关键是"先能跑通一条完整链路,再补宽度",与原文"先把 14 个包建齐"相反。