简介:2025年第二十二届五一数学建模竞赛C题配套论文、代码与结果整合在单个docx文档中,面向参加该赛事或学习社交媒体用户行为预测的建模爱好者。文档以附件1的用户行为记录为基础,针对问题一预测博主次日新增关注数并给出Top5博主,问题二预测用户次日新关注博主并生成关注列表,问题三预测用户在线状态及与博主的互动数,整体采用LSTM等时间序列模型建模,结合数据统计与特征工程。论文部分包含问题重述、数据探索、模型假设、特征工程、模型构建、求解与结果分析,附录代码覆盖数据清洗、时间窗口特征提取、LSTM训练调参与预测、结果导出等完整流程,并附注释,可直接运行和替换数据复用。压缩包仅含1个docx文件,大小1.33MB,便于阅读批注,也方便按论文公式对照代码逻辑。目前已有1003人学习下载,适合需要快速理解C题建模思路、借鉴预测模型实现细节的参赛者参考。
1. 别把C题写成黑匣子:论文、代码、结果要能一起交付
如果你正在准备数学建模竞赛,手头最怕的不是题难,而是好不容易跑出来的模型,最后交上去的 docx 里只有模型公式和表格,代码放哪儿了?结果怎么算的?评委拿着文档根本走不通。这个标题真正想说的是:论文、代码、结果三者要能对得上,合成一个能自洽、能复现的 Word 文档。对新手来说,这是拿奖的基本功;对熟手来说,这是把赛题做扎实的最后一道工序。本文就围绕“怎么把这三个部件装进一个 docx”展开,从模型选型讲到文档排版,最后落到几个我真实踩过的坑。
2. C题结果从哪来:问题拆解与建模选型
2.1 先判断C题在考什么:数据挖掘还是优化决策
C题通常给你一张大表,列几十个字段,让你做预测、分类或者给出一套方案。拿到题目别急着敲代码,先判断问题类型。连续值预测,比如销量、温度、价格,属于回归;离散标签,比如用户是否会续费,属于分类;要是给了多个约束让安排资源,那是优化问题。判断错了,后面整个模型方向就歪了。
一个简单的拆分方法:看目标变量的类型和题目最后的问法。问“预测未来三个月”就是回归,问“选哪几类车投入市场”就是分类或排序,问“设计一个调运方案使成本最小”就是约束优化。C题往往混合型,比如先预测再优化,这时要拆成两个子问题分别建模块,不要指望一个模型解决所有事。
我第一次参加这类赛题时,看到数据有几十列就想上深度学习,结果数据量只有几百行,训练效果很差。后来老老实实先做线性回归和树模型做对比,论文反而更好写。建议把这个问题想清楚再动手,可以少走一半弯路。
2.2 从可解释的基线模型开始,再逐步升级
不要一上来就上黑匣子模型。常见做法是先用线性回归跑通一个基线,拿到可解释的系数和误差,再叠加树模型看能否提升。这样论文里能写出“为什么要选最终模型”的对比逻辑,评委也更认可。
下面这段代码演示了如何用同一份数据训练线性回归和梯度提升树,并计算 MAPE(平均绝对百分比误差):
import numpy as np import pandas as pd from sklearn.linear_model import LinearRegression from sklearn.ensemble import GradientBoostingRegressor from sklearn.metrics import mean_absolute_percentage_error # 固定随机种子,保证结果可复现 rng = np.random.default_rng(42) n = 36 # 模拟三个变量:价格、广告投入、销量 price = rng.uniform(50, 80, n) ad = rng.uniform(5, 20, n) # 真实关系:销量 = 1500 - 12*价格 + 80*广告投入 + 噪声 sales = 1500 - 12 * price + 80 * ad + rng.normal(0, 50, n) df = pd.DataFrame({'价格': price, '广告投入': ad, '销量': sales}) # 按时间顺序切分,前30条训练,后6条测试 train = df.iloc[:30] test = df.iloc[30:] X_train = train[['价格', '广告投入']] y_train = train['销量'] X_test = test[['价格', '广告投入']] y_test = test['销量'] # 线性基线 lr = LinearRegression().fit(X_train, y_train) pred_lr = lr.predict(X_test) # 梯度提升树 gbr = GradientBoostingRegressor(n_estimators=200, learning_rate=0.05, max_depth=3, random_state=0) gbr.fit(X_train, y_train) pred_gbr = gbr.predict(X_test) print('LR MAPE:', round(mean_absolute_percentage_error(y_test, pred_lr), 4)) print('GBR MAPE:', round(mean_absolute_percentage_error(y_test, pred_gbr), 4))这里有两个关键参数:n_estimators=200表示生成200棵子树,太少会欠拟合,太多会过拟合,一般看验证集误差拐点去调;learning_rate=0.05是每棵树的贡献系数,调小能提高精度,但需要更多树;max_depth=3限制每棵树深度,防止单个模型过度记忆训练集。random_state=0只控制随机采样,不控制训练顺序,但对结果可复现性很重要。
跑完这段代码,你会得到两个指标。如果线性回归和提升树差距不大,论文里完全可以用线性回归当最终模型,附上系数解释,反而显得稳。如果树模型明显更好,就把两者对比作为“模型改进”一节。
2.3 把模型输出变成论文里的三类表格
模型跑完只是第一步,结果要能转写成论文能用的格式。常见做法是输出三类东西:指标汇总表、预测值与真实值对比表、误差分布图。下面这段代码把预测结果存成 DataFrame 并计算误差占比:
results = pd.DataFrame({ '实际销量': y_test.values, 'GBR预测': pred_gbr, '绝对误差': np.abs(y_test.values - pred_gbr), '误差百分比': np.abs((y_test.values - pred_gbr) / y_test.values) * 100 }) print(results.round(2)) results.to_csv('model_results.csv', index=False, encoding='utf-8-sig')encoding='utf-8-sig'是为了让 CSV 在 Excel 里打开不乱码,这是中文 Windows 环境常见的坑。计算误差百分比时要用绝对值,否则正负误差会互相抵消。表格里的列名建议直接用中文,评委读起来省力。
有了这张表,论文中的结果分析就可以直接引用。你还可以按误差百分比排序,找误差最大的几条样本,分析为什么预测不准,这就成了“误差原因分析”一节的素材,比单放一张预测曲线图更有说服力。
3. 论文+代码+结果.docx的内容结构:让评审能按图索骥
3.1 从空文档到完整包:目录层级与附录组织
Word 文档不能只有论文正文,还要让评审能顺着目录找到代码、结果数据和运行说明。我习惯的结构是:摘要 → 问题重述与分析 → 模型建立与求解 → 结果分析和敏感性检验 → 模型评价 → 附录(代码和完整运行输出)。每个一级标题对应一个章节,不要塞一堆无关内容。
下面是一个推荐的文件内容清单,你可以直接抄到自己的 docx 里:
| 章节 | 内容 | 对应文件 |
|---|---|---|
| 摘要 | 解决的问题、主要模型、关键结果 | 无 |
| 1. 问题分析 | 数据特征、问题类型判断 | 无 |
| 2. 模型假设 | 每条都对应数据里的可解释字段 | 无 |
| 3. 模型建立 | 公式、推导、模型选择理由 | 无 |
| 4. 模型求解 | 关键代码段,而不是全文代码 | solve.py |
| 5. 结果分析 | 指标表、误差图、敏感性表 | model_results.csv |
| 附录A | 运行环境、依赖包版本、运行顺序 | requirements.txt |
| 附录B | 完整代码或关键函数代码块 | main.py |
这个结构的关键是:每个表格都注明来自哪个文件,每段代码都说明在哪个脚本里可以找到完整版本。评审不想看到“代码略”,但也不需要你把几百行代码全贴在正文里。
3.2 把长代码塞进 Word 不翻车的三种做法
最推荐的做法是在正文只放核心代码,完整代码放附录。核心代码控制在 30-50 行,用等宽字体,比如 Word 里的 Consolas 或 Courier New。第二种做法是把代码文件以附件形式放进 docx,具体操作是“插入” → “对象” → “文件”。第三种做法是截图嵌入,但不建议,因为图片不能复制、不能搜索、清晰度也容易出问题。
插入代码块之前,先关闭 Word 的“自动更正”功能,尤其是“自动套用格式”,否则代码里的中文引号会被自动替换成弯引号,Python 直接语法错误。代码粘贴后还要检查缩进,Word 有时会把连续空格压成制表符,导致运行时报错。最简单的方法是先在纯文本编辑器里确认缩进,再粘贴到 Word。
我见过有人把整个main.py复制进去,结果 Word 因为代码太长自动分页,把函数定义拆到两页,读起来很乱。正确做法是只放调用链最核心的部分,其余放附录。附录里可以标注“完整代码见附录B”,这样正文篇幅受控,评审想复现也能找到。
3.3 结果表和图片的呈现:三线表与矢量图
数学建模论文的表格默认用三线表,也就是只有表格顶线、栏目线和底线,不要竖线。Word 里可以设置表格样式为“三线表”,或手动设置:选中表格,边框选“上框线”“下框线”“栏线”三种,其他都去掉。这样打印出来干净,评审也习惯。
图片方面,matplotlib 直接输出的 PNG 如果 dpi 不够,缩放后会很模糊。我一般用dpi=300保存,或者直接存 PDF/SVG 再插入 Word。Word 支持插入 SVG,插入后文字清晰,但需要注意旧版 Word 可能不兼容。稳妥做法是存成 300dpi 的 PNG,并把图片宽度设为 12 厘米左右,不要拉全宽。
所有图表都要有编号和标题,比如“表4 梯度提升树预测误差统计”。图表标题用“表上、图下”的规则,这是论文通用标准。最后检查一遍全文交叉引用:如果正文写“如图3所示”,图3就一定要存在,且内容相关。
4. 从数据到 docx 的最小可复现流程:拿一个C题例子走一遍
4.1 定义场景与数据说明
为了说明完整流程,这里构造一个常见的 C 题式场景:某平台公开了连续 36 个月的订单量、商品价格、广告投入和季节指数四个字段,题目要求预测未来 6 个月的订单量,并给出定价建议。数据是模拟的,但你完全可以用自己手头的数据替换。
注意时间陷阱:预测未来 6 个月,训练集只能取前 30 个月,测试集用后 6 个月当作“未来”,这样评估才真实。如果随机抽样做训练测试,时间序列会泄漏未来信息,论文里的误差会非常离谱。具体原因在下一章避坑里专门讲。
4.2 数据清洗、特征构造、训练、导出结果的完整代码
下面这段代码覆盖了读取数据、按时间切分、构造滞后特征、训练模型、导出结果和运行说明的全过程:
import pandas as pd import numpy as np from sklearn.linear_model import LinearRegression # 读取数据,假设已有 data.csv df = pd.read_csv('data.csv', encoding='utf-8-sig') df = df.sort_values('月份') # 确保按时间排序 df = df.dropna() # 去掉空值 # 构造滞后特征:上个月订单量 df['上月订单量'] = df['订单量'].shift(1) df = df.dropna() # shift 后第一行没有上月,删掉 # 特征列与目标列 features = ['价格', '广告投入', '季节指数', '上月订单量'] target = '订单量' # 时间切分:最后6个月做测试 train = df.iloc[:-6] test = df.iloc[-6:] X_train = train[features] y_train = train[target] X_test = test[features] y_test = test[target] # 线性回归训练 model = LinearRegression() model.fit(X_train, y_train) test['预测订单量'] = model.predict(X_test) # 计算相对误差 test['相对误差%'] = ((test['预测订单量'] - test['订单量']) / test['订单量']) * 100 # 导出结果,保留两位小数 out = test[['月份', '订单量', '预测订单量', '相对误差%']].round(2) out.to_csv('C题结果表.csv', index=False, encoding='utf-8-sig') # 输出模型参数,供论文撰写使用 print('模型截距:', round(model.intercept_, 2)) print('特征系数:', dict(zip(features, model.coef_.round(2))))dropna()这一步很关键,shift(1)会制造第一条空值,如果不删,模型就拿着 NaN 去训练,结果全变 NaN。时间序列切分用iloc而不是train_test_split,是为了保持先后顺序,避免未来数据混入训练。导出时round(2)是为了论文表格不出现太长的浮点数。encoding='utf-8-sig'保证 Excel 打开不乱码。
模型参数输出来以后,论文就能写“价格每上涨一个单位,订单量平均减少多少;广告投入每增加一个单位,订单量增加多少”,这是线性回归的优势,树模型做不到。如果最终模型是树模型,也可以用feature_importances_写特征重要性分析。
4.3 代码和结果怎么对应到论文章节
写完这段代码后不要直接全贴进 Word,而是给代码标上行号,然后在论文里写“见代码段2”,并在附录中放完整版。结果表放结果分析章节,截图或直接嵌入。模型参数放模型求解章节,作为公式的数值化结果。
一个常见的对应关系是:在“模型求解”段落后加一行小五号字,写着“运行main.py后得到C题结果表.csv,本文表4为该文件的直接输出”。这样做的好处是,如果评委对结果有疑问,可以直接运行你的脚本对证。即使评委不运行,看到这行字也会认为你的工作是严谨的。
5. C题交付避坑指南:评审最常打回的五种问题
5.1 现象:代码能跑,但结果对不上论文
这是一个高频问题。我们自己写论文时,经常是上午跑出一版结果,下午又改了模型和参数,忘记更新论文里的表格。评审一眼就看出数值不一致。
原因:没有把“结果文件”和“论文表格”绑定为同一来源,修改代码后没有重新生成 Word 中的数字。
解决:建立“结果自动导出”习惯。每次模型跑完,强制输出一个带时间戳的结果表,比如结果_20250130.csv。论文里直接引用最终版文件的数字,并在表格下方注上“数据来源:结果_20250130.csv”。改模型后重新跑一遍,不要手动替换表格里的某个数。
5.2 现象:代码从 Word 复制出来运行直接报错,尤其是缩进错误的报错
贴代码的人往往只贴了前半段,Word 自动把空格转成制表符,Python 报TabError: inconsistent use of tabs and spaces in indentation。
原因:Word 的智能缩进把代码里的空格修改成了制表符,或者在自动更正里把直引号改成了弯引号,导致字符串语法错误。
解决:在论文附录里提供原始代码文件,而不是只靠 Word 正文。正文代码块统一用等宽字体,关闭 Word 自动更正。更稳妥的做法是:把代码放在附录,并在正文中用“插入对象”链接一个.py文件,这样评审可以直接双击打开原文件。如果你担心链接失效,就在附录里同时放一份字符版本。
5.3 现象:评委说结果无法复现,原因是跑不出来或结果不同
很多时候不是我代码错了,而是依赖包版本不同导致结果轻微变化。如果模型里有随机数,每次运行都会不一样。
原因:缺少requirements.txt,没固定随机种子,模型训练使用了随机初始化或数据洗牌。
解决:在附录里写明运行环境,格式可以这样:
Python 3.9 pandas==1.5.3 numpy==1.23.5 scikit-learn==1.2.2同时在代码开头设置random_state或np.random.seed(0)。注意random_state只对特定库有效,如果用了多线程的任务,尽量固定全流程的随机种子。一个更简单的做法是:把最终预测结果和中间步骤的关键输出都写进 CSV,论文直接引用这个 CSV,就不怕结果漂移。
5.4 现象:误差指标低得离谱,但评委一眼看出不合理
很多人做时间序列预测时用了随机切分,导致训练集里混入了未来的数据,模型提前“知道”了答案,表现在测试集上误差极小。
原因:这是数据泄漏。C题给的是时间序列时,train_test_split默认随机切分会把未来样本放进训练集,模型学会了记忆,而不是泛化。
解决:时间序列必须用df.iloc按时间切分,或者用TimeSeriesSplit做交叉验证。如果你用的是交叉验证,也指定shuffle=False。在论文里明确写“采用前 X 个月训练、后 Y 个月预测”,这个说明本身就能避免很多质疑。
5.5 现象:Word 文档里的图放大后模糊,文字边像锯齿
成因很直接:图片分辨率太低,或者直接从网页截图粘贴,缩放时失真。
解决:所有图表统一用 300 dpi 输出,matplotlib 保存时加dpi=300,不要直接用plt.show()后截图。图片在 Word 里插入后,宽度设置为不超过 15 厘米。如果你用的是 Seaborn,底层也是 matplotlib,同样适用。另一个更省事的方法是保存成 SVG 再插入,但要确认评审用的 Word 版本能正常显示。
6. 最后一步:把结果做成评委能一眼看懂的验证页
这一章没有新模型,但有一个我每次交付前必做的动作:在 docx 的附录最前面放一页“复现说明”,告诉评委这个包怎么跑通、需要什么环境、每一步输出什么文件。这页不需要太多文字,但要让一个没看过你代码的人也能操作。
复现说明里至少要有三部分:运行顺序、运行时间、结果文件清单。下面是一个可以直接套用的示例:
| 步骤 | 操作 | 输出 |
|---|---|---|
| 1 | 安装依赖包 | 无 |
| 2 | 运行1_data_preprocess.py | clean_data.csv |
| 3 | 运行2_model_train.py | model_results.csv |
| 4 | 运行3_make_figures.py | figures/*.png |
| 5 | 打开主论文.docx,核对表格数值 | 文档 |
每个脚本的最后一行都加一个打印语句,输出“脚本执行成功”,这样可以避免中途静默报错。我一般还会加一段自动检查,如果结果表和论文里的某个关键指标不一致,直接打印警告。这相当于给自己留了一双眼睛。
做验证页的时候,记得自己也按这个顺序跑一遍。我有一个特别普通的习惯,每次完赛都会关掉所有环境,重新打开一个干净的目录,照着复现说明一步步跑。第一次跑不通的地方,往往就是评委也会卡住的地方。修改完再跑一遍,直到完全顺滑。最后把那句话写进交付文档:“所有结果均可通过附录脚本按步骤复现。”这样做的直接好处是,至少不会被“复现不了”一票否决,希望帮到你。
本文还有配套的精品资源,点击获取