简介:这是一份昆明理工大学数学建模实验2的线性规划实验报告PDF,面向数学建模、运筹学初学者以及需要完成类似实验任务的高校学生。报告围绕广告投放优化场景展开,要求在总广告费不超过800千元、电视广告费用不超过500千元等约束下,通过MATLAB求解白天电视、最佳时间电视、无线电广播和杂志广告的最佳投放次数,以最大化招徕顾客数。全文包含实验目的与环境、符号说明、问题分析、线性规划模型建立、目标函数与约束条件推导,以及使用linprog函数的完整源程序、运行结果截图和最终最佳投放方案分析。资源为单个PDF文件,大小仅158KB,内容排版完整,可直接作为课程作业、实验报告或期末复习的参考资料。目前已有350人学习下载,适合需要快速掌握线性规划建模步骤和MATLAB求解方法的学习者。
1. 一份"数学建模+实验2.pdf",解开后是一整套建模练习
如果你在某高校课程平台下过一份《数学建模+实验2》PDF,多半有过类似经历:满怀期待地打开,发现里面既没有可复制的完整代码,也没有标准答案,只有两三页问题描述、一张数据表,外加几句"请用所学方法求解并完成实验报告"。这份文件看起来单薄,实际是老师把完整建模流程装进了一次作业里:从读题、定变量、选方法,到写代码、检验、写报告,每一步都被要求明确。它能帮你补的,恰恰是课堂上学不到的那段"从题目到交付"的完整链路。适合正在修数学建模课的学生、刚进数模竞赛门的新手,以及准备给学生设计实验题的助教。
2. 从PDF到任务清单:先把实验指导书拆成能执行的四步
2.1 先看结构:一份实验PDF里通常藏着哪四样东西
拿到PDF,别急着打开数据表。我花十分钟通读一遍,通常会发现文档由四部分组成:实验目的、问题背景与数据、实验要求、评分与提交规范。这四部分位置不固定,评分标准常藏在最后一页,数据可能在正文也可能在附录,漏掉任何一块都可能导致返工。
| 结构块 | 常见位置 | 要提取的信息 | 容易漏的坑 |
|---|---|---|---|
| 实验目的 | 开头一至两段 | 本实验训练哪个建模方法 | 写报告时照抄,不转成自己的表述 |
| 问题背景与数据 | 第2至3节 | 数据含义、单位、来源 | 数据可能有缺失,单位混着给 |
| 实验要求 | 文档中后部 | 用几种方法、是否需要画图 | 明确要求对比时,只做一种会扣分 |
| 评分与提交规范 | 末页或附录 | 报告结构、代码是否附上 | 只提交结果没提交过程可能被打回 |
这四块里,最容易被忽略的是"评分与提交规范"。很多同学把全部精力放在建模上,最后发现报告要按固定模板写、代码要附完整可运行版本、图表要单独编号。我一般拿到PDF第一件事,就是把评分标准复制到单独的笔记里,当作写报告时的检查清单,等全部做完再逐条核对提交物,能省掉不少返工时间。
2.2 把要求翻译成执行步骤:从"看懂题目"到"四步流水线"
通读之后,我会把PDF要求翻译成一条四步流水线:第一步,定义问题,把题目里的每句话拆成变量和约束;第二步,选方法,根据数据类型决定用拟合、插值还是回归;第三步,写代码求解,把模型表达式变成可运行脚本;第四步,检验与报告,计算指标、画图、把结论写成人话。
举个例子:PDF给出的题目是"某产品过去12个月的销量数据,预测下三个月销量",同时要求"用拟合方法建模并检验"。那么四步的产物分别是:变量定义表,列出月份序号t、月销量y、参数a与b;方法选型说明,先用散点图判断趋势走向,再选线性或指数拟合;脚本与图表,包括拟合参数、预测值、拟合曲线图;检验报告,包括R²、残差图、参数置信区间,以及对预测结果的业务解释。
变量定义这一步我建议用表格写死,后面写报告直接搬过去:
| 符号 | 含义 | 单位 | 来源/说明 |
|---|---|---|---|
| t | 月份序号,从0开始 | 月 | 数据表第1列,t=0对应起始月 |
| y | 月销量 | 件 | 数据表第2列 |
| a | 截距,t=0时预测销量 | 件 | 拟合参数 |
| b | 斜率,每过一个月销量变化 | 件/月 | 拟合参数 |
四步中最容易被跳过的其实是第一步变量定义。很多同学直接写代码,到报告里解释参数含义时卡壳,回头再补定义等于重做一遍。我把这份定义表放在所有工作的起点,每次跑完一步回头看一眼,确认没偏离题目。
如果PDF是扫描版,文字没法直接复制,我一般先用一个小脚本把文本抽出来再读:
import pdfplumber # 从PDF中按页提取纯文本,便于快速定位题目和数据 with pdfplumber.open("experiment2.pdf") as pdf: for i, page in enumerate(pdf.pages): text = page.extract_text() if text: print(f"--- Page {i+1} ---") print(text)逻辑说明:
extract_text()对印刷体PDF效果很好,扫描图片版需要先OCR,这一步不做也不影响后续建模,至少能帮你快速定位页码;- 打印分页文本后,再用上面说的四块结构去切分,比直接盯着一页页截图效率高得多;
- 如果提取出的文本乱序,多半是文档本身分栏排版,可以按栏顺序手工拼接,不需要重新下载其他工具。
这一章的核心目标是:开工之前先花二十分钟把PDF吃透,把一份模糊的"请建模求解"变成一张有验收标准的任务清单。做完这一步,后面所有操作都只是填空。
3. 用最小二乘拟合打通实验2:原理、代码和参数
3.1 为什么实验2十有八九落在"拟合—检验"这条主线上
从教学安排看,"实验2"很少直接上微分方程或仿真模拟,最常见的就是数据拟合加回归分析。原因有三:第一,它能把建模从抽象变成可操作,散点图一画、模型曲线一配,建模过程立刻可视化;第二,它天然衔接后续的优化和误差分析课程,R²、残差、置信区间这些概念都会在后面反复用;第三,它容易产出可量化的结果,老师好评分,学生也好核对。
这里必须澄清一个常见误区:拟合不等于插值。插值要求曲线穿过每一个已知点,拟合只要求曲线整体误差最小。实验2要的是拟合,因为你关心的是数据背后的趋势,不是逐点还原噪声。选拟合还有一个现实原因:带噪声的数据做插值,曲线会剧烈震荡,拟合曲线的抗噪能力更强,预测也稳定得多。
| 维度 | 插值 | 拟合 |
|---|---|---|
| 目标 | 曲线精确穿过所有已知点 | 曲线整体误差最小 |
| 对噪声的态度 | 很敏感,容易过拟合 | 对噪声有平滑效果 |
| 适用场景 | 需要精确还原每个数据点 | 找趋势、做预测 |
| 在实验2中的出现率 | 偶尔用于样条插值 | 主流做法 |
所以拿到实验2的数据,先画散点图,看趋势是线性、指数还是带周期,再决定用哪一族拟合函数。这一步别省,后面所有代码都建立在这个判断上,方法选错等于全盘重做。
3.2 最小二乘拟合的Python实现:最小脚本与三个必调参数
下面是最小可运行的拟合脚本,包含从读数据到输出参数的全过程。假设数据文件是data.csv,两列,列名分别是month和sales,共12行。
import pandas as pd import numpy as np import matplotlib.pyplot as plt from scipy.optimize import curve_fit # 1. 读取CSV数据,month为月份序号,sales为销量 df = pd.read_csv("data.csv", encoding="utf-8-sig") t = df["month"].values.astype(float) y = df["sales"].values.astype(float) # 2. 定义线性模型 y = a + b * t def linear_model(t, a, b): return a + b * t # 3. 拟合:返回最优参数popt和协方差矩阵pcov popt, pcov = curve_fit(linear_model, t, y, p0=[1, 1]) # 4. 计算拟合值和残差 y_fit = linear_model(t, *popt) residual = y - y_fit # 5. 画散点图和拟合曲线 plt.scatter(t, y, label="观测值", color="steelblue") plt.plot(t, y_fit, label="拟合曲线", color="darkorange", linewidth=2) plt.xlabel("月份 t") plt.ylabel("销量 y") plt.legend() plt.grid(alpha=0.3) plt.savefig("fit_result.png", dpi=150) plt.show() # 6. 输出参数 print("拟合参数 a=%.3f, b=%.3f" % (popt[0], popt[1]))逻辑说明:
curve_fit的四个核心输入是模型函数、自变量、因变量和初始猜测p0。模型函数必须能用*popt解包方式调用,函数签名顺序和popt顺序要一致;pcov是协方差矩阵,后面算置信区间要用,别丢;- 残差要自己算,
curve_fit不直接返回残差列表,而残差图又是报告必需品,所以第4步不能省。
三个必调参数:
p0:初始参数猜测。线性模型不敏感,但换成指数模型后p0设不好会收敛到局部最优,甚至直接报错。常见做法是按数据范围估计初始值,比如指数模型取p0=[min(y), (y[-1]-y[0])/(t[-1]-t[0])];maxfev:最大迭代次数。数据量大或者模型复杂时,默认值可能不够,设maxfev=8000能减少"拟合失败"的玄学问题;absolute_sigma:设为True时,把数据误差当成绝对误差处理,适合已知测量误差的实验数据;实验2一般不知道误差,保持默认即可。
模型要换时,只需改模型函数和p0。比如数据呈现指数增长,改成:
def exp_model(t, a, b, c): return a * np.exp(b * t) + c popt, pcov = curve_fit(exp_model, t, y, p0=[y.min(), 0.1, 0], maxfev=8000)参数说明:
- 指数模型的
a控制起始幅值,b控制增长速率,c控制基线偏移。p0里的 0.1 是b的初始猜测,如果数据增长特别快,可以试 0.5; - 指数拟合对
p0敏感,一个通用技巧是先用线性拟合对数据取对数,把斜率作为b的初始值。
预测下一个月很简单,把 t=13 代入模型:
t_next = 13.0 y_pred = linear_model(t_next, *popt) print("第13个月预测销量: %.2f" % y_pred)做预测前要确认自变量t的定义:如果数据里 month 是从 1 开始的序号,那下次就是 13;如果 month 存的是实际日期,必须转成连续序号再预测,否则模型不知道"下个月"在数值上是多少。
3.3 R²、残差图与置信区间:实验2的评分点其实在检验环节
拟合跑通只是开始,报告的重头在检验。实验2的评分标准通常明确要求"给出模型检验",我见过很多代码一步到位、报告里却只有一张图和一句"拟合效果很好"的作业,结果都被打回。检验环节至少要做三件事。
第一件,算R²(决定系数)。公式是1减去残差平方和除以总平方和,越接近1说明模型解释的数据变异越多,但R²不是越高越好,过拟合时R²也会虚高。
from scipy import stats # R² ss_res = np.sum(residual**2) ss_tot = np.sum((y - np.mean(y))**2) r2 = 1 - ss_res / ss_tot print("R² = %.4f" % r2) # 残差图:残差随拟合值的变化 plt.scatter(y_fit, residual, alpha=0.6) plt.axhline(0, color="gray", linestyle="--") plt.xlabel("拟合值") plt.ylabel("残差") plt.savefig("residual_plot.png", dpi=150) plt.show()残差图怎么看:理想状态是残差点在零线上下随机分布,没有明显弯曲,也没有从窄到宽的喇叭口。如果残差呈现弯曲,说明模型结构不对,比如数据其实是二次趋势却用了直线;如果残差呈喇叭形,说明方差不稳定,可以考虑对 y 取对数。
第三件,算参数的置信区间。这里用pcov和自由度修正的标准误,给出 a 和 b 的95%置信区间,能说明参数估计稳不稳:
# 参数标准误与95%置信区间 n, p = len(t), 2 sigma2 = np.sum(residual**2) / (n - p) se = np.sqrt(np.diag(pcov) * sigma2) alpha = 0.05 t_value = stats.t.ppf(1 - alpha / 2, df=n - p) for i, name in enumerate(["a", "b"]): ci_low = popt[i] - t_value * se[i] ci_high = popt[i] + t_value * se[i] print(f"{name}: {popt[i]:.3f} ± {t_value * se[i]:.3f}, CI=[{ci_low:.3f}, {ci_high:.3f}]")逻辑说明:
- 这里用残差方差
sigma2除以自由度 n-p,再乘pcov的对角线,得到参数标准误se,是教材里的标准做法; - 如果置信区间跨过 0,说明该参数在统计上不显著,或许应该从模型中去掉这一项;
- 把这三样东西写进报告,检验环节才算完整。很多比赛的优秀论文,检验部分的篇幅比建模部分还长,原因就在这里。
4. 避坑:数据拟合实验的5个典型翻车现场
4.1 数据没排序,拟合曲线像一团乱麻
现象:用 Pandas 读入 CSV 后直接画图,散点图在横轴方向来回跳,拟合曲线扭成麻花,R² 甚至出现负值。
原因:数据文件的行顺序不是按自变量排好的。画图时plot是按行号顺序连线的,数据乱序就会画出折返线;而拟合本身按点计算,不依赖顺序,问题出在可视化,不是模型错了。
解决:画曲线前先按自变量排序。df = df.sort_values("month"),再取数组;拟合可以不排序,但画图必须保证 x 单调递增,否则曲线一团乱。这类问题在 Excel 导入的数据里特别常见,我处理时习惯先print(df.head())确认 month 列是不是单调递增,省得画完图才发现。
4.2 贪图高次多项式,结果过拟合
现象:用 8 次多项式把 12 个点的数据拟到 R²=0.999,报告很好看,但预测下一个点时数值直接飞了。
原因:高次多项式自由度足够大,可以穿过每个噪声点,拟合的是噪声而不是趋势,R² 在过拟合场景里完全失真。
解决:把数据分成训练集和测试集,用测试集表现判断模型好坏。多项式次数超过 3 就要警惕,或者直接看残差图——残差不再随机而是一段段弯曲,就是过拟合信号。判断是否过拟合还有个快速办法:如果测试集 RMSE 比训练集差一大截,说明模型把训练集里的噪声也学进去了。
4.3 把日期直接喂给模型
现象:数据里月份是 "2023-01" 这种字符串,astype(float)直接报错;有人手动转成 1、2、3 后模型能跑,但解释参数时说不清"每过一个月"到底从哪天算起。
原因:日期不是连续数值变量,字符串更没法参与计算。即便转成序号,也需要明确基线,否则回归系数的含义悬空,答辩时容易被问住。
解决:把日期统一转成连续的时间索引,比如从起始月份开始编号 0、1、2…,并在报告里写清"t=0 对应 2023 年 1 月";或者用pd.to_datetime处理成真正的 datetime,再按月份差值计算序号。核心是让模型表达式里每个变量的定义都无歧义。统一化之后,记得检查数据里有没有缺失月份,缺月会导致序号错位,预测全偏。
4.4 只贴R²不画残差图,报告被打回
现象:报告里只有一个 R²=0.94,配一张拟合曲线图,老师反馈"检验不充分,补充残差分析后提交"。
原因:评分标准里明确写了"给出模型检验过程"。R² 是整体指标,残差图是局部诊断,两者互补,只写一个等于检验只做了一半。
解决:把 3.3 的残差图代码跑出来,并在报告里补一句判断结论:"残差在零线附近随机分布,无明显弯曲和漏斗形,说明线性模型假设基本合理。"这句话虽然短,却是评分时区分"跑完了"和"想明白了"的关键。残差图这步没有捷径,画出来几秒钟,但能提前发现模型选错。
4.5 报告只有代码没有假设,看起来像调包侠
现象:代码能跑,图也贴了,但整份报告找不到变量定义和模型假设,答辩被问"为什么选线性模型"直接卡住。
原因:数学建模报告评分看的是建模过程,代码只是过程的一部分。没写假设等于没交代清楚模型成立的前提条件,老师无从判断你是理解了还是只会调包。
解决:报告固定写五段:问题重述、变量定义、模型建立(含假设)、求解与检验、结论。变量定义用表格列符号、含义、单位、来源;模型假设写三条足够,比如"数据无系统性采集偏差""销量变化短期连续""忽略节假日等特殊扰动"。写假设不是凑字数,每条假设后面跟一句"如果这个假设不成立,模型会怎样",比如数据有系统偏差会导致参数估计有偏,这才是建模者的思考。
5. 把实验2从"能交"做到"能打比赛":交叉验证与模型对比
5.1 用交叉验证替代单次划分,报告里多一张有说服力的图
实验2多数人习惯全量数据拟合完算 R² 交差。这个做法在竞赛里会被挑战,因为拟合和评价用的是同一批数据,属于"拿答案考自己"。常见改进是留出最后几个点做预测检验,但小样本下留出法波动大,折数选得不同结论可能完全不同。
我一般建议用 K 折交叉验证,代码量很小:
from sklearn.model_selection import KFold, cross_val_score from sklearn.linear_model import LinearRegression X = t.reshape(-1, 1) # 自变量需要二维数组 kf = KFold(n_splits=5, shuffle=True, random_state=42) scores = cross_val_score(LinearRegression(), X, y, cv=kf, scoring="r2") print("五折R²: %.3f ± %.3f" % (scores.mean(), scores.std()))参数说明:
shuffle=True在分折前打乱数据,避免时间顺序造成前后期偏移;但如果你是严格的时间序列预测任务,应该用TimeSeriesSplit按顺序划分,实验2里做交叉验证用随机划分即可;random_state=42固定划分方式,保证结果可复现,报告里必须写,不然别人复现不出你的数值会以为你编数据;scores.std()大于 0.2 说明模型对数据划分很敏感,也就是模型不稳定,这时候要回头检查特征或数据量,而不是硬调参数。
加了这段指标,报告里就能写:"五折交叉验证 R² 均值为 0.87±0.06,模型在不同数据子集上表现稳定,未出现明显过拟合。"这句话比单独贴一个 R² 有说服力得多。
5.2 做一次模型对比,把报告从"完成作业"提到"可参赛"水平
实验2通常只要求做一个模型,但主动加一个对比模型会让报告质量上一个台阶。常见组合是线性、指数、二次多项式三选二,用统一指标比较。看 RMSE 直观,看 AIC 能惩罚参数数量,防止无脑加项。
| 模型 | 适用数据形状 | 参数个数 | 实验2常见坑 |
|---|---|---|---|
| 线性 | 均匀上升或下降 | 2 | 有弯曲趋势时强行用线性 |
| 指数 | 近似等比增长 | 2或3 | 初始值设不好不收敛 |
| 二次多项式 | 有单峰或单谷 | 3 | 次数再高就过拟合 |
from sklearn.metrics import mean_squared_error def rmse(y_true, y_pred): return np.sqrt(mean_squared_error(y_true, y_pred)) def aic(n, rss, k): return n * np.log(rss / n) + 2 * k # 假设三个模型的预测值已存在:y_fit_linear, y_fit_exp, y_fit_quad models = { "线性": (y_fit_linear, 2), "指数": (y_fit_exp, 2), "二次": (y_fit_quad, 3), } for name, (y_pred, k) in models.items(): rss = np.sum((y - y_pred) ** 2) print(f"{name}: RMSE={rmse(y, y_pred):.3f}, AIC={aic(len(y), rss, k):.2f}")前提是三个模型必须用同一份数据拟合,预测值要按 t 排序对齐。把对比结果放进报告并写一句结论,比如"AIC 最小的是指数模型,说明加入增长趋势后模型复杂度合理,且比线性模型多解释 X% 的方差",选型就有了依据。多数实验2的评分表里都有一项"方法选择是否合理",这半页内容就是专门送给这项分数的。我自己带学生做这类实验时,最常见的情况是代码跑通就以为完事,直到答辩被问"你的模型假设是什么"才发现整份报告最值钱的部分没写。后来我习惯把评分标准倒过来当检查清单,先满足检验要求、再补代码、再补叙述,每轮返工时间都短不少。希望帮到你。
本文还有配套的精品资源,点击获取