TOPSIS法
TOPSIS(Technique for Order Preference by Similarity to an Ideal Solution)法,中文名"优劣解距离法",由 C. L. Hwang 和 K. Yoon 于 1981 年提出,是多属性决策分析(MADM)中最经典的排序方法之一。它的核心思想极其朴素:最好的方案应该离"正理想解"(所有指标都取最优值的虚构方案)最近,同时离"负理想解"(所有指标都取最差值的虚构方案)最远。TOPSIS 对数据分布、样本量没有要求,计算简单、几何意义直观,而且与 AHP、熵权法天然互补——前者负责"定权重",TOPSIS 负责"排顺序",是数学建模竞赛评价类题目的黄金搭档。本文从原理、适用场景、评价指标、可视化图表到可运行代码,完整梳理 TOPSIS 的竞赛实战用法。
一、算法含义
1.1 通俗理解
假设你是一家制造企业的采购经理,要从 A、B、C、D 四家供应商中选一家长期合作,考察 5 个指标:价格、质量合格率、交货准时率、距离、服务评分。直接比较很头疼,因为:
- 方向不一致:价格、距离是"越小越好"(极小型指标),质量、准时率、服务是"越大越好"(极大型指标),不能直接加总;
- 量纲不一致:价格是"万元"(十几到二十几),合格率是"百分比"(0.85
0.98),距离是"公里"(90550),如果直接加权求和,距离的几百会"淹没"合格率的零点几; - 往往没有"全优选手":A 厂最便宜但距离最远,B 厂质量服务最好但价格最贵,怎么取舍?
TOPSIS 的思路分两步:
- 统一方向与量纲:先正向化(把极小型、中间型、区间型都变成"越大越好"),再标准化(消除量纲),最后加权(体现指标重要性差异);
- 虚拟两个"标杆":虚构一个"正理想解" ——每个指标都取所有方案中的最优值(哪怕现实中不存在这样的方案);再虚构一个"负理想解" ——每个指标都取所有方案中的最差值。然后测量每个方案到这两个标杆的欧氏距离:离 越近、离 越远的方案越好。
把"离正理想近"和"离负理想远"两个目标合成一个 0~1 之间的数,就是相对贴近度 ,按 从大到小排序,就得到最终推荐顺序。所以 TOPSIS 的本质是:把"多指标择优"转化为"多维空间里谁离最优虚拟点最近"的几何问题。
1.2 数学模型:决策矩阵
设共有 个待评价方案(本文例: 家供应商), 个评价指标(本文例:),原始数据排成决策矩阵:
其中 表示第 个方案在第 个指标上的取值。另外还需要一个权重向量 ,满足 、,由 AHP(主观)或熵权法(客观)事先确定(见 2.5 节)。本文示例的权重由 AHP 判断矩阵求出:。
1.3 算法七步详解
TOPSIS 的标准流程共七步(定权作为第 0 步预备),下面按步给出公式,并配本文示例数据的具体数值。
第 0 步(预备):确定权重 。权重可以用 AHP、熵权法、Delphi 法或主客观组合赋权得到,TOPSIS 本身不生成权重、只使用权重。本文示例用 AHP 判断矩阵的最大特征向量法定权(代码见第六节),得到 ,一致性比例 ,通过一致性检验。
1.3.1 ① 指标正向化(把方向统一为"越大越好")
正向化是 TOPSIS 最容易被忽略、也最容易出错的一步:只有所有指标都统一成"越大越好"后,后面的"每列取最大值作为正理想解"才成立。常见的四类指标及正向化公式:
| 指标类型 | 含义 | 竞赛常见例子 | 正向化公式要点 |
|---|---|---|---|
| 极大型(效益型) | 越大越好 | 质量合格率、服务评分、GDP | min-max 归一化 |
| 极小型(成本型) | 越小越好 | 价格、距离、成本、污染指数 | 用"最大减原值"翻方向 |
| 中间型 | 越接近某个最优值越好 | 水质 pH 越接近 7 越好 | 按偏离最优值的程度线性衰减 |
| 区间型 | 落在区间 内最好 | 体温 36~37℃、湿度 40%~60% | 区间内取 1,区间外线性衰减 |
(1)极大型指标(本来就越大越好,只需做 min-max 归一化压缩量纲):
本例质量合格率一列:(B 厂)、(D 厂),A 厂 ,B 厂取到 。
(2)极小型指标(越小越好,把方向"翻过来"):
注意分子是 而不是 ——这是全篇最常记错的公式。本例价格一列(越小越好):(B 厂)、(A 厂),A 厂 (最便宜 → 正向化后最大),B 厂 (最贵 → 正向化后最小)。距离一列同理:C 厂 550 km 最远 → ,B 厂 90 km 最近 → 。
(3)中间型指标(越接近最优值 越好):
其中 是所有样本与最优值的最大偏离。例如水质 pH 数据 ,最优值 ,最大偏离 ,则正向化结果为 :pH 恰好等于 7 的样本取 1,偏离最远的 9.0 取 0。
(4)区间型指标(落在区间 内最好,如体温 36~37℃):
其中 是"所有样本离区间边界的最远距离"。例如体温数据 ,最优区间 ,则 ,正向化结果为 :区间内的 36.5 取 1,偏离最远的 38.2 取 0。
正向化完成后得到正向化矩阵 ,所有元素都在 内且统一为"越大越好"。本文示例的正向化矩阵(见 7.1 节完整输出):
1.3.2 ② 标准化(向量归一化,消除量纲)
正向化只统一了方向,但各指标的数值尺度仍可能不同(虽然 min-max 已压到 0~1,但更标准的做法是再做一步向量归一化,保证每个指标"贡献的平方和"相等):
z_{ij} = \frac{x'_{ij}}{\sqrt{\sum_{i=1}^{n} x'_{ij}^{\,2}}}
即第 列的每个元素除以该列的欧氏范数。归一化后每列平方和为 1,各指标在距离公式中的地位只由权重 决定,量纲彻底消除。注意:TOPSIS 通常用这种"向量归一化",而不是 z-score(标准化到均值 0 方差 1)——两者都能消量纲,但向量归一化保留比例关系、不引入负值,是 TOPSIS 的标准做法。本文示例中,价格列正向化值 的范数为 ,故 A 厂 ,与 7.1 节输出一致。
1.3.3 ③ 加权标准化矩阵
把权重乘进去,得到加权标准化矩阵 :
权重越大,该指标在距离计算中的"话语权"越大。例如 B 厂质量一列:。
1.3.4 ④ 确定正理想解 与负理想解
因为前面已正向化(所有指标都是"越大越好"),所以正、负理想解的定义统一为逐列取最大 / 最小:
两个要点:
- 是"虚构的完美方案":它每列取到的最大值往往来自不同方案(本例中价格最优来自 A 厂,其余四列最优都来自 B 厂),现实中并不存在;但它给出了"每个指标都做到最好"的参照点。
- 若忘记正向化,则极小型指标的正确理想解方向要反过来(极小型取 作 、取 作 ),一旦和正向化后的定义混用,结果完全错误——这就是"常见坑 1、2"的来源。
本文示例的加权标准化矩阵 与正负理想解(见 7.1 节完整输出):
本例 是全 0 向量——因为正向化用的 min-max 保证每列至少有一个 0(该列最差方案),加权后仍是 0。这不是巧合而是 min-max 正向化的必然结果,解读时要注意(见 7.3 节坑 6)。
1.3.5 ⑤ 计算各方案到理想解的欧氏距离
是方案 到正理想解的欧氏距离(越小越好), 是到负理想解的欧氏距离(越大越好)。本文示例(见 7.1 节输出):B 厂 (最小)、(最大),双优;D 厂 (最大)、(最小),双劣。
1.3.6 ⑥ 计算相对贴近度(综合得分)
- 若方案与 完全重合(),则 ;
- 若方案与 完全重合(),则 ;
- 越大,说明方案离正理想解相对越近、离负理想解相对越远,综合表现越好。
为什么用"比值"而不是单独用 ?因为只比 会漏掉"离负理想解多远"的信息:两个方案可能到 距离相同,但一个紧贴 、一个远离 ,显然后者更稳健、更应排在前面。 把"近优"与"离劣"两个目标合成一个 0~1 的数。本文示例:B 厂 ,为四家最高。
1.3.7 ⑦ 按贴近度排序
把 从大到小排列,即得最终排序; 最大者为最优方案。本文示例排序:B 厂(0.6929)> A 厂(0.4953)> C 厂(0.4185)> D 厂(0.2747),推荐选择 B 厂。
1.4 优缺点
优点:
- 原理直观:"离最好的近、离最差的远",一句话讲清,几何意义明确,评审一眼看懂;
- 计算简单、无超参数:不需要迭代、不需要调参、不需要训练集,公式全部是四则运算和开方,Excel 都能算;
- 对数据几乎没要求:不要求样本量、不要求分布假设(区别于回归类方法的诸多假设),指标多少都能算;
- 输入输出规整:输入"决策矩阵 + 权重",输出"0~1 的得分 + 完整排序",天然适合写进论文;
- 扩展性好:换权重(熵权、组合赋权)、换距离(马氏距离、灰色关联)就能派生出大量变体(见第八节),给竞赛论文留下创新空间;
- 与定权方法解耦:AHP、熵权法负责定权,TOPSIS 只负责排序,分工清晰。
缺点:
- 权重外生:TOPSIS 自身不给权重,权重质量直接决定结果质量;主观定权争议大,客观定权(熵权)又可能违背常识;
- 对指标相关敏感:欧氏距离默认各指标独立正交,指标强相关时(如"GDP"与"人均收入")相关方向会被重复计权,结果偏向这些指标,需改用马氏距离或先降维;
- 结果受标准化与正向化方式影响:min-max 正向化的结果依赖样本范围(样本变了,正向化值就变),结论是"相对"而非"绝对"的;
- C 值只是排序依据: 的大小没有绝对意义( 不代表"方案有 90 分好"),只能用于排序和相对比较;
- 新方案加入需整体重算:加一个新方案,min-max 的极值、理想解、所有 C 值都会变(排名可能"翻盘");
- 不考虑决策者风险偏好:TOPSIS 假设"离理想解越近越好"是唯一准则,不区分"平均表现好"与"短板严重"(关心短板时应改用 VIKOR)。
二、何时使用(适用场景与条件)
2.1 适用场景
- 多方案综合评价与排序:TOPSIS 的"本命"场景是——有若干候选对象、每个对象有多个指标、需要给出一个优劣顺序或最优推荐。典型如:供应商选择(价格/质量/准时率/距离/服务)、城市竞争力评价(GDP/环境/教育/医疗/房价)、楼盘选购、车型比较、员工绩效评定、旅游目的地选择。
- 竞赛中的评价类题目:数学建模竞赛中"评优、评选、排序、推荐"类题目几乎必然用到评价模型,TOPSIS 与 AHP、熵权法、模糊综合评价一起构成"评价模型四大件"。TOPSIS 常作为排序引擎出现:先用 AHP/熵权法定权,再用 TOPSIS 排序出结果。
- 有明确"理想标杆"的决策:当题目本身隐含"最好是什么样、最差是什么样"(如最优供应商、最宜居城市、最安全方案)时,TOPSIS 的"贴近理想解"思路与题目天然契合。
- 多目标优化问题求解后方案挑选:多目标优化(如遗传算法求 Pareto 解集)得到的候选解往往很多,可以用 TOPSIS 在 Pareto 前沿上选出"折中最优"的那一个,这是 TOPSIS 在优化类题目中的经典用法。
- 作为对比基准:竞赛论文中常把"简单加权总分法"作为基线,与 TOPSIS(或改进 TOPSIS)对比,说明自己方法排序更合理、区分度更高。
2.2 使用前提(建模前检查清单)
| 检查项 | 具体要求 | 检查手段 |
|---|---|---|
| 指标可量化 | 所有指标都能用数值表示 | 定性指标先量化(专家打分、模糊评语转隶属度) |
| 指标方向明确 | 每个指标明确属于极大型/极小型/中间型/区间型 | 逐个指标判断"越大越好还是越小越好" |
| 权重已确定 | 有一组合法权重 , | 用 AHP(主观)/熵权法(客观)/组合赋权求出 |
| 量纲不统一 | 指标间量纲差异大(必须走正向化 + 标准化) | 观察决策矩阵各列取值范围 |
| 方案数足够 | 一般 , 时结论退化为直接比较 | 数方案个数 |
| 指标数适中 | 一般 ,过多时先降维或分层 | 相关指标可先用主成分/因子分析合并 |
| 数据完整 | 无缺失值(有缺失先插补) | pandas 查 isnull().sum() |
| 指标独立性 | 指标间尽量不高度相关(相关系数 < 0.9) | 画相关系数热力图;强相关考虑马氏距离 TOPSIS |
竞赛提示:TOPSIS 没有"假设检验",所以检查清单通过率要求不高;但论文中至少要说清"指标怎么选的、方向怎么定的、权重怎么来的",这三件事交代清楚,评价模型就立住了。
2.3 不适用 / 慎用的情形
- 指标无法量化:如"企业文化""品牌美誉度"这类纯定性指标过多且难以打分时,TOPSIS 无从算起,应改用模糊综合评价(把定性评价用隶属度向量表示)。
- 指标间强相关:欧氏距离默认指标正交,两个高度相关的指标(相关系数接近 ±1)等于被重复计权,会放大该方向的差异。处理:删减指标、主成分降维后再 TOPSIS,或改用马氏距离 TOPSIS。
- 决策者关心"短板":TOPSIS 用"总分"排序,一个指标极差的方案可能靠其他指标"拉回"排名;若决策者要求"各指标都不能太差"(如安全指标一票否决),应改用 VIKOR(多准则妥协解)或先做一票否决筛选。
- 只有两个方案:两方案时排序结论恒等于"比 D+/D− 谁更好",TOPSIS 的多余框架没有意义。
- 需要绝对结论而非相对排名:TOPSIS 输出的是"相对排序",样本集合变了排名可能变;若题目要求绝对评级(如"哪些城市达到宜居标准"),应配合阈值判定或模糊综合评价。
- 动态、多阶段决策:TOPSIS 是静态单期方法,跨期、多阶段的动态评价需要时序 TOPSIS 变体或其他动态评价方法。
- 权重完全无法确定且争议巨大:TOPSIS 对权重敏感,若连定权依据都没有,结果会被质疑"拍脑袋";至少应做权重敏感性分析(见 7.4 节)。
2.4 与 AHP / 熵权法 / 模糊综合评价的组合
TOPSIS 在竞赛中很少单独出现,"定权 + 排序"的组合是标准打法:
| 组合方法 | 分工 | 典型应用 | 优点 / 局限 |
|---|---|---|---|
| AHP + TOPSIS | AHP 定权重(主观),TOPSIS 排序 | 有专家经验、指标重要性有共识的评价题 | 权重有道理、解释性强;主观性受质疑 |
| 熵权法 + TOPSIS | 熵权法定权重(客观),TOPSIS 排序 | 数据完整、指标取值差异明显的评价题 | 完全由数据说话;可能违背业务常识(重要但差异小的指标权重低) |
| AHP-熵权组合 + TOPSIS | 主客观权重组合(如 ),TOPSIS 排序 | 既要专家经验又要数据支撑的题目 | 折中最稳妥,是论文常见创新点 |
| 模糊综合评价 + TOPSIS | 定性指标先用模糊隶属度量化,再 TOPSIS 排序 | 指标多为"好/中/差"等评语的评价题 | 处理模糊边界;隶属度函数设定要说明 |
| 灰色关联 + TOPSIS | 灰色关联度与欧氏距离结合成灰色关联贴近度 | 小样本、信息不完备的评价题 | 对贫信息稳健;见第八节 |
| 多目标优化 + TOPSIS | 优化求 Pareto 解集,TOPSIS 选折中解 | 多目标优化类题目收尾 | 把"求出一堆解"变成"给出一个推荐" |
选择原则(竞赛实战):有专家判断 → AHP 定权;数据充分 → 熵权定权;两者都有 → 组合赋权;再交给 TOPSIS 排序。论文里写"先用 AHP(熵权法)确定指标权重,再用 TOPSIS 对方案进行综合排序",一句话就把两大方法的职责讲清楚,评审非常买账。
三、算法指标
下面每个指标都给出:中文名、公式、取值范围、方向(越大/越小越好)、如何解读,并配本文示例(4 家供应商)的实际数值。符号定义见第五节。
3.1 到正理想解的距离
- 取值范围:,无量纲(标准化后);
- 方向:越小越好, 表示方案在每个指标上都达到全样本最优(与 重合);
- 解读:方案 与"完美虚构方案"的加权欧氏距离,衡量"离最好还有多远"。本文示例:B 厂 最小(只在价格一维落后于 A 厂,其余四维全部达到正理想值),D 厂 最大。
3.2 到负理想解的距离
- 取值范围:,无量纲;
- 方向:越大越好, 表示方案在每个指标上都是全样本最差(与 重合);
- 解读:方案 与"最差虚构方案"的距离,衡量"比最差好多少"——它体现方案的稳健性:离负理想解越远,说明方案没有明显拖后腿的维度。本文示例:B 厂 最大,D 厂 最小。
3.3 相对贴近度
- 取值范围:,无量纲;
- 方向:越大越好; 当且仅当方案与正理想解重合, 当且仅当与负理想解重合;
- 解读:把"离正理想近( 小)"和"离负理想远( 大)"两个目标合成的综合排序得分,是 TOPSIS 的最终输出。注意三点:一是 只用于排序和相对比较,没有绝对意义( 不代表"69 分");二是 对应 ,表示方案恰好位于正负理想解"正中间",不是及格线;三是按 排序与按 排序完全等价(单调变换),论文两种写法都见过。本文示例:B 厂 ,为最优。
3.4 最终排序(排名)
- 定义:将 从大到小排列,第 1 名为最优方案;
- 解读:本文示例排序为 B 厂(0.6929)> A 厂(0.4953)> C 厂(0.4185)> D 厂(0.2747)。注意:排序只反映"相对优劣",相邻名次之间差距多大要看 值之差——B 厂领先第二名 A 厂约 0.20,优势明显;A 厂与 C 厂差距约 0.08,属"同一档"。
- 并列处理:若两个方案 完全相同(计算精度内),名次并列,论文中应说明。
3.5 指标汇总表
| 指标 | 中文名 | 公式 | 取值范围 | 方向 | 一句话解读 |
|---|---|---|---|---|---|
| 到正理想解的距离 | 越小越好 | 离"最优虚构方案"还有多远 | |||
| 到负理想解的距离 | 越大越好 | 比"最差虚构方案"好多少(稳健性) | |||
| 相对贴近度(综合得分) | 越大越好 | 近优 + 离劣的合成得分,排序依据 | |||
| 排名 | 最终排序 | 降序 | 越小越好 | 最终决策结论 |
四、可视化图表
TOPSIS 的结论是排序,但"排名是怎么来的"必须用图讲清楚。以下 4 张图覆盖"几何原理展示 + 最终结论 + 距离分解 + 数据预处理效果",全部代码见第六节,图片自动保存到 figures/ 目录。
4.1 四张图速查表
| 图名(输出文件) | 用途 | 关键解读点 |
|---|---|---|
① 二维属性空间散点图(topsis_scatter.png) | 取加权标准化矩阵的两个维度(价格、质量)把方案与正负理想解画在同一平面,直观展示"离 近、离 远"的几何原理 | 红色星 在右上(质量最优),绿色星 在原点附近;B 厂紧贴 (仅价格一维偏离),D 厂离 最远;离红星近、离绿星远的方案越好 |
② 贴近度排序条形图(topsis_ranking.png) | 展示最终结论:各方案 从高到低排列 | 柱长即 ,第一名红色突出;本例 B 厂(0.6929)> A 厂(0.4953)> C 厂(0.4185)> D 厂(0.2747),首末差距约 0.42,区分度良好 |
③ / 距离对比分组柱状图(topsis_distance.png) | 把贴近度拆回两个距离分量,解释"为什么它赢/输" | 红柱()越矮越好,绿柱()越高越好;B 厂双优( 最小且 最大),D 厂双劣;A 厂两项居中但 占优,故排第二 |
④ 正向化前后数据对比面板(topsis_forward_compare.png) | 展示正向化消除量纲、统一方向的效果,说明预处理必要性 | 左图量纲差异巨大(价格 18 |
4.2 每张图的解读要点
图① 二维属性空间散点图
- 好图特征:最优方案应出现在 附近;若某方案离 比离 还近,说明它"烂到家了"(本例 D 厂即离绿星更近)。
- 注意:二维图只是"高维空间的降维投影",只选了前两个指标作坐标,图上的欧氏距离 ≠ 真实的五维 。论文中应注明"取其中两个代表性维度作示意"。
- 读图技巧:把红绿两星连成一条线,各方案在这条线上的"投影位置"大致对应贴近度高低。
图② 贴近度排序条形图
- 好图特征:条形单调下降、数值标注清晰、第一名与最后一名差距明显(区分度好)。
- 异常特征:所有柱长几乎相等 → 各方案 接近,说明指标区分度低或权重过于平均,结论"脆弱"(权重微调就可能翻盘),论文中应补敏感性分析。
图③ / 距离对比分组柱状图
- 好图特征:"红短绿长"的方案排在前面;两柱高度差越大,贴近度越高。
- 读图技巧:,所以只看"红绿比"就能大致判断排序——B 厂红色远短于绿色,稳居第一;D 厂红色远长于绿色,垫底。
图④ 正向化前后数据对比面板
- 好图特征:左图展示"不可比"(量纲差异巨大、方向不一致),右图展示"可比"(都在 0~1 且越大越好),左右对比即正向化的全部意义。
- 论文用法:这张图放在"数据预处理"小节,能直观说明"为什么不能直接加权求和",是评价模型论文的加分细节。
五、符号说明
| 符号 | 含义 | 示例/单位 |
|---|---|---|
| 方案(评价对象)个数 | (4 家供应商) | |
| 评价指标个数 | (价格/质量/准时率/距离/服务) | |
| 第 个方案在第 个指标上的原始值 | 价格 18 | |
| 原始决策矩阵 | ,量纲各异 | |
| 正向化后的值(统一为越大越好) | ||
| 正向化矩阵 | ||
| 中间型指标的最优值 | 水质 pH 的最优值 7.0 | |
| 区间型指标的最优区间 | 体温 36~37℃ | |
| 正向化公式中的最大偏离 | 中间型 | |
| 标准化后的值 | z_{ij} = x'_{ij} / \sqrt{\sum_i x'_{ij}^2},无量纲 | |
| 标准化矩阵 | ||
| 第 个指标的权重 | ;本例 | |
| 加权标准化值 | ,无量纲 | |
| 加权标准化矩阵 | ||
| 正理想解(虚构的最优方案) | ||
| 负理想解(虚构的最差方案) | ||
| 方案 到正理想解的欧氏距离 | 本例 B 厂 0.1701,无量纲 | |
| 方案 到负理想解的欧氏距离 | 本例 B 厂 0.3840,无量纲 | |
| 相对贴近度(综合得分) | ||
| AHP 判断矩阵最大特征值 | 本例 5.0331 | |
| AHP 一致性指标 | ,本例 0.0083 | |
| AHP 一致性比例 | ,本例 0.0074( 通过) | |
| 方案下标、指标下标 | ; |
六、可运行程序(完整代码)
环境要求:Python 3.12,依赖 numpy、scipy、scikit-learn、matplotlib、pandas(
pip install numpy scipy scikit-learn matplotlib pandas)。说明:scikit-learn 没有 TOPSIS 接口——TOPSIS 是多准则决策方法,没有"训练/预测"的概念,因此本程序不调用 sklearn,对照部分改用 pandas/numpy 向量化写法与 scipy.cdist 验证手写结果。本文示例数据为固定数值(不含随机数),每次运行结果完全一致;若换成随机生成的数据,请先加np.random.seed(42)固定种子。以下所有代码块按顺序拼接保存为topsis_demo.py,在本文档所在目录运行即可:控制台打印全部指标,并在figures/子目录生成 4 张图。
# -*- coding: utf-8 -*-
"""
============================================================
TOPSIS 法(优劣解距离法)完整示例:手写实现 + 向量化对照
------------------------------------------------------------
案例:4 个供应商 × 5 个指标(价格/质量/准时率/距离/服务)
指标权重由 AHP 判断矩阵(最大特征向量法)确定
流程:正向化 → 标准化 → 加权 → 正负理想解 → 欧氏距离 → 贴近度 → 排序
输出:控制台打印全部指标 + figures/ 目录下 4 张图
依赖:numpy、scipy、scikit-learn、matplotlib、pandas
说明:scikit-learn 无 TOPSIS 接口(TOPSIS 是多准则决策方法,
无"训练"概念),故本脚本不涉及 sklearn,仅手写与向量化对照。
============================================================
"""
# ========== 0. 导入库与全局设置 ==========
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.linalg import eig # AHP 权重:一般矩阵的特征分解
from scipy.spatial.distance import cdist # 欧氏距离的 scipy 对照实现
# ---- matplotlib 中文显示设置(防止图内中文乱码)----
plt.rcParams["font.sans-serif"] = ["PingFang SC", "Arial Unicode MS", "SimHei"]
plt.rcParams["axes.unicode_minus"] = False # 让负号"-"正常显示
# ---- 图片输出目录(相对当前工作目录的 figures/ 子目录)----
FIG_DIR = "figures"
os.makedirs(FIG_DIR, exist_ok=True)
# ========== 1. 构造数据:4 个供应商 × 5 个指标 ==========
suppliers = ["供应商1(A厂)", "供应商2(B厂)", "供应商3(C厂)", "供应商4(D厂)"]
indicator_names = ["价格", "质量合格率", "交货准时率", "距离", "服务评分"]
indicator_types = ["min", "max", "max", "min", "max"] # 指标类型:min=极小型,max=极大型
# 原始决策矩阵 X:每一行是一个方案,每一列是一个指标(量纲各不相同!)
X = np.array([
[18.0, 0.90, 0.83, 320.0, 6.5], # A 厂:价格最低,但距离远、质量/服务一般
[25.0, 0.98, 0.94, 90.0, 9.5], # B 厂:价格最高,但质量/服务/距离全面领先
[23.0, 0.92, 0.74, 550.0, 6.0], # C 厂:价格偏高,准时率差、距离最远
[20.0, 0.85, 0.87, 250.0, 4.5], # D 厂:价格较低,但质量/服务垫底
])
n, m = X.shape # n = 4 个方案,m = 5 个指标
print(f"原始决策矩阵 X({n} 个方案 × {m} 个指标):")
print(pd.DataFrame(X, index=suppliers, columns=indicator_names))
# ========== 2. AHP 层次分析法确定指标权重 ==========
# 构造 5×5 判断矩阵 A:a_ij 表示指标 i 相对指标 j 的重要程度(1~9 标度)
# 本例:质量最重要(是价格的 2 倍、准时率的 3 倍、距离的 4 倍、服务的 2 倍),
# 价格与服务同等重要,准时率次之,距离最不重要
A = np.array([
[1, 1/2, 2, 3, 1], # 价格 vs 质量/准时率/距离/服务
[2, 1, 3, 4, 2], # 质量
[1/2, 1/3, 1, 2, 1/2], # 准时率
[1/3, 1/4, 1/2, 1, 1/3], # 距离
[1, 1/2, 2, 3, 1], # 服务
])
def ahp_weights(A):
"""AHP 定权:判断矩阵最大特征值对应的特征向量(归一化)即权重向量。"""
# 注意:Saaty 判断矩阵是"正互反矩阵"(a_ji = 1/a_ij),不是对称矩阵,
# 不能使用只读取半个三角的对称特征分解 eigh,必须用一般特征分解 eig
vals, vecs = eig(A)
idx = np.argmax(vals.real) # 最大特征值 λ_max 的下标
w = np.abs(vecs[:, idx].real) # 对应特征向量(Perron 定理保证其分量全为正)
w = w / w.sum() # 归一化,使权重和为 1
lam = vals[idx].real # 最大特征值 λ_max
n = A.shape[0]
CI = (lam - n) / (n - 1) # 一致性指标
RI = {1: 0, 2: 0, 3: 0.58, 4: 0.90, 5: 1.12, 6: 1.24, 7: 1.32, 8: 1.41, 9: 1.45}
CR = CI / RI[n] # 一致性比例,CR < 0.1 认为通过一致性检验
return w, lam, CI, CR
w, lam_max, CI, CR = ahp_weights(A)
print("\n【步骤 0】AHP 定权结果")
msg = "(CR < 0.10,通过一致性检验)" if CR < 0.1 else "(CR ≥ 0.10,判断矩阵需调整!)"
print(f"λ_max = {lam_max:.4f},CI = {CI:.4f},CR = {CR:.4f}" + msg)
print("指标权重 w =", np.round(w, 4), f"(Σw = {w.sum():.4f})")
# ========== 3. 手写实现:四类指标的正向化函数 ==========
# 正向化的目的:把所有指标统一成"越大越好"(极大型),同时用 min-max 压缩量纲差异
def forward_max(x):
"""极大型指标(越大越好,如质量、准时率):min-max 归一化。"""
return (x - np.min(x)) / (np.max(x) - np.min(x))
def forward_min(x):
"""极小型指标(越小越好,如价格、距离):(max - x) / (max - min)。"""
return (np.max(x) - x) / (np.max(x) - np.min(x))
def forward_mid(x, best):
"""中间型指标(越接近最优值 best 越好,如 pH 越接近 7 越好):
x' = 1 - |x - best| / max|x - best|。"""
M = np.max(np.abs(x - best))
return 1 - np.abs(x - best) / M
def forward_interval(x, a, b):
"""区间型指标(落在区间 [a, b] 内最好,如体温 36~37℃):
区间内 → 1;区间外按偏离程度线性衰减到 0。"""
M = max(a - np.min(x), np.max(x) - b)
y = np.ones_like(x, dtype=float) # 先假设全部落在区间内
y[x < a] = 1 - (a - x[x < a]) / M # 低于区间下界
y[x > b] = 1 - (x[x > b] - b) / M # 高于区间上界
return y
# ---- 正向化函数自检(中间型、区间型竞赛中也会考到)----
print("\n【正向化函数自检】")
ph = np.array([6.0, 6.5, 7.0, 7.5, 9.0])
print(f"中间型示例:水质 pH = {ph}(最优值 7.0)→ 正向化 {np.round(forward_mid(ph, 7.0), 4)}")
tem = np.array([35.0, 35.6, 36.5, 37.4, 38.2])
print(f"区间型示例:体温 = {tem}(最优区间 [36.0, 37.0])→ 正向化 {np.round(forward_interval(tem, 36.0, 37.0), 4)}")
# ========== 4. 手写实现:TOPSIS 全流程(七步) ==========
def topsis_handwritten(X, w, indicator_types, mid_best=None, interval_bounds=None):
"""手写 TOPSIS 完整流程,返回每一步的中间结果,方便对照文档公式。"""
n, m = X.shape
# ---- ① 正向化:按指标类型调用对应函数,得到正向化矩阵 X' ----
Xf = np.zeros_like(X, dtype=float)
for j in range(m):
t = indicator_types[j]
if t == "max":
Xf[:, j] = forward_max(X[:, j])
elif t == "min":
Xf[:, j] = forward_min(X[:, j])
elif t == "mid":
Xf[:, j] = forward_mid(X[:, j], mid_best[j])
elif t == "interval":
a, b = interval_bounds[j]
Xf[:, j] = forward_interval(X[:, j], a, b)
else:
raise ValueError(f"未知指标类型:{t}")
# ---- ② 标准化(向量归一化):z_ij = x'_ij / sqrt(Σ_i x'_ij²) ----
Z = np.zeros_like(Xf)
for j in range(m):
denom = np.sqrt(np.sum(Xf[:, j] ** 2)) # 第 j 列平方和的平方根
Z[:, j] = Xf[:, j] / denom
# ---- ③ 加权标准化矩阵:v_ij = w_j · z_ij ----
V = np.zeros_like(Z)
for j in range(m):
V[:, j] = Z[:, j] * w[j]
# ---- ④ 正理想解 Z+ 与负理想解 Z-(每列取最大 / 最小)----
Z_pos = np.max(V, axis=0) # z_j^+ = max_i v_ij:各指标上的"最优水平"
Z_neg = np.min(V, axis=0) # z_j^- = min_i v_ij:各指标上的"最差水平"
# ---- ⑤ 欧氏距离:D_i^+ = sqrt(Σ_j (v_ij − z_j^+)²),D_i^- 同理 ----
D_pos = np.zeros(n)
D_neg = np.zeros(n)
for i in range(n):
D_pos[i] = np.sqrt(np.sum((V[i] - Z_pos) ** 2))
D_neg[i] = np.sqrt(np.sum((V[i] - Z_neg) ** 2))
# ---- ⑥ 相对贴近度:C_i = D_i^- / (D_i^+ + D_i^-) ∈ [0, 1] ----
C = D_neg / (D_pos + D_neg)
# ---- ⑦ 排序:C 越大越好 ----
order = np.argsort(-C) # 降序排列的下标
rank = np.empty_like(order)
rank[order] = np.arange(1, n + 1) # 第 i 个方案的排名
return Xf, Z, V, Z_pos, Z_neg, D_pos, D_neg, C, rank
Xf, Z, V, Z_pos, Z_neg, D_pos, D_neg, C, rank = topsis_handwritten(X, w, indicator_types)
print("\n【步骤 ①】正向化矩阵 X'(所有指标统一为越大越好,取值 0~1):")
print(pd.DataFrame(Xf, index=suppliers, columns=indicator_names).round(4))
# ========== 5. pandas/numpy 向量化写法对照 ==========
# 说明:scikit-learn 没有 TOPSIS 接口——TOPSIS 是多准则决策(MCDM)方法,
# 没有"训练/预测"的概念,因此对照对象改为 pandas/numpy 向量化写法。
# 向量化版与第 4 段手写版数学上完全等价:用广播和 np.linalg.norm
# 替代显式 for 循环,代码更短、速度更快,适合指标很多的场景。
def topsis_vectorized(X, w, indicator_types, mid_best=None, interval_bounds=None):
"""向量化版 TOPSIS:正向化后全程用 numpy 广播,不再逐元素循环。"""
cols = []
for j, t in enumerate(indicator_types):
xj = X[:, j]
if t == "max":
cols.append(forward_max(xj))
elif t == "min":
cols.append(forward_min(xj))
elif t == "mid":
cols.append(forward_mid(xj, mid_best[j]))
elif t == "interval":
a, b = interval_bounds[j]
cols.append(forward_interval(xj, a, b))
Xf = np.column_stack(cols)
Z = Xf / np.linalg.norm(Xf, axis=0) # ② 标准化:整列同时除以列范数
V = Z * w # ③ 加权:每一行 × 权重向量(广播)
Z_pos = V.max(axis=0) # ④ 正理想解
Z_neg = V.min(axis=0) # ④ 负理想解
D_pos = np.linalg.norm(V - Z_pos, axis=1) # ⑤ 每一行到 Z+ 的欧氏距离
D_neg = np.linalg.norm(V - Z_neg, axis=1) # ⑤ 每一行到 Z- 的欧氏距离
C = D_neg / (D_pos + D_neg) # ⑥ 贴近度
order = np.argsort(-C) # ⑦ 排序
rank = np.empty_like(order)
rank[order] = np.arange(1, X.shape[0] + 1)
return D_pos, D_neg, C, rank
Dp2, Dn2, C2, rank2 = topsis_vectorized(X, w, indicator_types)
# ---- 等价性自检:两种写法结果应完全一致 ----
print("\n【等价性自检】手写循环 vs numpy 向量化(np.allclose 判定):")
print(f"D+ 一致:{np.allclose(D_pos, Dp2)};D- 一致:{np.allclose(D_neg, Dn2)};"
f"C 一致:{np.allclose(C, C2)};排名一致:{np.array_equal(rank, rank2)}")
# ---- scipy 对照:cdist 计算到正理想解的欧氏距离,与手写 sqrt(sum((v-z)^2)) 一致 ----
D_pos_scipy = cdist(V, Z_pos.reshape(1, -1), metric="euclidean").ravel()
print(f"D+ 手写 vs scipy.cdist 一致:{np.allclose(D_pos, D_pos_scipy)}")
# ========== 6. 打印第三节要求的所有指标 ==========
print("\n【步骤 ②】标准化矩阵 Z(z_ij = x'_ij / sqrt(Σ_i x'_ij²)):")
print(pd.DataFrame(Z, index=suppliers, columns=indicator_names).round(4))
print("\n【步骤 ③】加权标准化矩阵 V(v_ij = w_j · z_ij):")
print(pd.DataFrame(V, index=suppliers, columns=indicator_names).round(4))
print("\n【步骤 ④】正理想解 Z+:", np.round(Z_pos, 4))
print(" 负理想解 Z-:", np.round(Z_neg, 4))
print("\n【步骤 ⑤⑥】距离与贴近度:")
df_metric = pd.DataFrame({
"方案": suppliers,
"D+(到正理想解距离)": np.round(D_pos, 4),
"D-(到负理想解距离)": np.round(D_neg, 4),
"相对贴近度 C_i": np.round(C, 4),
"排名": rank.astype(int),
})
print(df_metric.to_string(index=False))
print("\n【步骤 ⑦】按贴近度从高到低排序:")
df_sorted = df_metric.sort_values("排名")
print(df_sorted[["方案", "D+(到正理想解距离)", "D-(到负理想解距离)", "相对贴近度 C_i", "排名"]].to_string(index=False))
best_idx = int(np.argmax(C))
print(f"\n结论:{suppliers[best_idx]} 最优,相对贴近度 C = {C[best_idx]:.4f}")
# ========== 7. 图① 二维属性空间散点图(topsis_scatter.png) ==========
# 取加权标准化矩阵 V 的前两列(价格、质量)作为二维坐标,
# 把 4 个方案和正/负理想解画在同一平面,直观展示"离 Z+ 近、离 Z- 远"的思想
fig, ax = plt.subplots(figsize=(7.5, 6.5))
ax.scatter(V[:, 0], V[:, 1], s=260, c="#2f6fb0", edgecolors="white",
linewidths=1.8, zorder=3, label="备选方案")
for i in range(n):
ax.annotate(suppliers[i], (V[i, 0], V[i, 1]),
textcoords="offset points", xytext=(12, 10), fontsize=11)
ax.scatter([Z_pos[0]], [Z_pos[1]], marker="*", s=700, c="#c0392b",
edgecolors="black", zorder=4, label="正理想解 Z+")
ax.scatter([Z_neg[0]], [Z_neg[1]], marker="*", s=700, c="#27ae60",
edgecolors="black", zorder=4, label="负理想解 Z-")
ax.annotate("Z+", (Z_pos[0], Z_pos[1]), textcoords="offset points", xytext=(10, -22),
fontsize=13, color="#c0392b", fontweight="bold")
ax.annotate("Z-", (Z_neg[0], Z_neg[1]), textcoords="offset points", xytext=(10, 10),
fontsize=13, color="#27ae60", fontweight="bold")
ax.set_xlabel("价格(加权标准化值,越大越便宜)", fontsize=11)
ax.set_ylabel("质量(加权标准化值,越大越好)", fontsize=11)
ax.set_title("图① 二维属性空间散点图:离 Z+ 越近、离 Z- 越远的方案越好", fontsize=13)
ax.legend(loc="lower right", fontsize=10)
ax.grid(alpha=0.3)
fig.tight_layout()
fig.savefig(os.path.join(FIG_DIR, "topsis_scatter.png"), dpi=200)
plt.show()
# ========== 8. 图② 贴近度排序条形图(topsis_ranking.png) ==========
order_desc = np.argsort(-C)
labels = [suppliers[i] for i in order_desc]
C_sorted = C[order_desc]
fig, ax = plt.subplots(figsize=(7.5, 5))
colors = ["#c0392b" if i == 0 else "#8ea9c8" for i in range(n)]
bars = ax.barh(labels, C_sorted, color=colors, edgecolor="white")
for b, c in zip(bars, C_sorted):
ax.text(b.get_width() + 0.008, b.get_y() + b.get_height() / 2,
f"{c:.4f}", va="center", fontsize=11)
ax.set_xlim(0, 1.0)
ax.invert_yaxis() # 排名第 1 的放在最上面
ax.set_xlabel("相对贴近度 C_i(越大越好,0~1)", fontsize=11)
ax.set_title("图② 各方案贴近度 C_i 排序条形图(最终结论)", fontsize=13)
ax.grid(axis="x", alpha=0.3)
fig.tight_layout()
fig.savefig(os.path.join(FIG_DIR, "topsis_ranking.png"), dpi=200)
plt.show()
# ========== 9. 图③ D+ / D- 距离对比分组柱状图(topsis_distance.png) ==========
x_pos = np.arange(n)
width = 0.35
fig, ax = plt.subplots(figsize=(7.5, 5.5))
b1 = ax.bar(x_pos - width / 2, D_pos, width,
label="D+(离正理想解越近越好)", color="#c0392b")
b2 = ax.bar(x_pos + width / 2, D_neg, width,
label="D-(离负理想解越远越好)", color="#27ae60")
for b in list(b1) + list(b2):
ax.text(b.get_x() + b.get_width() / 2, b.get_height() + 0.004,
f"{b.get_height():.4f}", ha="center", va="bottom", fontsize=9)
ax.set_xticks(x_pos)
ax.set_xticklabels(suppliers, fontsize=10)
ax.set_ylabel("欧氏距离", fontsize=11)
ax.set_title("图③ 各方案到正/负理想解的欧氏距离对比", fontsize=13)
ax.legend(fontsize=10)
ax.grid(axis="y", alpha=0.3)
fig.tight_layout()
fig.savefig(os.path.join(FIG_DIR, "topsis_distance.png"), dpi=200)
plt.show()
# ========== 10. 图④ 正向化前后数据对比面板(topsis_forward_compare.png) ==========
# 左图:原始数据(量纲差异巨大:价格 18~25 万元 vs 合格率 0.85~0.98,不可直接比较)
# 右图:正向化后的数据(全部在 0~1,且方向统一为"越大越好")
fig, axes = plt.subplots(1, 2, figsize=(13, 5.5))
x_pos = np.arange(m)
width = 0.18
supplier_colors = ["#2f6fb0", "#c0392b", "#27ae60", "#8e44ad"]
short_names = ["A厂", "B厂", "C厂", "D厂"]
for i in range(n):
axes[0].bar(x_pos + (i - (n - 1) / 2) * width, X[i], width,
label=short_names[i], color=supplier_colors[i])
axes[0].set_xticks(x_pos)
axes[0].set_xticklabels(indicator_names, fontsize=10)
axes[0].set_title("左:原始数据(量纲差异巨大,无法直接比较)", fontsize=12)
axes[0].set_ylabel("原始取值(量纲各异)", fontsize=11)
axes[0].legend(fontsize=9)
for i in range(n):
axes[1].bar(x_pos + (i - (n - 1) / 2) * width, Xf[i], width,
label=short_names[i], color=supplier_colors[i])
axes[1].set_xticks(x_pos)
axes[1].set_xticklabels(indicator_names, fontsize=10)
axes[1].set_title("右:正向化后(统一为 0~1 且越大越好)", fontsize=12)
axes[1].set_ylabel("正向化取值(0~1)", fontsize=11)
axes[1].legend(fontsize=9)
fig.suptitle("图④ 正向化前后数据对比面板", fontsize=14)
fig.tight_layout()
fig.savefig(os.path.join(FIG_DIR, "topsis_forward_compare.png"), dpi=200)
plt.show()
print("\n4 张图已保存到 figures/ 目录:topsis_scatter.png、topsis_ranking.png、"
"topsis_distance.png、topsis_forward_compare.png")
七、结果解读与注意事项
7.1 运行输出解读(以本例供应商选择为例)
以第六节代码(4 家供应商 × 5 个指标,AHP 定权)为例,运行脚本后控制台输出如下:
原始决策矩阵 X(4 个方案 × 5 个指标):
价格 质量合格率 交货准时率 距离 服务评分
供应商1(A厂) 18.0 0.90 0.83 320.0 6.5
供应商2(B厂) 25.0 0.98 0.94 90.0 9.5
供应商3(C厂) 23.0 0.92 0.74 550.0 6.0
供应商4(D厂) 20.0 0.85 0.87 250.0 4.5
【步骤 0】AHP 定权结果
λ_max = 5.0331,CI = 0.0083,CR = 0.0074(CR < 0.10,通过一致性检验)
指标权重 w = [0.2147 0.376 0.1209 0.0738 0.2147] (Σw = 1.0000)
【正向化函数自检】
中间型示例:水质 pH = [6. 6.5 7. 7.5 9. ](最优值 7.0)→ 正向化 [0.5 0.75 1. 0.75 0. ]
区间型示例:体温 = [35. 35.6 36.5 37.4 38.2](最优区间 [36.0, 37.0])→ 正向化 [0.1667 0.6667 1. 0.6667 0. ]
【步骤 ①】正向化矩阵 X'(所有指标统一为越大越好,取值 0~1):
价格 质量合格率 交货准时率 距离 服务评分
供应商1(A厂) 1.0000 0.3846 0.45 0.5000 0.4
供应商2(B厂) 0.0000 1.0000 1.00 1.0000 1.0
供应商3(C厂) 0.2857 0.5385 0.00 0.0000 0.3
供应商4(D厂) 0.7143 0.0000 0.65 0.6522 0.0
【等价性自检】手写循环 vs numpy 向量化(np.allclose 判定):
D+ 一致:True;D- 一致:True;C 一致:True;排名一致:True
D+ 手写 vs scipy.cdist 一致:True
【步骤 ②】标准化矩阵 Z(z_ij = x'_ij / sqrt(Σ_i x'_ij²)):
价格 质量合格率 交货准时率 距离 服务评分
供应商1(A厂) 0.7926 0.3208 0.3530 0.3863 0.3578
供应商2(B厂) 0.0000 0.8340 0.7845 0.7726 0.8944
供应商3(C厂) 0.2265 0.4491 0.0000 0.0000 0.2683
供应商4(D厂) 0.5661 0.0000 0.5099 0.5039 0.0000
【步骤 ③】加权标准化矩阵 V(v_ij = w_j · z_ij):
价格 质量合格率 交货准时率 距离 服务评分
供应商1(A厂) 0.1701 0.1206 0.0427 0.0285 0.0768
供应商2(B厂) 0.0000 0.3136 0.0948 0.0570 0.1920
供应商3(C厂) 0.0486 0.1688 0.0000 0.0000 0.0576
供应商4(D厂) 0.1215 0.0000 0.0616 0.0372 0.0000
【步骤 ④】正理想解 Z+: [0.1701 0.3136 0.0948 0.057 0.192 ]
负理想解 Z-: [0. 0. 0. 0. 0.]
【步骤 ⑤⑥】距离与贴近度:
方案 D+(到正理想解距离) D-(到负理想解距离) 相对贴近度 C_i 排名
供应商1(A厂) 0.2325 0.2281 0.4953 2
供应商2(B厂) 0.1701 0.3840 0.6929 1
供应商3(C厂) 0.2569 0.1849 0.4185 3
供应商4(D厂) 0.3729 0.1412 0.2747 4
【步骤 ⑦】按贴近度从高到低排序:
方案 D+(到正理想解距离) D-(到负理想解距离) 相对贴近度 C_i 排名
供应商2(B厂) 0.1701 0.3840 0.6929 1
供应商1(A厂) 0.2325 0.2281 0.4953 2
供应商3(C厂) 0.2569 0.1849 0.4185 3
供应商4(D厂) 0.3729 0.1412 0.2747 4
结论:供应商2(B厂) 最优,相对贴近度 C = 0.6929
4 张图已保存到 figures/ 目录:topsis_scatter.png、topsis_ranking.png、topsis_distance.png、topsis_forward_compare.png
逐项解读:
- AHP 定权:,,,判断矩阵通过一致性检验,权重可信。权重排序:质量(0.3760)> 价格(0.2147)= 服务(0.2147)> 准时率(0.1209)> 距离(0.0738)——质量的重要性约是距离的 5 倍。
- 正向化自检:pH 数据 正向化为 ,恰好等于 7 的样本取 1、偏离最远的 9.0 取 0,符合 1.3.1 节公式;体温数据同理验证了区间型公式。竞赛中建议把四类正向化函数都各跑一个小例子自检,这是防止"公式记错"最廉价的手段。
- 正向化矩阵 :方向全部统一(价格列 A 厂最便宜 → 1.0000,B 厂最贵 → 0.0000),量纲全部压到 0~1。注意每一列都至少有一个 0(该列最差者)和一个 1(该列最优者),这是 min-max 正向化的必然结果。
- 等价性自检:手写循环与 numpy 向量化的 、、、排名全部一致,且手写 与 scipy.cdist 计算结果一致(均返回 True),说明两种写法实现的是同一套公式——论文附录放"手写 + 库对照"是可靠性的加分证据。
- 加权标准化矩阵 :B 厂在质量(0.3136)、准时率(0.0948)、距离(0.0570)、服务(0.1920)四个维度全部达到正理想值,只在价格维度(0.0000)落后;因此它的 恰好等于"价格维度的差距"(A 厂价格加权值 0.1701),几何上就是"只在一条坐标轴上偏离 "。
- 负理想解 :全 0 向量是 min-max 正向化的必然结果(每列最差者正向化后为 0)。此时 退化为"方案到原点的距离",,解读时注意它的含义是"离全样本最差水平多远",而不是"离某个具体的最差方案多远"。
- 距离与贴近度:B 厂 (最小)且 (最大),双优, 排名第一;D 厂 (最大)且 (最小),双劣, 垫底。A 厂两项居中(0.2325 / 0.2281),贴近度 0.4953 列第二;C 厂 0.4185 列第三。
- 结论的业务解读:B 厂虽然价格最贵(25 万元),但质量(0.98)、服务(9.5)、准时率(0.94)、距离(90 km)全面领先,而质量权重高达 0.376,最终综合得分最高——TOPSIS 的结论是"贵得有道理"。D 厂虽然价格便宜(20 万元),但质量(0.85)与服务(4.5)双双垫底,被质量权重"一票打下去",排名最后。注意排名差距的分档:B 厂领先 A 厂约 0.20(差距大,结论稳);A 厂与 C 厂差距约 0.08,若权重微调,两者名次可能互换(见 7.4 敏感性分析)。
7.2 四张图的解读(本例)
- 图①(二维属性空间散点):红星 位于右上(价格维度 0.1701 来自 A 厂、质量维度 0.3136 来自 B 厂,是"拼凑出来的完美方案");绿星 与原点重合。B 厂紧贴红星的右侧(仅价格一维偏离),D 厂位于左下、离红星最远,与最终排名一致。注意图上展示的是二维投影,与五维真实距离不同,仅供示意。
- 图②(贴近度排序条形图):四根柱从 0.6929 到 0.2747 单调下降,第一名 B 厂用红色突出;首末差距约 0.42,区分度良好,排序结论有说服力。
- 图③(距离对比分组柱状图):B 厂"红矮绿高"(、)双优稳居第一;D 厂"红高绿矮"(、)双劣垫底;A 厂两柱高度接近(0.2325 / 0.2281),贴近度接近 0.5,属于"不上不下"的中间选手。
- 图④(正向化前后对比面板):左图价格(18
25)与距离(90550)的柱子很高,而质量合格率(0.850.98)几乎贴地——若直接加权求和,距离差 460 会彻底淹没质量差 0.13,这就是必须标准化的直观证据;右图所有柱子都落在 01,且方向统一为"越高越好",五个指标第一次"站在同一起跑线"。
7.3 常见坑与应对
- 忘记正向化:极小型指标(价格、成本、距离)直接套用"每列取 max 作 ",等于把"最贵的"当成"最优的",正负理想解方向全反。应对:建模第一步就逐个指标标注类型(极大型/极小型/中间型/区间型),在论文中列成表格,代码里用
indicator_types列表显式声明(如第六节代码)。 - 极小型公式记错:把极小型正向化写成 (这是极大型的公式),方向没翻过来, 取 max 后等于"最差即最优"。自检方法:拿一列已知数据算一下,"最便宜的价格正向化后必须是 1"。本例 A 厂价格 18 万元(最便宜)→ ,可作检验锚点。
- 量纲未处理:跳过标准化(或只正向化不标准化)直接把不同量纲的指标加权求和,取值范围大的指标(如距离的几百)会主导结果。应对:正向化之后必须再做向量归一化(除以列范数),两步缺一不可;图④直观展示了"不处理会怎样"。
- 误读:把贴近度当"绝对得分"("0.6929 分"),或把 0.5 当"及格线"。正确解读: 只是相对排序依据,0.5 表示方案恰好在正负理想解正中间; 整体偏高只说明"最优方案相对贴近",不代表方案客观上很好。
- 正负理想解方向取反:做了正向化却仍按"极小型取 min 作 "的原始定义取理想解,属于双重处理。记住:正向化之后所有指标都是极大型, 一律取列最大值、 一律取列最小值,公式统一,不再区分指标类型。
- 忽视" 是全 0 向量"的后果:min-max 正向化后每列必有一个 0,加权后 必为全 0 向量, 退化为到原点的距离。这本身没错,但要意识到: 的数值会整体偏高且区分度被 主导;若想让 更有"实物感",可改用其他标准化方式(如只做向量归一化不做 min-max,或 z-score 后平移),论文里讨论这一点属于亮点。
- 权重未归一化:权重向量和不为 1 直接加权。虽然按 排序的结论在权重整体缩放时不变,但 、、 的数值失去可比性,论文里报权重必须是归一化的()。本例 AHP 输出权重和为 1.0000,已归一化。
- 指标重复计权(强相关指标):欧氏距离假设各指标正交,两个高度相关的指标(如 GDP 与人均收入)等于把同一信息算了两遍。应对:先做相关系数矩阵分析,删冗余指标或用主成分降维,或改用马氏距离 TOPSIS。
- 中间型/区间型的 算错:中间型直接用 当 (应该是 ),区间型漏掉"低于下界"分支( 的两个候选都要考虑)。应对:照第六节代码的自检例子手算一遍再写进论文。
- 排序方向搞反:
np.argsort(C)默认升序,忘加负号(np.argsort(-C))会把最差方案排第一。应对:排序后立刻打印"第一名是谁",与肉眼判断(谁的综合表现最好)核对;本例若第一名不是 B 厂,说明排序方向写反了。
7.4 竞赛论文写作建议(话术模板)
建模段(先讲为什么用、再讲流程、再报结果):
供应商选择问题是一个典型的多指标综合评价问题,涉及价格、质量、准时率、距离、服务 5 个指标,各指标方向不一、量纲各异。本文首先采用层次分析法(AHP)构造判断矩阵确定指标权重,一致性比例 ,通过一致性检验,得权重 ;随后采用 TOPSIS 法进行综合排序:先对决策矩阵进行指标正向化与向量归一化,再构造加权标准化矩阵,确定正理想解 与负理想解 ,计算各方案到正负理想解的欧氏距离 、 与相对贴近度 ,按 降序排序。
正向化段(交代预处理细节,评审加分点):
由于 5 个指标中价格、距离为极小型指标(越小越好),其余为极大型指标,且各指标量纲差异悬殊(价格 18
25 万元、距离 90550 km、合格率 0.85~0.98),不能直接加权。本文对极小型指标采用 正向化,对极大型指标采用 min-max 归一化,使所有指标统一为"越大越好"且取值落在 ;随后采用向量归一化 z_{ij} = \dfrac{x'_{ij}}{\sqrt{\sum_i x'_{ij}^2}} 进一步消除量纲影响。
结果段:
计算得各供应商相对贴近度:供应商 2(B 厂)、供应商 1(A 厂)、供应商 3(C 厂)、供应商 4(D 厂)。综合排序为 B 厂 > A 厂 > C 厂 > D 厂,故推荐选择供应商 2(B 厂)。B 厂虽价格最高,但其质量合格率(0.98)、服务评分(9.5)、交货准时率(0.94)、距离(90 km)均为四家最优,在权重最大的质量指标(0.376)上表现突出,综合表现最佳。
敏感性(稳健性)分析段(评价类题目必备,评审极看重):
为检验排序结果对权重的敏感性,将各指标权重依次在 范围内扰动并保持其余权重按比例调整,重复计算 TOPSIS 贴近度与排序。结果表明在权重扰动范围内,B 厂始终排名第一、D 厂始终排名第四,排序结果稳健;A 厂与 C 厂在部分扰动下名次互换,二者贴近度差距较小(0.0768),属"同一档次"的方案,实际决策中可结合其他因素进一步考察。
方法对比段:
将本文 TOPSIS 排序结果与简单加权总分法对比,两者第一名一致(均为 B 厂),但简单加权法受距离指标量纲影响,A 厂与 C 厂名次相反;进一步采用熵权法重新定权并再次运行 TOPSIS,第一名仍为 B 厂,说明结论对不同定权方法稳健。相较于简单加权法,TOPSIS 通过正负理想解与欧氏距离构造的贴近度指标能更充分地利用各方案的相对位置信息,排序更具区分度。
八、延伸阅读
- 灰色关联 TOPSIS(GRA-TOPSIS):用灰色关联度修正/替换欧氏距离,构造"灰色关联贴近度"进行排序。灰色系统理论擅长处理小样本、贫信息、数据不完备的评价问题,两者结合后对数据质量要求更低,是竞赛中非常流行的改进方向(参考邓聚龙《灰色系统基本方法》)。
- 熵权 TOPSIS:用熵权法(根据各指标取值的变异程度客观定权,信息熵越小权重越大)替代 AHP 主观定权,再走 TOPSIS 流程。纯数据驱动、无人为干预,适合"评价指标体系明确但权重争议大"的题目;与 AHP 定权结果对比本身就是论文的一节内容。
- 马氏距离 TOPSIS:把欧氏距离换成马氏距离 ( 为指标协方差矩阵),自动消除指标间相关性与量纲的影响,解决"强相关指标重复计权"的问题;当 时退化为标准 TOPSIS。
- VIKOR 法(多准则妥协解):TOPSIS 的"表亲",同时考虑群体效用 (离理想解的总距离)与个体遗憾 (离理想解的最大单维距离),追求"最大化群体效用、最小化个体遗憾"的妥协解,更适合决策者关心短板、需要折中方案的场景。VIKOR 排序与 TOPSIS 排序不一致时,常作为稳健性讨论的依据。
- 组合赋权与改进标准化:AHP-熵权组合赋权(乘法归一化 或线性加权 )是目前竞赛论文最常用的"创新点";标准化方面还有 z-score、极差变换、固定基点标准化等变体,不同标准化方式下排序的稳定性可作为敏感性分析内容。
- 其他排序型 MCDM 方法:ELECTRE(淘汰与选择转换)、PROMETHEE(偏好排序组织法)等欧式学派方法,以及 Borda/Kemeny 等投票类排序方法,可用于与 TOPSIS 结果交叉验证(用 Spearman 秩相关系数度量不同方法排序的一致性)。
- 多目标优化中的 TOPSIS:多目标进化算法(NSGA-II、MOEA/D)求得 Pareto 前沿后,用 TOPSIS 在非劣解集中挑选折中最优解,是优化类竞赛题目中"方案落地"的标准收尾动作。
- 推荐资源:Hwang & Yoon《Multiple Attribute Decision Making: Methods and Applications》(1981,TOPSIS 原始出处);司守奎、孙玺菁《数学建模算法与应用》(中文竞赛教材,综合评价章节含 TOPSIS 与熵权法实例);姜启源等《数学模型》(竞赛经典,评价模型一章);Tzeng & Huang《Multiple Attribute Decision Making: Methods and Applications》(综述各类 MCDM 方法,含 VIKOR、ELECTRE 的严格推导)。