多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

数学建模竞赛C题实战:从数据预处理到定价补货优化模型全解析

数学建模竞赛C题实战:从数据预处理到定价补货优化模型全解析 1. 项目概述从“解题”到“建模”的思维跃迁每年九月的全国大学生数学建模竞赛国赛对于很多理工科学生而言不亚于一场“学术高考”。C题作为国赛的经典题型通常聚焦于数据分析、优化决策或复杂系统建模其特点是背景贴近现实、数据量大、问题开放性强对参赛者的综合能力提出了极高要求。很多新手队伍拿到题目后往往陷入“有思路但无从下手有数据但不会处理有模型但不会求解”的困境。这篇内容就是为你准备的“破局指南”。我将以一个拥有多年指导经验的“老建模人”视角为你深度拆解2023年国赛C题假设其为一个典型的“蔬菜类商品定价与补货决策”问题的完整解决路径。这不仅仅是一份“思路代码数据”的罗列更是一次从问题理解、模型构建、算法实现到论文写作的全流程实战复盘。无论你是初次参赛的小白还是希望提升成绩的老手都能从中找到可直接“抄作业”的模块化方案和那些只有踩过坑才知道的宝贵经验。2. 赛题核心剖析与解题总纲设计2.1 题目背景与问题重述别急着建模先当好“翻译官”拿到赛题第一步不是找公式而是做“阅读理解”。以典型的C题为例题目通常会提供数万条甚至更多的历史销售数据涉及多个品类、多个市场的蔬菜商品要求你建立定价与补货模型以实现商超利润最大化、损耗最小化等目标。关键动作问题重述。这不是简单地复述题目而是用你自己的、更结构化、更量化的语言重新定义问题。例如原题可能说“根据历史销售和定价制定未来补货策略”。你的重述应该是“本问题旨在建立一个多目标优化模型其决策变量为未来N天各品类在各门店的日补货量x_ijk和日定价p_ijk目标函数为总利润最大化max Profit与总损耗最小化min Loss的加权和约束条件包括最大陈列量、最小展示量、供应商供货能力、价格波动范围等。” 这个过程是将模糊的自然语言转化为清晰的数学语言是后续所有工作的基石。常见误区与心得误区一忽略隐含约束。题目说“保证正常销售”隐含了“每日补货量前日库存 ≥ 预测日销量”的约束。题目说“定价需考虑市场接受度”可能隐含了价格弹性系数或与竞争对手价格的关联。心得拿出一张白纸逐字逐句地画线标注。把所有名词如“损耗”、“销量”、“定价”列出来思考它们之间的数学关系。把所有的动词如“预测”、“制定”、“优化”列出来思考它们对应的模型方法。这个清单就是你论文第一部分的雏形。2.2 整体解题思路框架四步走战略面对复杂问题一个清晰的顶层设计能让你团队的三天高效运转。我推荐“四步走”框架数据预处理与特征工程第1天上午-下午这是所有分析的基础。处理缺失值、异常值进行数据可视化挖掘销量与价格、时间、品类、节假日等的潜在关系生成新的特征变量如周销量均值、价格环比、是否为周末等。核心模型构建第1天晚上-第2天全天这是攻坚阶段。通常包含两个子模型预测模型用于预测未来每天各品类在各门店的需求量。这是补货和定价决策的依据。优化模型在预测需求的基础上以利润最大化为目标求解最优的补货量和定价方案。模型求解与结果分析第3天上午利用编程工具如MATLAB、Python求解优化模型得到决策方案。并对结果进行敏感性分析如成本变化对利润的影响、稳健性检验如数据轻微扰动下方案是否稳定。论文撰写与整合贯穿全程第3天下午集中冲刺论文是最终交付物。从第一天起就要同步记录思路、过程和中间结果。最后半天进行整合、润色、图表美化。注意这个时间轴非常理想化。实际中模型构建和求解往往会遇到意想不到的困难必须预留出调整和备选方案的时间。我建议在第一天结束前必须完成数据预处理并确定至少一个可运行的预测模型原型。3. 数据预处理与特征工程实战详解3.1 数据清洗给数据“洗个澡”拿到的销售数据通常是“脏”的。常见问题包括日期格式不统一、销量为负值可能是退货或录入错误、价格为零或极高极低、大量缺失值等。Python实操示例使用Pandasimport pandas as pd import numpy as np # 假设df是原始销售数据框 # 1. 日期标准化 df[销售日期] pd.to_datetime(df[销售日期], format%Y/%m/%d, errorscoerce) # 2. 处理异常值假设我们认为日销量大于1000或小于0为异常 def correct_sales_outliers(series): Q1 series.quantile(0.25) Q3 series.quantile(0.75) IQR Q3 - Q1 lower_bound Q1 - 1.5 * IQR upper_bound Q3 1.5 * IQR # 将异常值替换为上下边界值或置为NaN后续用均值/中位数填充 series_clipped series.clip(lower_bound, upper_bound) return series_clipped df[销量(千克)] df.groupby([单品编码, 门店编码])[销量(千克)].transform(correct_sales_outliers) # 3. 处理缺失值对于数值列用分组中位数填充更稳健 df[销量(千克)] df.groupby([单品编码, 门店编码])[销量(千克)].transform(lambda x: x.fillna(x.median())) df[销售单价(元/千克)] df.groupby(单品编码)[销售单价(元/千克)].transform(lambda x: x.fillna(x.median())) # 4. 创建基础特征 df[星期几] df[销售日期].dt.dayofweek # 周一0, 周日6 df[是否周末] df[星期几].isin([5, 6]).astype(int) df[月份] df[销售日期].dt.month df[年份] df[销售日期].dt.year避坑指南不要盲目删除缺失值尤其是时间序列数据删除会导致序列断裂。优先使用前向填充ffill、后向填充bfill或插值法。对于横向比较的数据使用同类均值/中位数填充更合理。异常值处理要谨慎。先用箱线图或3σ原则识别然后分析异常原因。如果是录入错误则修正如果是特殊事件如促销导致可以考虑单独建模或将其视为正常波动。直接删除可能会损失重要信息。3.2 特征工程挖掘数据的“潜在价值”原始数据字段有限我们需要创造更有预测力的特征。滞后特征Lag Features对于时间序列预测过去的值是最好的预测因子。可以创建过去1天、7天一周、30天一个月的销量、价格均值作为新特征。df[销量_滞后1天] df.groupby([单品编码, 门店编码])[销量(千克)].shift(1) df[销量_7天均值] df.groupby([单品编码, 门店编码])[销量(千克)].transform(lambda x: x.rolling(7, min_periods1).mean())统计特征计算每个单品的历史平均销量、销量标准差、价格中位数等作为该品类的固有属性。交互特征考虑品类之间的关联。例如西红柿和鸡蛋的销量可能存在正相关。可以计算品类组合的联合销售指数。外部特征如果题目允许或提供加入天气数据温度、降雨量、节假日标志春节、国庆、促销活动标志等能极大提升预测精度。提示特征不是越多越好。过多的特征会导致模型过拟合和计算负担。建议先基于业务理解如蔬菜销售受季节、周末影响大构造核心特征在模型训练后通过特征重要性排序如使用树模型来筛选关键特征。4. 核心模型构建预测与优化双轮驱动4.1 需求预测模型选型与实现预测未来需求是第一步。对于此类具有明显时序性、且受多个因素影响的销量预测单一模型往往力有不逮。方案一传统时间序列模型ARIMA/Prophet适用场景数据序列相对平稳周期性明显如周周期外部影响因素较少或可量化。优点理论成熟解释性强对线性趋势和周期捕捉好。缺点对多变量输入支持较弱处理大量品类需为每个单品单独建模时工作量巨大。实操建议可作为基线模型Baseline用于快速验证和对比。对于国赛C题这种海量单品的数据不建议作为主力模型。方案二机器学习模型LightGBM/XGBoost适用场景当前主流选择。特别适合处理表格数据能自动捕捉特征间的复杂非线性关系对类别特征、缺失值友好。优点预测精度高训练速度快能方便地融入大量手工特征。缺点模型可解释性相对较差需要仔细调参。Python实现核心步骤import lightgbm as lgb from sklearn.model_selection import train_test_split from sklearn.metrics import mean_absolute_error, mean_squared_error # 假设 feature_df 是构造好的特征DataFrametarget是‘销量(千克)’ # 划分训练集和测试集按时间划分避免数据泄露 train_data feature_df[feature_df[销售日期] 2023-06-01] test_data feature_df[feature_df[销售日期] 2023-06-01] X_train train_data.drop([销量(千克), 销售日期], axis1) y_train train_data[销量(千克)] X_test test_data.drop([销量(千克), 销售日期], axis1) y_test test_data[销量(千克)] # 创建并训练模型 params { objective: regression, metric: mae, boosting_type: gbdt, num_leaves: 31, learning_rate: 0.05, feature_fraction: 0.9, verbose: -1 } lgb_train lgb.Dataset(X_train, y_train) gbm lgb.train(params, lgb_train, num_boost_round100) # 预测与评估 y_pred gbm.predict(X_test) print(f测试集MAE: {mean_absolute_error(y_test, y_pred):.2f})方案三深度学习模型LSTM/Transformer适用场景数据量极大序列长期依赖关系复杂且团队有较强的深度学习背景。优点能自动学习特征对超长序列建模能力强。缺点训练时间长调参复杂需要大量数据容易过拟合结果不易解释。参赛建议国赛三天时间非常紧张除非有现成代码和充足经验否则不建议轻易尝试深度学习模型风险较高。我的选择与心得在国赛的高压环境下我强烈推荐LightGBM作为主力预测模型。它的效率、精度和易用性达到了最佳平衡。关键点在于特征工程和损失函数选择。对于销量预测由于可能存在长尾分布少数日子销量极高使用MAE平均绝对误差作为损失函数比MSE均方误差更稳健因为MAE对异常值不那么敏感。4.2 定价与补货联合优化模型在获得需求预测D(p)需求是价格p的函数后我们进入核心的优化阶段。这是一个典型的非线性规划问题。模型构建决策变量x_i(第i个商品的补货量)p_i(第i个商品的销售单价)。目标函数最大化总利润Maximize Σ [ p_i * min(D_i(p_i), x_i) - c_i * x_i - h_i * max(x_i - D_i(p_i), 0) - s_i * max(D_i(p_i) - x_i, 0) ]c_i: 单位进货成本h_i: 单位库存持有成本或损耗成本s_i: 单位缺货损失成本机会成本min(D_i, x_i)是实际销售量max(x_i - D_i, 0)是期末库存可能产生损耗max(D_i - x_i, 0)是缺货量约束条件补货量约束L_i ≤ x_i ≤ U_i供应商供货能力或仓库容量限制价格约束P_min_i ≤ p_i ≤ P_max_i市场定价范围需求函数D_i(p_i) f(p_i, 其他特征)即上一节预测模型给出的结果。这里通常假设需求与价格呈线性或指数负相关即D_i(p_i) a_i - b_i * p_i线性或D_i(p_i) A_i * p_i^(-e_i)指数e_i为价格弹性系数。系数a_i, b_i或A_i, e_i可以通过对历史数据进行回归分析得到。模型求解策略这是一个带约束的非线性优化问题决策变量可能成百上千品类×门店。直接求解全局最优解非常困难。实用化的简化与求解方法解耦与迭代优化由于定价影响需求补货基于需求两者耦合。可以采用迭代法步骤1定价假设补货量充足即销售量等于需求量对每个商品利润函数简化为π_i(p_i) (p_i - c_i) * D_i(p_i)。通过求导或一维搜索可以快速求出使单个商品利润最大化的价格p_i*。这是一个经典的“报童模型”定价扩展。步骤2补货将上一步得到的最优价格p_i*代入需求函数得到预测需求量D_i*。此时考虑补货约束和损耗/缺货成本问题转化为一个随机规划或鲁棒优化问题。一个常用的简化是使用“报童模型”公式计算最优补货量x_i* F_i^{-1}( (p_i - c_i s_i) / (p_i - c_i h_i s_i) )其中F_i是需求D_i*的概率分布函数通常假设为正态分布。这个公式平衡了多进货导致的损耗风险和少进货导致的缺货风险。步骤3迭代用新的补货量x_i*修正销售预期可能反过来微调价格。通常迭代1-2次后结果就会稳定。使用现成优化求解器将上述模型非线性目标线性约束形式化后可以使用专业的优化库求解如Python的SciPy.optimize适用于中小规模、PuLP线性/整数规划或更强大的商业求解器Gurobi、CPLEX的接口它们也能处理部分非线性问题。这需要较强的数学建模和编程能力。# 使用SciPy进行简化版优化的伪代码示意 from scipy.optimize import minimize def total_profit(variables): # variables是一个数组前半部分是价格p后半部分是补货量x p variables[:n_products] x variables[n_products:] demand demand_prediction_model(p) # 根据价格预测需求 sales np.minimum(demand, x) inventory np.maximum(x - demand, 0) shortage np.maximum(demand - x, 0) profit np.sum(p * sales - cost * x - holding_cost * inventory - shortage_cost * shortage) return -profit # 因为minimize是求最小所以加负号 # 定义约束价格上下限补货上下限 bounds [(p_min, p_max) for _ in range(n_products)] [(x_min, x_max) for _ in range(n_products)] # 初始猜测 initial_guess np.array([(p_minp_max)/2 for _ in range(n_products)] [x_min for _ in range(n_products)]) result minimize(total_profit, initial_guess, boundsbounds, methodL-BFGS-B) optimal_solution result.x心得与技巧先分后合对于成百上千的商品直接整体优化计算量太大。可以先按品类或销售特性聚类对每个类代表商品进行精细优化同类其他商品按比例分配策略。敏感性分析至关重要在论文中必须分析关键参数如进货成本c、损耗成本h、需求预测误差变化时你的最优策略和最终利润如何变化。这能体现模型的鲁棒性是重要的加分项。例如可以设定成本上涨10%重新运行模型观察利润下降百分比。可视化决策结果将优化后的补货计划表和价格表用热力图的形式展示出来品类×时间。评委一眼就能看出你的策略是否合理例如是否在周末前增加了高需求品的补货。5. 编程实现、论文写作与团队协作要点5.1 代码组织与数据管理三天时间混乱的代码是灾难。必须从开始就建立规范。项目结构CUMCM2023_C/ ├── data/ # 存放所有数据文件 │ ├── raw/ # 原始数据只读永不修改 │ ├── processed/ # 清洗后的数据 │ └── features/ # 生成的特征文件 ├── src/ # 源代码 │ ├── 01_data_preprocessing.py │ ├── 02_feature_engineering.py │ ├── 03_demand_forecasting.py │ ├── 04_optimization_model.py │ └── utils.py # 自定义工具函数 ├── output/ # 输出结果 │ ├── figures/ # 生成的图表 │ └── results/ # 模型结果、预测文件 ├── paper/ # 论文LaTeX或Word源文件 └── README.md # 项目说明使用Jupyter Notebook要谨慎Notebook适合探索性分析但最终一定要将稳定的代码整理成.py脚本方便模块化调用和调试。可以用Notbook做原型开发用脚本进行最终批量运行。数据管道化确保每个步骤的输入输出明确。例如特征工程脚本读取data/processed/cleaned_data.csv输出data/features/engineered_features.csv。这样当调整某个步骤时不需要从头运行。5.2 论文写作你的“终极产品”论文是唯一评分依据。务必做到结构清晰、逻辑自洽、图文并茂、重点突出。摘要重中之重评委首先看摘要。要用300-500字浓缩全部精华针对什么问题、用了什么方法、建立了什么模型、采用了什么算法、得到了什么结果、有何特色与创新。避免空洞描述多用量化语言“建立了基于LightGBM的需求预测模型预测误差MAE降低至X.XX在此基础上构建了定价-补货联合非线性规划模型采用迭代优化法求解使得商超在2023年7-8月预期总利润提升约XX%。”模型假设与符号说明假设要合理且明确如“假设短期内蔬菜进货成本不变”、“假设各门店需求独立”。符号说明表格要规范、完整。模型建立与求解这是核心章节。不要只扔公式要解释为什么用这个公式。例如在建立需求预测模型时先说明“考虑到销量受价格、时间、品类等多因素非线性影响而LightGBM模型擅长处理此类问题...”然后再给出模型形式。求解部分要说明算法流程可以配流程图。结果分析与检验展示关键图表并对图表进行解释而不是简单地说“如图所示”。进行敏感性分析和稳健性检验。例如“图5显示当生菜进货成本上涨20%时最优利润下降约8%表明模型对该成本参数较为敏感建议商超重点关注此类易波动商品的供应链。”模型评价与推广客观评价自己模型的优点考虑因素全面、求解效率高和缺点未考虑突发天气、假设需求函数形式固定等并提出改进方向。将模型推广到其他类似场景如水果、日用品定价。5.3 团队协作与时间管理角色定位经典三人组合理想分工是建模手主攻模型构建与理论推导、编程手主攻数据清洗、算法实现与求解、写手主攻论文撰写、图表绘制与润色。但三人必须紧密沟通互相了解对方进度。每日站会每天早中晚固定时间快速同步过去几个小时做了什么遇到了什么问题接下来几个小时计划做什么确保信息同步避免重复劳动或方向偏离。版本管理至少要对论文和核心代码使用Git进行版本控制。避免“final_v2_真的最终版.docx”的悲剧。保底策略在第二天结束前无论模型多完美都必须得到一个“能用”的结果并开始撰写论文初稿。最后一天是用来打磨、美化、做敏感性分析而不是从零开始构建新模型。6. 常见问题排查与实战技巧锦囊6.1 预测模型效果不佳怎么办症状预测误差MAE/RMSE很大或者预测曲线完全跟不上真实波动。排查步骤检查数据泄露这是最常见错误确保在构造特征如滞后特征、移动平均和划分训练集/测试集时严格按时间顺序进行绝对不能用未来的信息预测过去。测试集的时间必须晚于训练集。检查特征有效性画出特征与目标变量的散点图或计算相关性。如果特征与目标无关果断剔除。增加更有力的特征如节假日、促销标志。调整模型参数对于LightGBM重点调整num_leaves控制模型复杂度、learning_rate学习率配合n_estimators使用、min_data_in_leaf防止过拟合。使用网格搜索GridSearchCV或贝叶斯优化进行调参。尝试模型融合将LightGBM的预测结果和简单的时序模型如历史同期均值的结果进行加权平均有时能提升稳健性。6.2 优化模型求解太慢或无法收敛症状程序运行几小时没结果或者报错提示无法找到可行解。排查与解决缩小问题规模先对几个代表性品类或单个门店进行求解验证模型逻辑和代码的正确性。简化模型如果使用非线性规划求解器检查目标函数和约束是否过于复杂。能否将某些非线性部分线性化能否将连续变量离散化如价格只取几个档位检查约束冲突“无可行解”往往意味着约束条件互相矛盾。例如要求利润高于100万但给定的价格上限和成本下理论最大利润只有80万。放松一些约束或检查约束的数学表达式是否正确。提供更好的初始值优化算法对初始值敏感。用启发式方法如2.2节提到的迭代法先算出一个较好的解作为初始猜测值提供给求解器能大大加快收敛速度。6.3 论文图表怎么做才专业原则一图胜千言但糟糕的图不如无图。工具Python的MatplotlibSeaborn组合是首选美观且可控。避免使用Excel默认图表样式显得不够专业。技巧折线图趋势用于展示销量、价格随时间的变化。多条线时线型、颜色要区分明显并添加图例。热力图矩阵用于展示不同品类、不同门店的销量/利润矩阵非常直观。箱线图分布用于对比不同品类或不同时间段销量的分布情况可以看出中位数、离散程度和异常值。子图对比将多个相关图表放在一起方便对比。例如用子图分别展示预测销量和实际销量。务必标注每个图都必须有清晰的标题、坐标轴标签含单位、图例。图中重要的点或趋势可以用文字箭头或文本框在图中直接说明。配色使用简洁、对比度高的配色方案。可以使用Seaborn的默认主题或viridis、plasma等色盲友好的配色映射。最后三天是智力、体力和协作能力的综合考验。保持冷静遇到卡点及时与队友讨论或切换思路牢牢抓住“问题分析-模型建立-求解验证-论文表达”这条主线。记住一个完整、自洽、表述清晰的解决方案远比一个追求极致复杂但漏洞百出、无法实现的“完美模型”更能打动评委。祝你在国赛的舞台上把这三天的汗水凝结成一份让自己骄傲的答案。
返回列表