美赛各题型常见参考代码汇总:从微分方程到TOPSIS的快速实现

发布时间:2026/10/12 16:01:54
美赛各题型常见参考代码汇总:从微分方程到TOPSIS的快速实现 简介这份资源面向参加美国大学生数学建模竞赛及国内数学建模赛事的选手尤其适合需要快速调用成熟算法代码、补齐编程实现短板的中高年级本科生与研究生。包内按题型整理了从线性回归、时间序列到遗传算法改进神经网络等常见模型的参考实现覆盖数据分析、智能算法与神经网络等方向可作为赛前练手与赛中查阅的代码库。压缩包共约2000个文件以m脚本、mat数据、txt说明、fig图形、pdf文档为主另含少量c、cpp、exe及多种平台mex文件整体约109.72MB目录按算法与赛题模块划分便于按需检索。目前已有4062人学习下载。读者可从中获得各题型可运行的代码模板、数据与结果图示例、算法改进思路及调试参考帮助在有限时间内搭建解题框架、验证模型效果并积累可复用的实现经验。1. 美赛各题型常见参考代码汇总一份能让你少熬两个通宵的代码地图每年美赛开赛那几天总有人在群里问同一个问题这题到底该用哪套代码我见过太多队伍前两天全耗在“找代码、调环境、改参数”上真正建模的时间被压到不足 24 小时。美赛各题型常见参考代码汇总.zip 这类资源之所以被反复搜索本质上是大家想要一张“题型到代码”的映射表——看到题目能立刻判断它属于连续型、离散型、数据型还是评价型然后直接调出对应的求解框架而不是从零搭轮子。这份汇总的价值不在于代码本身多高级而在于它把六类题型的常用解法、输入输出格式、参数默认值都固定下来了。适合谁适合已经学过 Python 或 MATLAB 基础、但没打过完整建模比赛的新手队伍也适合打过一次但每次都在“选模型”环节卡住的熟手。下面我按题型拆开讲每个题型给一套能直接跑的最小代码再补上参数怎么改、结果怎么验、哪里最容易翻车。2. 连续型与离散型从微分方程到整数规划的代码骨架2.1 连续型题型微分方程数值解的固定套路连续型题目在美赛里出现频率最高典型特征是“随时间变化的量”“增长率”“衰减”“传播”。常见做法是先把文字描述转成微分方程再用数值方法求解。我一般用scipy.integrate.solve_ivp因为它对刚性方程和非刚性方程都能处理参数也好调。import numpy as np from scipy.integrate import solve_ivp import matplotlib.pyplot as plt # 定义微分方程组这里以经典的 SIR 传播模型为例 # y[0]S 易感者, y[1]I 感染者, y[2]R 移除者 def sir_model(t, y, beta, gamma): S, I, R y dS -beta * S * I dI beta * S * I - gamma * I dR gamma * I return [dS, dI, dR] # 参数设置beta 是感染率gamma 是恢复率 beta 0.3 gamma 0.1 y0 [0.99, 0.01, 0.0] # 初始比例总和为 1 t_span (0, 100) # 时间区间 t_eval np.linspace(0, 100, 500) # 求解RK45 是默认方法适合非刚性BDF 适合刚性 sol solve_ivp(sir_model, t_span, y0, args(beta, gamma), methodRK45, t_evalt_eval, rtol1e-6, atol1e-9) # 画图验证 plt.plot(sol.t, sol.y[0], labelS) plt.plot(sol.t, sol.y[1], labelI) plt.plot(sol.t, sol.y[2], labelR) plt.legend() plt.xlabel(Time) plt.ylabel(Proportion) plt.show()逻辑说明solve_ivp的第一个参数是函数名函数签名必须是f(t, y, *args)args用来传额外参数避免全局变量。method选RK45时适合大多数非刚性方程如果求解报错“Required step size is less than spacing between numbers”说明方程刚性太强换成BDF或LSODA。rtol和atol控制相对和绝对误差默认 1e-3 和 1e-6做灵敏度分析时建议收紧到 1e-6 和 1e-9否则参数微调后曲线可能跳变。参数怎么改beta和gamma通常来自题目数据拟合不要手拍。如果题目给了累计感染人数先用最小二乘拟合出这两个参数再代入求解。拟合代码用scipy.optimize.curve_fit把solve_ivp包一层作为目标函数即可。2.2 离散型题型整数规划与 0-1 规划的求解模板离散型题目的标志是“选择”“分配”“排班”“路径”变量只能取整数或 0-1。常见做法是用pulp或scipy.optimize.milp。我倾向pulp因为约束写起来接近数学表达式调试时能直接打印模型。import pulp # 建立问题最小化成本 prob pulp.LpProblem(Assignment_Problem, pulp.LpMinimize) # 决策变量x[i][j] 表示第 i 个人做第 j 项任务0-1 变量 workers [A, B, C] tasks [T1, T2, T3] x pulp.LpVariable.dicts(x, (workers, tasks), catBinary) # 成本矩阵这里用虚构数据 cost { (A, T1): 4, (A, T2): 2, (A, T3): 8, (B, T1): 6, (B, T2): 5, (B, T3): 3, (C, T1): 7, (C, T2): 4, (C, T3): 6 } # 目标函数 prob pulp.lpSum(cost[(i, j)] * x[i][j] for i in workers for j in tasks) # 约束1每个人只能做一个任务 for i in workers: prob pulp.lpSum(x[i][j] for j in tasks) 1 # 约束2每个任务只能由一个人做 for j in tasks: prob pulp.lpSum(x[i][j] for i in workers) 1 # 求解 prob.solve(pulp.PULP_CBC_CMD(msg0)) # 输出结果 for i in workers: for j in tasks: if pulp.value(x[i][j]) 1: print(f{i} - {j}) print(最小成本:, pulp.value(prob.objective))逻辑说明LpVariable.dicts批量创建变量catBinary表示 0-1 变量如果是纯整数用catInteger。目标函数用lpSum而不是 Python 的sum因为lpSum对大规模变量更高效。约束里的是精确等于如果题目允许“最多一个”就改成 1。求解器默认用 CBC小规模问题够用如果变量超过几千个换GUROBI或CPLEX但需要额外安装。参数怎么改成本矩阵、人数、任务数都是外部输入建议从 CSV 读不要硬编码。如果求解状态是Infeasible先检查约束是否矛盾——最常见的是“每个人必须做且只能做一个”和“每个任务必须由且只能由一个人做”同时存在但人数和任务数不等。2.3 连续与离散的选型边界很多人卡在“这题到底算连续还是离散”。我的判断标准很简单如果决策变量可以取小数且有意义就是连续型如果只能取整数、或者本质是“选或不选”就是离散型。混合整数规划MIP是两者的交叉连续变量和整数变量同时出现用pulp也能写把对应变量的cat设成Continuous或Integer即可。注意连续型题目不要硬套离散方法否则结果会失真离散型题目也不要强行松弛成连续再四舍五入那样可能违反约束。3. 数据型与评价型从回归预测到综合排序的代码实现3.1 数据型题型回归与时间序列的快速基线数据型题目的特征是给一堆数据让你预测、分类或找关系。常见做法是先跑一个线性回归或随机森林作为基线再根据残差决定要不要上复杂模型。我一般用sklearn的LinearRegression和RandomForestRegressor对比因为两者训练快、解释性强。import numpy as np import pandas as pd from sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import mean_squared_error, r2_score # 读取数据假设 CSV 最后一列是目标变量 df pd.read_csv(data.csv) X df.iloc[:, :-1].values y df.iloc[:, -1].values # 划分训练集和测试集 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42) # 线性回归基线 lr LinearRegression() lr.fit(X_train, y_train) y_pred_lr lr.predict(X_test) # 随机森林对比 rf RandomForestRegressor(n_estimators200, max_depthNone, random_state42, n_jobs-1) rf.fit(X_train, y_train) y_pred_rf rf.predict(X_test) # 评估 for name, y_pred in [(Linear, y_pred_lr), (RF, y_pred_rf)]: rmse np.sqrt(mean_squared_error(y_test, y_pred)) r2 r2_score(y_test, y_pred) print(f{name}: RMSE{rmse:.4f}, R2{r2:.4f})逻辑说明train_test_split的random_state固定后结果可复现比赛时建议固定。RandomForestRegressor的n_estimators默认 100调到 200 通常更稳但超过 500 收益递减。n_jobs-1用满所有 CPU 核心。评估指标里 RMSE 对量纲敏感R2 反映拟合优度两个都要看。如果线性回归的 R2 已经超过 0.85随机森林提升有限优先用线性模型因为解释起来容易写进论文。参数怎么改如果数据有缺失值先df.fillna(df.median())或df.dropna()不要直接丢给模型。如果目标变量是时间序列不要用train_test_split随机划分要用时间顺序切分否则会数据泄露。时间序列的基线可以用statsmodels的 ARIMA但参数(p,d,q)需要看 ACF 和 PACF 图定新手建议先用auto_arima自动搜。3.2 评价型题型熵权法与 TOPSIS 的组合套路评价型题目让你对多个对象排序常见于“综合评估”“排名”“选优”。固定套路是先用熵权法算权重再用 TOPSIS 算贴近度。这两步加起来不到 50 行代码但能撑起一整篇论文的方法部分。import numpy as np # 原始决策矩阵每行一个对象每列一个指标 # 这里用虚构数据假设所有指标都是正向越大越好 data np.array([ [80, 90, 70, 85], [75, 95, 80, 80], [90, 85, 60, 95], [85, 88, 75, 90] ], dtypefloat) # 归一化正向指标 def normalize_positive(x): return (x - x.min(axis0)) / (x.max(axis0) - x.min(axis0)) # 如果是负向指标用这个 def normalize_negative(x): return (x.max(axis0) - x) / (x.max(axis0) - x.min(axis0)) # 假设第 3 列是负向指标 norm data.copy() norm[:, :2] normalize_positive(data[:, :2]) norm[:, 2] normalize_negative(data[:, 2]) norm[:, 3] normalize_positive(data[:, 3]) # 熵权法 def entropy_weight(norm): n, m norm.shape # 平移避免 0 norm norm 1e-12 p norm / norm.sum(axis0) e -np.sum(p * np.log(p), axis0) / np.log(n) d 1 - e w d / d.sum() return w w entropy_weight(norm) print(权重:, np.round(w, 4)) # TOPSIS def topsis(norm, w): weighted norm * w ideal_best weighted.max(axis0) ideal_worst weighted.min(axis0) d_best np.sqrt(((weighted - ideal_best) ** 2).sum(axis1)) d_worst np.sqrt(((weighted - ideal_worst) ** 2).sum(axis1)) score d_worst / (d_best d_worst) return score score topsis(norm, w) print(贴近度:, np.round(score, 4)) print(排名:, np.argsort(-score) 1)逻辑说明归一化是必须的否则量纲大的指标会主导权重。熵权法里加1e-12是防止log(0)这是血泪经验不加会报RuntimeWarning并产生nan。TOPSIS 的贴近度越接近 1 越好排名用argsort(-score)得到从大到小的索引。如果指标方向搞反了排名会完全颠倒所以归一化前一定要确认每个指标是正向还是负向。参数怎么改决策矩阵从题目数据来不要手编。权重方法除了熵权法还有 AHP 和变异系数法AHP 需要构造判断矩阵主观性强但适合指标少的情况。TOPSIS 的距离公式默认欧氏距离如果指标间相关性高可以换成马氏距离但计算量大新手慎用。3.3 数据型与评价型的常见误用数据型题目最容易犯的错是“过拟合还沾沾自喜”。训练集 R2 到 0.99测试集 R2 只有 0.3这种结果写进论文会被评委一眼看穿。解决办法是交叉验证cross_val_score跑 5 折看均值方差。评价型题目最容易犯的错是“权重拍脑袋”熵权法虽然客观但完全依赖数据分布如果某个指标所有对象取值接近熵权法会给它极低的权重但这不代表它不重要。这时候要结合 AHP 做组合赋权或者至少在论文里说明权重方法的局限性。4. 避坑与排查那些让代码跑不通的细节4.1 环境与依赖版本冲突现象import scipy报ImportError: DLL load failed或者pulp求解时提示找不到 CBC。原因Python 环境里混装了多个版本的科学计算库或者 CBC 二进制没在 PATH 里。解决用conda建独立环境conda create -n mcm python3.10然后pip install scipy pulp scikit-learn。如果pulp找不到 CBC在prob.solve()里显式指定路径pulp.PULP_CBC_CMD(pathC:/cbc.exe)或者换用pulp.GLPK_CMD()。4.2 求解器返回“Infeasible”但约束看起来没问题现象整数规划模型明明有可行解求解器却返回Infeasible。原因约束里的变量索引写错了或者lpSum里用了 Python 的sum导致表达式没被正确识别。解决先prob.writeLP(model.lp)把模型导出用文本编辑器打开逐条检查约束。常见错误是x[i][j]写成了x[j][i]或者约束里的写成了赋值而不是约束。4.3 微分方程求解结果发散或为负现象solve_ivp跑出来的 S、I、R 出现负数或者数值爆炸到 1e10。原因步长太大或者方程本身刚性太强但用了 RK45。解决先换methodBDF再把rtol和atol收紧到 1e-8。如果还是发散检查方程是否写错——比如把-beta * S * I写成了-beta * S I这种符号错误在推导时很难发现但数值结果会立刻暴露。4.4 归一化后出现 NaN现象熵权法计算权重时出现nan或者 TOPSIS 的贴近度全是nan。原因归一化时分母为 0即某个指标所有对象取值相同。解决在归一化前检查每列的最大值和最小值如果相等说明该指标无区分度直接剔除或赋一个固定权重。代码里加if x.max() x.min(): return np.zeros_like(x)作为保护。4.5 随机森林训练太慢现象RandomForestRegressor在几万条数据上跑了几十分钟还没结束。原因n_estimators设得太大或者max_depth没限制导致树太深。解决先把n_estimators降到 100max_depth设成 10 到 20 之间n_jobs-1开并行。如果还慢换HistGradientBoostingRegressor它在大数据集上比随机森林快一个数量级精度也不差。5. 进阶技巧把参考代码变成自己的论文武器参考代码能帮你跑通结果但拿奖靠的是“结果怎么解释、参数怎么调、模型怎么改进”。我一般会在跑通基线后做三件事第一对关键参数做灵敏度分析比如把beta从 0.1 扫到 0.5看峰值感染率怎么变画一张热力图这张图能直接放进论文的“模型分析”部分。第二对比至少两种方法比如连续型题目用 RK45 和 BDF 各跑一遍数据型题目用线性和随机森林各跑一遍把对比结果做成表格说明为什么选最终方案。第三把代码里的硬编码参数全部改成从配置文件读这样换一组数据不用改代码也方便复现。灵敏度分析的代码模板如下import numpy as np from scipy.integrate import solve_ivp def peak_infection(beta, gamma0.1): def sir(t, y): S, I, R y return [-beta * S * I, beta * S * I - gamma * I, gamma * I] sol solve_ivp(sir, (0, 200), [0.99, 0.01, 0.0], methodBDF, t_evalnp.linspace(0, 200, 1000)) return sol.y[1].max() betas np.linspace(0.1, 0.5, 20) peaks [peak_infection(b) for b in betas] for b, p in zip(betas, peaks): print(fbeta{b:.2f}, peak{p:.4f})这段代码把beta作为扫描变量输出每个取值下的感染峰值。逻辑上peak_infection封装了完整的求解过程betas用linspace生成等间距采样点。参数说明gamma固定为 0.1 是为了控制变量实际分析时可以同时对beta和gamma做二维扫描用meshgrid生成网格再画contourf热力图。验证方法上我习惯用“解析解对照”和“残差检查”两条路。如果微分方程有解析解比如 Logistic 方程把数值解和解析解画在同一张图上看最大误差是否在可接受范围。如果没有解析解就检查残差把数值解代回原方程看左右两边差多少残差应该随步长减小而收敛。数据型题目则用学习曲线判断是否过拟合sklearn.model_selection.learning_curve画训练集和验证集得分随样本量变化的曲线如果两条曲线收敛且验证集得分不再上升说明模型容量够了。最后说一个我自己的习惯每次跑通一个模型立刻把参数、随机种子、库版本记在一个run_log.md里。美赛四天里你会跑几十次实验没有日志的话第三天根本记不清哪个结果对应哪组参数。这个习惯帮我省下了至少两个通宵的重复劳动。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询