资讯详情

数学建模C题:供应链优化与供应商评价全流程代码解析

📅 2026/10/21 6:00:20 | 华诺云谱 👁 阅读
数学建模C题:供应链优化与供应商评价全流程代码解析
简介面向全国大学生数学建模竞赛参赛者及想系统学习建模实战的学习者这份2021年C题完整代码资料包覆盖了从问题理解、模型假设、建立方程、求解验证到结果分析的完整流程。包内共27个文件压缩后仅996KB其中13个.m脚本为MATLAB解题代码按题目小节编号命名便于对照问题定位10个.mat文件保存中间结果或辅助数据便于复现计算4个.xlsx表格则整理了供应商、转运商近5年数据以及最终的订购、转运方案结果。已有2310人学习下载适合用来复盘竞赛思路、对照代码理解模型构建与算法实现。通过逐一研读这些文件可以直接看到数学理论如何转化为可运行的计算过程尤其适合备赛冲刺或日常提升数学建模与MATLAB编程能力配套的Excel数据也可直接用于数据清洗与可视化分析。1. 项目定位一道供应链优化赛题把评价和规划全链路代码收齐了2021年数学建模竞赛C题生产企业原材料的订购与运输是一道典型的供应链优化题某生产企业每周要消耗A、B、C三类原材料50家供应商供货、8家转运商承运手里握着402周的历史订货与供货数据要回答四件事——选最重要的24家供应商、确定最少需要的转运商数量、设计每周的订购方案和转运方案。这份资料把从数据清洗、供应商评价、整数规划到结果出图的全部代码收在一起拿到手就能复现当年的完整解题流程。适合两类人备战数学建模竞赛想研究C题标准解法的学生以及做供应商管理、物流调度的一线开发评价体系和规划模型可以直接移植到业务里改。2. 赛题解读与建模思路从50家供应商里选出24家再定转运方案2.1 数据文件与赛题背景先看数据。赛题给了四个附件Excel 里装的是以周为单位的历史生产数据不是模拟数据附件内容维度附件150家供应商近402周的订货量50 × 402附件250家供应商近402周的供货量50 × 402附件38家转运商近402周的运输量8 × 402附件4生产企业每周产能与原材料分类按周 按类别402周大约是8年的数据。我第一眼看到附件1和附件2时以为只是同一个表复制了两份仔细比对才发现不是——订货量是企业每周向供应商下的订单供货量是供应商实际交付的数量。这两个量经常对不上而它们之间的差值恰恰是整个赛题的核心矛盾企业按需求订货供应商却经常供不满生产计划随时被打乱。附件4里把原材料分成A、B、C三类每类对应不同的供应商群。生产企业的产能是固定的每周需要多少原材料基本由产能倒推。赛题把产能—订货—供货—转运四个环节串成一条链任何一个环节掉链子下一周的生产就要缺料。2.2 问题一供应商评价的指标体系怎么设计问题一要求从50家供应商里选24家最重要的。难点不在算法在重要这个词怎么定义。很多参赛队上来只算总供货量排序取前24家结果后续问题做不下去——有些供应商供货量大但极不稳定一半周数不供货还有些供应商供货量中等但每单都足额交付。常规做法是建一套多维指标体系再找一个综合评价方法把多维压成一维排序。我一般用四个维度供货能力总供货量、平均周供货量供货可靠性供货量/订货量的满足率以及满足率低于阈值的周数占比供货稳定性周供货量的变异系数越小越稳供货持续性连续供货周数、断供次数。维度确定后把指标量化、标准化再用熵权法自动算权重最后用TOPSIS排序。熵权法的好处是不用拍脑袋定权重它根据各指标的离散程度分配权重——数据波动越大的指标权重越高。在赛题里这恰好合理供货稳定性在50家供应商之间差异很大熵权法会自然把它顶上去。2.3 问题二与问题三从评价到规划的递进问题二把难度往上提了一截在24家供应商选定之后要设计每周的订购方案和转运方案使得进货量尽量满足生产目标同时转运商数量越少越好。这一问是典型的混合整数规划——订多少、向谁订、让哪家转运商运三个决策变量叠在一起还是逐周滚动决策。问题三更贴近现实转运商每周的运输量存在上限且运输过程有损耗。于是问题二算出来的纸面方案直接作废规划模型里要加入转运商容量约束和损耗修正。从问题二到问题三本质是给模型加了两组约束目标函数和决策变量的骨架没变。这里有个关键技巧问题三的损耗率不要按周平均要按转运商分别统计。某转运商平均损耗率5%但有的周损耗0%、有的周损耗15%用平均值算出来的缺口会和用逐周损耗算出来的差一大截。这道题很多队伍翻车就翻在这里。3. 核心模块拆解数据清洗、熵权TOPSIS与整数规划的完整实现整个工程的目录结构大致如下C2021/ ├── data/ │ ├── 附件1_供应商订货量.xlsx │ ├── 附件2_供应商供货量.xlsx │ ├── 附件3_转运商运输量.xlsx │ └── 附件4_企业产能与原材料分类.xlsx ├── code/ │ ├── 01_data_clean.py │ ├── 02_evaluate.py │ ├── 03_plan.py │ └── 04_visualize.py └── output/ ├── supplier_ranking.xlsx ├── plan_result.xlsx └── *.png3.1 数据预处理订货量和供货量必须先对齐再算数据清洗是所有后续步骤的地基。附件1和附件2的表头是第1周第2周这样的文本供应商ID是混合类型的字符串直接用pandas读出来会造成列名无法按数字排序、供应商ID对齐错位两个坑。第一步先统一格式import pandas as pd # header1 跳过第一行说明文字 order_df pd.read_excel(data/附件1_供应商订货量.xlsx, header1) supply_df pd.read_excel(data/附件2_供应商供货量.xlsx, header1) # 供应商ID统一转字符串并补零对齐 order_df[供应商ID] order_df[供应商ID].astype(str).str.zfill(2) supply_df[供应商ID] supply_df[供应商ID].astype(str).str.zfill(2) # 列名重命名为 W1W402 order_df.columns [供应商ID] [fW{i} for i in range(1, order_df.shape[1])] supply_df.columns [供应商ID] [fW{i} for i in range(1, supply_df.shape[1])] # 以供应商ID为键对齐 order_df order_df.set_index(供应商ID).sort_index() supply_df supply_df.set_index(供应商ID).sort_index() print(order_df.shape, supply_df.shape) # (50, 402) (50, 402)这段代码有两个关键细节。zfill(2)是把1补成01否则两个表合并时1和01会被当成两个供应商列名重命名为W1W402是为了后面能按周切片如果保留第1周这种文本列名做df[W1:W52]这种时序切片会很别扭。两个表用set_index(供应商ID)对齐后后续算满足率就是逐元素除法不会出现行错位。3.2 熵权法 TOPSIS供应商评价的核心实现评价模块是整个资料里复用价值最高的部分。先构建指标矩阵每一行是一家供应商每一列是一个指标然后熵权法定权重TOPSIS 算综合得分import numpy as np def entropy_weight(mat): # mat: n×mn家供应商m个指标要求已正向化越大越好 p mat / mat.sum(axis0, keepdimsTrue) p np.clip(p, 1e-12, 1) # 防止 log(0) e -(p * np.log(p)).sum(axis0) / np.log(mat.shape[0]) w (1 - e) / (1 - e).sum() # 信息熵越大 - 权重越小 return w def topsis_score(mat, weights): # 向量归一化 norm mat / np.sqrt((mat**2).sum(axis0, keepdimsTrue)) weighted norm * weights best weighted.max(axis0) # 正理想解 worst weighted.min(axis0) # 负理想解 d_best np.sqrt(((weighted - best)**2).sum(axis1)) d_worst np.sqrt(((weighted - worst)**2).sum(axis1)) return d_worst / (d_best d_worst) # 得分越接近1越优逻辑说明熵权法先归一化成概率矩阵再算每个指标的信息熵。信息熵小说明该指标在供应商之间差异大、携带的信息多权重就高反之差异小的指标权重低。这正好解决了供货稳定性到底占多少权重这种拍脑袋问题。TOPSIS 则是找出一个虚拟的最优供应商每项指标都取所有供应商里的最大值和一个虚拟的最差供应商然后算每家供应商到这两个虚拟点的距离离最优近、离最差远的得分高。参数上要注意两点一是所有指标必须先正向化比如断供次数是越小越好的逆向指标要用1/(1x)或取倒数转成正向二是熵权法里np.clip(p, 1e-12, 1)不能省否则某家供应商某指标为0时log(0)会算出NaN权重直接全盘崩掉。3.3 整数规划订购与转运方案怎么同时定问题二、三的建模用 PuLP 实现它是开源的线性规划库自带求解器开箱即用。核心模型是决策变量为0-1的转运商选择变量和连续型的订购量、运输量约束为每周需求满足、转运商容量上限目标分两段——先最小化转运商数量再在固定转运商集合下最小化缺货量from pulp import LpProblem, LpMinimize, LpVariable, lpSum, LpBinary, LpStatus def build_plan(demand, capacity, loss_rate, max_transporters8, weeks52): prob LpProblem(min_shortage, LpMinimize) # y[i][t]: 转运商i第t周是否启用 y {(i, t): LpVariable(fy_{i}_{t}, catLpBinary) for i in range(max_transporters) for t in range(weeks)} # q[i][t]: 转运商i第t周实际运输量扣除损耗前 q {(i, t): LpVariable(fq_{i}_{t}, lowBound0) for i in range(max_transporters) for t in range(weeks)} # 目标缺货量最小化 shortage LpVariable(shortage, lowBound0) prob shortage # 每周转运总量含损耗 需求 - shortage for t in range(weeks): prob lpSum(q[i, t] * (1 - loss_rate[i]) for i in range(max_transporters)) \ demand[t] - shortage for i in range(max_transporters): prob q[i, t] capacity[i] * y[i, t] # 不启用则不能运 prob.solve() return LpStatus[prob.status], shortage.varValue, y, q逻辑说明shortage是一个松弛变量它把必须完全满足需求这种容易无解的硬约束改成了缺货量越小越好的软约束模型几乎不会跑挂。q[i, t] capacity[i] * y[i, t]这一行是关键——它把连续变量 q 和0-1变量 y 绑在一起转运商不启用时运输量强制为0启用时不能超过容量。损耗率折算放在约束左侧q * (1 - loss_rate)是实际到货量扣完损耗还要够需求。跑完第一步拿到最少转运商数量 K 后把模型改一下prob lpSum(y[i, t] for i in range(...)) K重新求解就得到具体每周订购与运输方案。两步走比一次性把最少转运商和最小缺货写进同一个目标要稳因为两个目标量纲不同加权系数很难调容易顾此失彼。3.4 可视化输出让论文直接用上结果竞赛论文讲究图表化表达资料里最后一步是把排名和方案导出成表和图import matplotlib.pyplot as plt top24 rank_df.sort_values(score, ascendingFalse).head(24) fig, ax plt.subplots(figsize(10, 6)) ax.barh(top24[供应商ID], top24[score], color#4C72B0) ax.invert_yaxis() ax.set_xlabel(TOPSIS 综合得分) ax.set_ylabel(供应商 ID) ax.set_title(供应商重要性排序 Top24) plt.tight_layout() plt.savefig(output/supplier_top24.png, dpi300)运行后 output 目录下会生成 supplier_top24.png 这样的论文级图片dpi300 是印刷精度直接插入论文不会糊。同期还会用to_excel把排名表写成 Excel方便在表格里继续调格式。提示竞赛论文评审很看重图表规范性建议所有图统一字体、统一配色横纵坐标一定要带物理单位。4. 运行方法与参数调优三步跑通全流程并调出合理结果4.1 环境准备与目录说明这份资料基于 Python 3.8 以上的环境依赖就五个包pip install pandas numpy openpyxl matplotlib pulp scipyopenpyxl 是 pandas 读写 .xlsx 的后端引擎不装的话read_excel会直接报错。所有数据文件放在 data/ 目录代码按 01 到 04 编号依次执行。如果在 Windows 上跑注意路径别用中文文件夹名pandas 和 matplotlib 在部分环境下对中文路径兼容性不好容易莫名报编码错误。4.2 三步跑通全流程执行顺序是从数据到结论的依赖链不能跳cd code python 01_data_clean.py # 生成清洗后的 supplier_order.csv、supplier_supply.csv python 02_evaluate.py # 生成 supplier_ranking.xlsx 和 top24 名单 python 03_plan.py # 生成订购与转运方案 plan_result.xlsx python 04_visualize.py # 生成论文用图每步都会在控制台打印关键中间量清洗后矩阵形状、熵权结果、TOP24名单、最少转运商数量、平均缺货率。我一般先跑 01确认输出的 CSV 维度是 50×402 再往下走——如果维度不对后面全白跑这是血泪经验。4.3 关键参数怎么调跑通只是第一步结果要合理还得调参数。列几个最容易影响结果的点参数位置影响调法建议熵权法 clip 值02_evaluate.py权重稳定性1e-12 是经验值调成 1e-8 权重会有细微变化无伤大雅满足率阈值02_evaluate.py可靠性指标默认 80%可试 70%/90% 观察排名变化规划周期 weeks03_plan.py求解规模52 周全量求解慢可先跑 4 周验证模型再放开损耗率 loss_rate03_plan.py方案可行性建议按转运商逐周统计不要用全年平均缺货松弛 shortage03_plan.py方案激进程度默认软约束缺货率在 2%~5% 之间算合理这里重点说下周期的坑。build_plan里weeks52是全量规划自带求解器要跑几分钟到十几分钟调试阶段建议先设weeks4确认模型无冲突再改回 52。如果赛题需要 402 周全量方案别硬怼整数规划——改成滚动窗口每次优化 4 周滑 1 周往前推进402 周只要 100 多次小规模求解速度和稳定性都好得多。注意评判方案是否合理先看缺货率而不是看运输量大小。缺货率超过 10% 说明模型约束或供应商集合有问题优先检查损耗率计算是否把不供货周也算进了平均值。5. 避坑与常见问题数据对齐、NaN 和求解器翻车现场这一章是复现这套代码时实际踩过的坑每条都按现象 → 原因 → 解决记录。5.1 read_excel 读出来列名全是第1周计算直接崩现象order_df[第1周] 1这种操作报错或者列排序变成第100周排在第2周前面。原因附件表头是文本而不是数字字符串排序按字典序100排在2前时序切片全乱。解决读入后立即重命名列为 W1W402用df.filter(regex^W)取数。这一步放在数据清洗最前面别拖到建模时再处理。5.2 熵权法输出全是 NaN现象entropy_weight返回的权重数组全是 nan。原因某指标在某供应商处为 0归一化后 p0log(0)产生 -inf再参与运算就是 NaN。解决p np.clip(p, 1e-12, 1)先截断再做 log。这是熵权法最常见的玄学翻车点不是公式错了是数值计算边界没处理。5.3 PuLP 求解提示 Infeasible现象prob.solve()返回Infeasible方案全无。原因约束太紧常见于把每周需求必须完全满足写成硬约束而实际数据里供应商总产能根本不够。解决引入shortage松弛变量把硬约束改成软约束。如果还想保留硬约束语义就先把需求侧按 ABC 三类拆开检查——很多时候是 A 类原材料单独不够不是总量不够。5.4 排名结果和别人差很多榜单对不上现象同一份数据自己算的 TOP24 和参考论文差 5 家以上。原因指标定义不一致。比如满足率有人按供货量/订货量逐周算平均有人按总供货量/总订货量直接除两种算法在供货量波动大时排名差异明显。解决统一定义并在论文里写明公式。我一般用逐周满足率再求平均这样能体现每周是否稳定交付而不是被某几周的大额供货拉高。5.5 matplotlib 出图中文字体变方块现象图片标题中文全是空心方块。原因matplotlib 默认字体不含中文字形。解决绘图前设置字体Linux 下用plt.rcParams[font.sans-serif] [WenQuanYi Zen Hei]Windows 下用[SimHei]再加一句plt.rcParams[axes.unicode_minus] False处理负号显示。6. 二次开发改造把 C 题评价框架迁移到自己的数据上这套代码里复用价值最高的是评价模块——熵权法 TOPSIS 是不依赖赛题语境的通用排序框架任何从一堆候选里选一批最优的业务都能套。改造成供应商绩效评分工具时只需要动三处指标矩阵从写死改成从 CSV 读、指标列表改成配置文件、输出从 Excel 改成 Excel PDF 报告。具体改法把mat的构建抽成一个函数build_feature_matrix(df, feature_config)feature_config 是一个 dict每个指标对应一个计算函数和方向正向/逆向。这样换业务数据时只需要加指标函数不用动熵权和 TOPSIS 的数学实现。验证方法也很直接用 402 周历史数据做回测按模型选出的 TOP24 去算过去一年的平均缺货率和随机选 24 家的缺货率对比差 3 个百分点以上说明模型有效。如果想把规划模型也搬走建议把 PuLP 换成 scipy 的milp接口或商用求解器自带求解器在小规模够用业务数据一上规模就吃力。另外我习惯每次改完都跑一遍python 01_data_clean.py python 02_evaluate.py确认数据流没断。从那以后我每次改数据脚本都强制先跑一遍 01→02 全链路确认中间文件格式没变再动模型。数学建模赛题最怕的就是模型公式都对结果因为前面某一步数据格式变了而全盘作废。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑