Sams-.rar超市建模:从数据清洗到优化求解的完整路径

发布时间:2026/9/15 0:48:45
Sams-.rar超市建模:从数据清洗到优化求解的完整路径 简介围绕2004年全国大学生数学建模竞赛A题“奥运会临时超市网点设计”而形成的MATLAB解题案例面向数模竞赛选手、运筹优化学习者及MATLAB编程入门者。压缩包规模很精简共1个文件为.m格式的源代码脚本整体大小仅1KB集中呈现了从数据读取、选址模型构建、约束条件设置到求解与结果输出的完整代码流程。内容对应超市建模问题可用于理解人口需求预测、网络优化、p-median选址等典型方法在零售网点布局中的实际应用也便于对照赛题逐步复现求解过程。目前已有154人学习/下载适合希望借助实际赛题案例快速掌握数模解题框架与MATLAB代码实现的学生和研究者。通过研读这份源码可直观学习如何将地理分布、交通可达性等抽象条件转译为可计算的优化模型并在此基础上扩展自己的建模思路。1. Sams-.rar 背后那个超市建模问题到底在考什么拿到一个名为Sams-.rar的压缩包里面的“超市建模问题”通常不是让你解剖仓储货架而是把现实里的超市运营抽象成可计算、可优化的数学模型。这类题在数学建模竞赛和业务数据分析岗的笔试题里都很常见常见考核点包括基于历史销售数据做需求预测在货架容量和采购成本约束下做补货策略或者给门店排班、确定促销降价幅度。压缩包里的.txt、.csv、.xlsx甚至.dat文件往往就是题目原始数据和一张张待求解的业务表。这篇文章会站在拿到这个 rar 后的一线处理视角从解压开始到数据清洗、模型选择、求解器调参再到验证和可视化走完一条能复用的路径。适合正在备赛数学建模的学生也适合刚接触供应链优化、需要把数据变成决策方案的工程师。阅读前提是你需要装好 Python 环境和常用的数据、优化库后面的命令和代码都按可复现的标准来写。2. 先把 rar 里的数据老老实实解压出来再谈建模这步看着简单但不少人就在文件名、编码、文件缺失上栽了跟头。Sams-.rar这个名字本身没有信息量真正有信息量的是压缩包内部的文件列表。我建议的流程是先看列表再解压再逐文件读入最后做一遍数据完整性检查。搜热词里的“rar解压”“rar密码移除”之所以那么高频是因为竞赛题目的压缩包经常带密码或者文件命名混乱这一步处理不好后面的模型全是空中楼阁。2.1 查看压缩包结构与验证完整性在 Linux 或 macOS 下用unrar或unar命令。Windows 上可以用 WinRAR 或7z。先不急着解压用l参数列出文件清单unrar l Sams-.rar如果只装了 7-Zip等价命令是7z l Sams-.rar这条输出会显示每个文件的原始大小、压缩后大小、文件属性和路径。重点检查三件事路径是不是带有..这类危险字符是否有.jpg图片格式的题面是否有多个数据文件且命名相似。带密码的压缩包会在列表输出时提示加密状态用7z l -slt可以看更详细的加密头。确认文件数量与题目描述一致后开始解压到独立目录mkdir -p sams_data unrar x -o Sams-.rar sams_data/参数-o表示覆盖已存在的文件避免交互式询问。若存在密码unrar会等待输入在脚本环境里可以用 Python 的rarfile库配合密码字典进行批量尝试但在正式竞赛或工作场景中建议先联系题目提供方不要用暴力破解工具去碰密码那既不高效也容易踩到合规风险。解压完成后立即做一次文件校验cd sams_data md5sum -c checksums.txt 2/dev/null || echo 没有校验文件改用文件大小和数量核对如果原包里有checksums.txt用上面的方式校验没有的话用wc -l统计每个数据文件的行数和题目文档里描述的样本量对照。这一步能筛掉解压不完整或传输损坏的情况。2.2 三种常见数据文件的开读姿势压缩包里最常见的是 CSV、纯文本和 Excel 三种格式。CSV 先用 Python 的pandas读取最稳import pandas as pd df pd.read_csv( sams_data/sales_2019.csv, encodinggbk, # 中文 data 常用 gbk encoding_errorsreplace, dtype{商品ID: str}, # 防止带 0 的商品编码被读成整数 ) print(df.head()) print(df.info())这段代码里encodinggbk和encoding_errorsreplace是针对中文超市数据的常见双保险。若读出来中文列名是乱码就看utf-8、gb18030两个编码挨个试。把“商品ID”强制转成str是为了避免像078这样的编码被读成78造成后续关联失败。如果是.dat或固定宽度的文本文件pandas.read_fwf比read_csv更合适。先看一眼文件的前几行确定列宽度用widths[8, 12, 6, ...]参数切列。如果文件里包含多张表比如门店表、商品表、销售明细表那就得按段落拆分不能直接全量读取。对于.xlsx稍大的文件用openpyxl或pandas.read_excel都可以但要注意超市数据经常在一张 sheet 里合并单元格读取后需要df.ffill()把缺失的维度字段向下填充。例如日期列每隔几行才有一个值这时候按时间粒度填充是建模前的必备动作。2.3 数据里常见的脏数据与统一口径超市数据几乎没有干净的常见的问题有四种同一商品在不同记录里单位不一致瓶 vs 箱、时间戳跨了营业时间23:59 与 00:01 本质是同一自然日、促销打折导致单价出现 0 或负数、部分门店在节假日没有销售记录。下面这段清洗逻辑值得直接抄df[销售日期] pd.to_datetime(df[销售日期], errorscoerce) df df[df[销售数量] 0].copy() df[销售额] df[销售数量] * df[单价].clip(lower0) # 统一单位把箱改成瓶假设一箱12瓶 bottle_rate {箱: 12, 瓶: 1} df[标准数量] df[销售数量] * df[单位].map(bottle_rate).clip(lower0)让负数单价变为 0避免把销售额拉成负数。errorscoerce会把无法解析的日期置为NaT随后用df.dropna(subset[销售日期])剔除。单位换算的字典要依据题目描述来定义不要想当然因为有的题目里“箱”是 6 瓶有的是 24 瓶。数据口径统一后做一个最基础的分组汇总验证数据量变化是否合理daily_sales df.groupby([门店ID, 销售日期], as_indexFalse)[标准数量].sum() print(daily_sales.describe())如果总金额与文件描述相差过大回头看是哪些行的清洗规则把数据误删了。到这一步数据准备工作才算结束接下来才能谈模型。3. 超市建模问题的三种常见数学模型与选择许多建模新手拿到数据就直接套神经网络这在大赛里往往不是最优解。超市类的运营问题多数可以用带约束的数学规划模型描述。根据题目要回答的问题我把常见建模需求归成三类你可以对照自己的数据特征选择入口。3.1 需求预测与安全库存先从时间序列入手如果题目给了各商品长达数月的日销售数据要你决定未来一周的补货量那本质是“需求预测 安全库存”问题。预测部分可以用statsmodels里的 ETS 或 SARIMAimport numpy as np from statsmodels.tsa.holtwinters import ExponentialSmoothing series daily_sales.set_index(销售日期)[标准数量].resample(D).sum().fillna(0) model ExponentialSmoothing( series, trendadd, seasonaladd, seasonal_periods7, # 周季节性超市数据最常见 initialization_methodestimated, ) fit model.fit(optimizedTrue) forecast fit.forecast(7) print(forecast)这里的关键参数是seasonal_periods7因为超市销售以周为周期非常明显周一和周末的销量可能差出一倍。为什么要用指数平滑而不是直接取平均值因为指数平滑能同时捕捉水平、趋势和季节性而平均值会抹平周末峰值。如果你发现数据存在明显的节假日效应比如春节前两周销量异常那就应该在模型中加入回归变量或者加一个节假日哑变量不能只靠时间序列硬拟。安全库存的计算公式是SS z * sigma_d * sqrt(L)其中z对应服务水平95% 服务水平取 1.65sigma_d是日需求标准差L是补货提前期天。所以预测只是第一步补货量 预测值 安全库存 - 当前库存。3.2 整数规划与选址/排班决策变量的构建“新建仓库选址”“给收银员排班”“促销品项选择”这类问题适合用整数线性规划MILP。模型的核心是定义好决策变量、约束和目标函数。以收银员排班为例需求是每个小时必须有不少于预测客流对应的收银台开放数每种班次有不同的成本需要决定每天每个班次安排多少人。用pulp实现先装包pip install pulp代码框架如下import pulp hours range(24) shift_options [(0, 8), (8, 12), (12, 18), (18, 24)] # 班次起止 staff_cost {s: 100 for s in shift_options} demand_per_hour [2, 1, 1, ...] # 从流量模型得到 prob pulp.LpProblem(Shift_Scheduling, pulp.LpMinimize) x pulp.LpVariable.dicts(x, shift_options, lowBound0, catInteger) prob pulp.lpSum(staff_cost[s] * x[s] for s in shift_options) for h in hours: prob pulp.lpSum(x[s] for s in shift_options if s[0] h s[1]) demand_per_hour[h] status prob.solve(pulp.PULP_CBC_CMD(timeLimit60, gapRel0.05)) print(pulp.LpStatus[status]) for s in shift_options: print(s, x[s].value())catInteger把决策变量声明为整数注意不能用连续变量直接取整那会导致约束被破坏。timeLimit60是求解器最多跑 60 秒gapRel0.05允许 5% 的相对最优间隙。超市排班的规模通常很小几十个班次改用scipy.optimize.milp也行但pulp的建模语法更接近纸面模型竞赛答辩时也更容易解释。3.3 用历史数据做仿真事件驱动模型如果题目要你评估“如果新开一个促销活动对客流和销量的影响”或者研究收银排队时长的分布这些场景很难用解析公式精确表达更适合用仿真。超市仿真可以简单到用 Python 的simpy模拟顾客到达、服务台动作、排队等待import simpy import random arrival_interval 1.5 # 平均到达间隔分钟 service_mean 2.0 # 平均服务时长分钟 counters 3 # 收银台数量 def customer(env, name, service_start, service_end): with counters.request() as req: yield req yield env.timeout(random.expovariate(1 / service_mean)) env simpy.Environment() counters simpy.Resource(env, capacitycounters) for i in range(500): env.process(customer(env, i, ...)) env.timeout(random.expovariate(1 / arrival_interval)) env.run()这里的arrival_interval和service_mean都要从数据里估计不能瞎填。仿真模型的好处是能输出排队长度、等待时间分布等指标但坏处是参数敏感性高需要跑多次取平均。对建模竞赛来说仿真通常作为验证手段而不是唯一目标所以建议把仿真输出和前面的数学规划结果对照用两者的一致性给答案加分。4. 把模型跑起来求解器选型与参数调优模型和代码写得再漂亮求解器不收敛就等于白干。这一节要解决三件事该用哪个求解器、哪些参数最值得调、求解失败时按什么步骤排查。你从热搜词里看到“2026数学建模c题”“华为杯研究生数学建模”很多人卡在超时或内存溢出基本都是没有控制求解器行为。4.1 本地求解器 vs 云端的取舍中小规模模型决策变量少于 10 万直接在本地用开源的PULP_CBC_CMD或scipy.optimize.milp完全够用。如果模型很大比如全国门店的 SKU 级补货计划本地开源求解器会跑得很吃力这时候有两种解决路径换用高性能闭源求解器比如 Gurobi 或 CPLEX它们有学术免费授权但对数据规模仍然存在内存瓶颈。在云上部署一个求解容器用服务器的线程数换时间但要注意把数据预处理也在云上进行否则传输一直是瓶颈。我个人的经验是在初版模型阶段永远先用小型子集比如选 3 家门店、10 种商品验证模型逻辑再扩展到全量数据。不要一上来就跑全量省下的时间可以用来做更多轮调参。4.2 三个必调参数时间限制、MIP 间隙、可行性容差无论你用什么求解器下面三个参数的逻辑是通用的参数常见求解器写法作用推荐初始值时间限制timeLimit60超过 60 秒后返回当前最优解60~300 秒MIP 间隙gapRel0.05允许的最优解与下界之间相对差距0.01~0.05越小越精确但越慢可行性容差feasTol1e-6判断一个解是否可行时允许偏离约束的阈值默认即可不要轻易调大gapRel是模型规模上去后最值得调的参数。如果你发现解的质量在 30 秒后几乎不再优化那说明 5% 的间隙已经可以接受。feasTol一般不要动调大会导致解在纸面上可行实际业务中却无法执行。如果你用 Gurobi写入参数的方式是env gp.Env(emptyTrue) env.setParam(TimeLimit, 120) env.setParam(MIPGap, 0.02) env.setParam(FeasibilityTol, 1e-6) env.start()4.3 灵敏度分析与求解失败时的排查步骤模型跑完不是终点竞赛评委和业务主管都会问同一个问题“如果某个参数变了结果还成立吗”灵敏度分析就是把目标函数系数、约束右端项分别微调观察最优解的变化。以补货模型为例把提前期从 2 天改成 3 天补货量是否大幅改变如果改变太大说明模型对提前期过度敏感那你在报告里就要强调这一风险。当遇到infeasible不可行时最常见的两个原因是约束冲突和数据单位错误。排查顺序可以按下面的步骤打印模型状态确认是Infeasible还是Unbounded。用prob.writeLP(debug.lp)导出模型文件人工检查约束系数。逐条注释掉约束找到导致不可行的那一组。检查是否有商品在统计期间无任何销量却被约束要求补货量必须大于 0。在pulp中prob.writeLP是最快的调试工具它输出的线性规划文件可以直接打开查看比在代码里print每个约束高效得多。5. 验证与可视化你的模型是否真的可解释建模题目通常会要求你给出最优值但只给数字是不够的。你需要证明你的解不是过拟合还要能顺着图表把业务逻辑讲清楚。这个环节在竞赛里往往决定能不能从二等奖跳到一等奖。用滚动预测和图表是两种最实用的验证办法。5.1 用滚动预测检验模型稳定性以库存优化为例把时间序列前 80% 作为训练集后 20% 作为测试集然后让模型在训练集上迭代多次每次把预测窗口向前滚动一步。代码逻辑import numpy as np from sklearn.metrics import mean_absolute_error test_days 30 errors [] for offset in range(test_days): train_end len(series) - test_days offset train series.iloc[:train_end] # 这里用前面封装的预测函数内部重新拟合模型 pred forecast_one_step(train, days1) true series.iloc[train_end] errors.append(abs(pred - true)) print(fMAE: {np.mean(errors):.2f})注意每次滚动都要重新拟合模型而不是一次拟合后反复预测。重新拟合的目的是模拟真实业务里每天拿到新数据后的更新过程。如果误差随着滚动逐渐变大说明模型对远期数据不稳定需要重新检查特征。5.2 可视化呈现最优决策与业务指标用matplotlib画两张图就够一张是实际销量与预测销量的对比曲线另一张是补货量随时间的阶梯图。关键在于标注出节假日或促销事件让读者一眼看到模型对事件响应的合理性。import matplotlib.pyplot as plt plt.figure(figsize(10, 4)) plt.plot(test_index, true_values, label实际) plt.plot(test_index, forecast_values, label预测, linestyle--) for event in promotion_dates: plt.axvline(event, colorgray, alpha0.5) plt.ylabel(销量) plt.legend() plt.tight_layout() plt.savefig(forecast_check.png, dpi150)dpi150是为了保证论文中的清晰度。图片不要用默认配色评委看你图片的第一眼质量会直接影响印象分。除了图还可以加一个小的汇总表格展示每个门店的预测误差、建议补货量、库存周转天数表格放在论文附录里作为支撑。6. 从一份 rar 到一份能交的建模答案一整套脚本化流程最后这步把前面的零散动作串成一个自动化流程做到“拿到新压缩包一条命令跑出结果”。这能极大节省比赛后期反复处理数据的时间也对工程师自建数据分析流水线有参考价值。推荐在解压后的根目录放一个run_all.py按阶段调用函数python run_all.py --input Sams-.rar --solver cbc --time-limit 60run_all.py内部依次执行解压、清洗、建模、求解、画图、生成摘要表并把每一步日志写到run.log。关键代码如下import argparse, subprocess, os def main(): parser argparse.ArgumentParser() parser.add_argument(--input, requiredTrue) parser.add_argument(--solver, defaultcbc) parser.add_argument(--time-limit, typeint, default60) args parser.parse_args() os.system(funar -o {args.input} sams_data/) os.system(python data_clean.py) os.system(fpython optimize.py --solver {args.solver} --time-limit {args.time_limit}) os.system(python visualize.py) print(分析与可视化完成。) if __name__ __main__: main()这样一来的好处是如果竞赛中途换了题目数据你只需要把新的 rar 路径传入数据清洗和模型训练都会自动跑一遍。即使不是竞赛场景这种把业务数据压缩包、清洗脚本、优化模型、可视化输出串联起来的模式也是企业里做决策支持系统的标准雏形。最后检查一下生成的所有图表和数值表确保保留在dist/目录下然后就可以直接整合到论文或汇报材料里了。本文还有配套的精品资源点击获取