跳转至

D2 · TOPSIS + 模糊综合评价

D1 解决的是"每个指标多重要",D2 解决"综合起来哪个方案最好"。这两天的内容合起来就是国赛评价类题目的完整解法链条——出现频率极高,务必吃透。

1. TOPSIS 的核心思想

全称"逼近理想解排序法"。思想极其朴素:构造一个虚拟的最优方案(正理想解,每个指标都取所有方案里的最好值)和一个虚拟的最差方案(负理想解),然后看每个真实方案离这两者的距离。离最优近、同时离最差远的,就是好方案。

为什么比"加权求和"强

加权求和只看总分,可能出现"某方案在关键指标上极差、但靠其他指标堆分反超"的情况。TOPSIS 用欧氏距离衡量,短板会被平方放大,更能反映"综合均衡性"。写论文时这句话就是你选 TOPSIS 而不用简单加权的理由。

2. 六步流程

  1. 正向化:把所有指标统一成"越大越好"(D1 的教训,四种类型的公式见第 4 节)
  2. 标准化:消除量纲。TOPSIS 用向量归一化(每列除以该列平方和的开方),不是 min-max
  3. 加权:乘上 D1 算出的权重(AHP / 熵权法 / 组合权重)
  4. 定理想解:Z⁺ = 每列最大值组成的向量;Z⁻ = 每列最小值组成的向量
  5. 算距离:每个方案到 Z⁺ 的欧氏距离 d⁺,到 Z⁻ 的距离 d⁻
  6. 算贴近度:C = d⁻ / (d⁺ + d⁻),取值 0~1,越大越优,按 C 排序即得结论

易错

贴近度公式分子是 d⁻ 不是 d⁺。记忆方法:分子放"离最差的距离",离最差越远越好,所以越大越优——和直觉方向一致。写反了排名会完全颠倒。

3. 例题精算(4 方案 × 3 指标,全程不跳步)

选购电动车,指标:价格(万元,成本型)、续航(km,效益型)、评分(效益型)。权重沿用 D1 组合赋权的结果 w = (0.4, 0.35, 0.25)

例子

       价格   续航   评分
方案A   20    500    8
方案B   25    600    7
方案C   30    550    9
方案D   22    450    6
  1. 正向化价格列(max−x,max=30):(10, 5, 0, 8)。其余两列本就是效益型,不动。
  2. 向量标准化,每列除以平方和开方:
价格列 √(10²+5²+0²+8²)=√189=13.748 → (0.7274, 0.3637, 0, 0.5819) 续航列 √1115000=1055.93 → (0.4735, 0.5682, 0.5209, 0.4262) 评分列 √230=15.166 → (0.5275, 0.4616, 0.5934, 0.3956)
  1. 加权(分别乘 0.4 / 0.35 / 0.25):
价格 续航 评分 方案A 0.2910 0.1657 0.1319 方案B 0.1455 0.1989 0.1154 方案C 0 0.1823 0.1484 方案D 0.2328 0.1492 0.0989
  1. 理想解:Z⁺ = (0.2910, 0.1989, 0.1484)(每列最大);Z⁻ = (0, 0.1492, 0.0989)(每列最小)
  2. 距离(以方案A为例演示): d⁺ = √[(0.2910−0.2910)² + (0.1657−0.1989)² + (0.1319−0.1484)²] = √0.001374 = 0.0371 d⁻ = √[(0.2910−0)² + (0.1657−0.1492)² + (0.1319−0.0989)²] = √0.08604 = 0.2933
  3. 贴近度:CA = 0.2933 / (0.0371 + 0.2933) = 0.888

四个方案全算完:

方案 d⁺ d⁻ 贴近度 C 排名
A 0.0371 0.2933 0.888
D 0.0911 0.2328 0.719
B 0.1492 0.1546 0.509
C 0.2914 0.0595 0.170

结果解读:C 方案评分最高(9分)却排末位——因为价格权重 0.4 最大,而它 30 万的价格正向化后归零,短板被欧氏距离放大。这个例子正好演示了 TOPSIS "惩罚短板"的特性,写论文时可以拿来做结果分析。

4. 四种指标类型的正向化公式(必背)

D1 只讲了成本型,实际赛题里另外两种也常见,漏掉会直接算错:

类型 含义 正向化公式
极大型 越大越好(评分、产量) 不用处理
极小型 越小越好(成本、污染) x' = max − x  或  x' = 1/x(x>0)
中间型 越接近某值越好(pH 值接近 7) M = max|x − x_best|,x' = 1 − |x − x_best| / M
区间型 落在 [a,b] 内最好(体温 36~37℃) M = max(a − min(x), max(x) − b);
x<a 时 x' = 1 − (a−x)/M;a≤x≤b 时 x' = 1;x>b 时 x' = 1 − (x−b)/M

5. 代码:完整可复用的 TOPSIS

topsis.py

import numpy as np

def positivize(X, kinds, best=None, interval=None):
    """kinds 每列一个: 'max' / 'min' / 'mid' / 'interval'
       best: {列索引: 最优值}      用于 mid
       interval: {列索引: (a, b)}  用于 interval"""
    X = np.asarray(X, dtype=float).copy()
    best = best or {}
    interval = interval or {}
    for j, kind in enumerate(kinds):
        col = X[:, j]
        if kind == 'min':
            X[:, j] = col.max() - col
        elif kind == 'mid':
            M = np.abs(col - best[j]).max()
            X[:, j] = 1 - np.abs(col - best[j]) / M
        elif kind == 'interval':
            a, b = interval[j]
            M = max(a - col.min(), col.max() - b)
            X[:, j] = np.where(col < a, 1 - (a - col) / M,
                      np.where(col > b, 1 - (col - b) / M, 1.0))
    return X

def topsis(X, kinds, w, **kw):
    X = positivize(X, kinds, **kw)
    Z = X / np.sqrt((X**2).sum(axis=0))     # 向量标准化
    Zw = Z * w                              # 加权
    best, worst = Zw.max(axis=0), Zw.min(axis=0)
    d_plus  = np.sqrt(((Zw - best )**2).sum(axis=1))
    d_minus = np.sqrt(((Zw - worst)**2).sum(axis=1))
    return d_minus / (d_plus + d_minus)     # 贴近度,越大越优

# 验证本文例题
data = np.array([[20,500,8],[25,600,7],[30,550,9],[22,450,6]])
C = topsis(data, kinds=['min','max','max'], w=np.array([0.4,0.35,0.25]))
print(C.round(3))          # 期望 [0.888 0.509 0.170 0.719]
print(np.argsort(-C) + 1)  # 期望 [1 4 2 3] 即 A > D > B > C

注意kinds 依然是必填参数,延续 D1 勘误里定下的"易错步骤强制显式声明"原则。

6. 模糊综合评价:处理"说不清的边界"

TOPSIS 要求每个方案在每个指标上有确切数值。但很多赛题里的评价是模糊的——"服务质量好不好"没法给一个精确数字,只能说"60% 的人认为优,30% 认为良"。模糊综合评价就是处理这类问题的。

三个核心概念

  1. 因素集 U:评价指标的集合,如 {服务, 价格, 环境}
  2. 评语集 V:评价等级的集合,如 {优, 良, 中, 差}
  3. 隶属度矩阵 R:m×n 矩阵,rij 表示"因素 i 属于评语 j 的程度",每行相加为 1。数据来源通常是问卷统计比例,或用隶属函数计算

最终结果 B = W ∘ R,其中 W 是 D1 算出的权重向量,∘ 是合成算子(常用矩阵乘法)。

7. 例题精算

例子

权重 W = (0.5, 0.3, 0.2)   对应 服务/价格/环境
隶属度矩阵 R:
        优    良    中    差
服务   0.4   0.4   0.1   0.1
价格   0.2   0.3   0.4   0.1
环境   0.1   0.2   0.5   0.2
  1. B = W · R,逐列计算: 优:0.5×0.4 + 0.3×0.2 + 0.2×0.1 = 0.28 良:0.5×0.4 + 0.3×0.3 + 0.2×0.2 = 0.33 中:0.5×0.1 + 0.3×0.4 + 0.2×0.5 = 0.27 差:0.5×0.1 + 0.3×0.1 + 0.2×0.2 = 0.12
  2. 校验:0.28+0.33+0.27+0.12 = 1.00 ✓(B 各分量之和应为 1,不为 1 说明 R 的行没归一化)
  3. 最大隶属度原则:取最大值 0.33 对应的等级 → 结论""
  4. 加权平均法(给等级赋分 优=4 良=3 中=2 差=1): 0.28×4 + 0.33×3 + 0.27×2 + 0.12×1 = 2.77 → 介于"中"与"良"之间,偏良

易错

最大隶属度原则给出"良",但 0.33 / 0.28 / 0.27 三者非常接近,说明评价结果其实很分散——此时"良"这个结论并不稳健。当最大值不显著占优时,必须改用加权平均法,因为最大隶属度原则丢掉了其余所有信息。论文里两种都算、并说明为什么选其一,是很好的加分点。

8. 合成算子的选择

算子 计算方式 特点 何时用
M(·, +) 加权平均型 普通矩阵乘法 保留全部信息,结果均衡 默认选它,国赛绝大多数情况
M(∧, ∨) 主因素决定型 先取小再取大 只由最突出的因素决定,丢失信息多 只关心单一主导因素时

写论文时不要只说"采用模糊综合评价",要写明用了哪个合成算子及理由——这是区分"套模板"和"真懂"的细节。

9. 代码

fuzzy_eval.py

import numpy as np

def fuzzy_eval(W, R, scores=None):
    W = np.asarray(W, dtype=float)
    R = np.asarray(R, dtype=float)
    assert np.allclose(R.sum(axis=1), 1), "R 每行必须归一化"
    B = W @ R                      # M(·,+) 加权平均型合成
    B = B / B.sum()                # 保险起见再归一化
    result = {'B': B, 'max_idx': int(np.argmax(B))}
    if scores is not None:         # 加权平均法,scores 如 [4,3,2,1]
        result['score'] = float(B @ np.asarray(scores, dtype=float))
    return result

W = [0.5, 0.3, 0.2]
R = [[0.4,0.4,0.1,0.1],
     [0.2,0.3,0.4,0.1],
     [0.1,0.2,0.5,0.2]]
out = fuzzy_eval(W, R, scores=[4,3,2,1])
print(out['B'].round(3), out['max_idx'], round(out['score'],3))
# 期望 [0.28 0.33 0.27 0.12] 1 2.77

10. 写作表达

评价类题目的完整叙述链条应该是四段式,每段一句话交代清楚:

论文里可以这么写

① 为什么要综合评价:本问需在 4 种电动车方案中择优,涉及 价格、续航、评分 等多个量纲不同且优劣方向不一致的指标,无法直接比较。

② 权重怎么来的:采用 AHP 与熵权法组合赋权确定指标权重,一致性检验 CR = 0.033 < 0.1,得权重向量 (0.40, 0.35, 0.25)。

③ 评价方法与关键处理:使用 TOPSIS 法排序。考虑到价格为极小型指标,先按 x' = max − x 完成正向化,再作向量标准化,以消除量纲影响。

④ 结果与合理性:各方案贴近度依次为 0.888、0.719、0.509、0.170,方案 A 最优。值得注意的是方案 C 虽评分最高,但因价格显著偏高而排名末位,说明 TOPSIS 对短板指标较为敏感,评价结果符合实际决策逻辑。

第 ④ 段的"值得注意的是…"这种句式很重要——它证明你解释了结果而不只是报了个数,是拿高分论文和普通论文的分水岭。

11. 今日任务清单

  • [ ]

任务1:复现例题

把第 5 节代码跑通,确认输出是 [0.888 0.509 0.170 0.719]。跑不对就逐步打印中间矩阵,对照第 3 节的手算结果定位问题

  • [ ]

任务2:处理中间型/区间型指标

给例题再加一列"电池温度"(区间型,最佳区间 20~30℃),自己编 4 个数值,用 positivize 的 interval 分支跑通

  • [ ]

任务3:串联 D1 + D2

用 D1 的 entropy_weight 从任务2 的数据里算出权重,直接喂给 topsis——完整走一遍"数据→权重→排序"的链条,这是评价类赛题的标准解法

  • [ ]

任务4:写作

套用第 10 节四段式模板,用任务3 的真实数字写一段完整的评价方法叙述

自查

自查:进 D3 之前答一遍

  • TOPSIS 的贴近度公式分子是 d⁺ 还是 d⁻?为什么?
  • 为什么 TOPSIS 用向量归一化而不是 min-max 归一化?(提示:min-max 会让最差方案某列直接归零)
  • 模糊综合评价里,B 向量各分量之和应该等于多少?不等于说明哪一步错了?
  • 什么情况下不该用最大隶属度原则?改用什么?
  • 一道赛题给你 20 个城市 8 个指标的完整数据表,要求排名——你会怎么组合 D1 和 D2 学的方法?完整说出流程。
参考答案

参考答案

  1. 分子是 d⁻,即 C = d⁻ / (d⁺ + d⁻)。因为"离最差方案越远"意味着越好,把 d⁻ 放分子才能让 C 越大越优,与直觉方向一致。写成 d⁺ 会让排名完全颠倒。

  2. 因为 min-max 归一化会把每列的最小值强行压成 0,该方案在这个指标上的贡献被完全抹掉,且负理想解 Z⁻ 恒为全 0 向量,距离计算失去区分度。向量归一化(除以列平方和的开方)保留了各方案间的相对比例关系,只消除量纲不扭曲结构。

  3. 应等于 1。B = W·R,其中 W 各分量之和为 1、R 每行之和为 1,因此 B 各分量之和必然为 1。 不等于 1 说明:要么 R 的某一行没有归一化(隶属度之和不为 1),要么 W 没有归一化。代码里用 assert np.allclose(R.sum(axis=1), 1) 提前拦住。

  4. 当最大隶属度不显著占优时不该用——比如 B = (0.28, 0.33, 0.27, 0.12),前三项非常接近,"良"这个结论并不稳健,而且最大隶属度原则丢掉了其余所有信息。 改用加权平均法:给等级赋分(优=4 良=3 中=2 差=1),算 B·scores = 2.77,落在"中"与"良"之间偏良,比单说"良"精确得多。论文里两种都算并说明取舍理由,是加分点。

  5. 标准流程如下(这就是评价类赛题的通用解法链条):

    1. 数据预处理  缺失值/异常值处理
    2. 正向化      逐列判断类型(极大/极小/中间/区间),统一成"越大越好"
    3. 确定权重    熵权法(客观) + AHP(主观) → 组合权重 w = α·w_AHP + (1−α)·w_entropy
    4. TOPSIS      向量标准化 → 加权 → 定 Z⁺/Z⁻ → 算 d⁺/d⁻ → 贴近度 C
    5. 排序        按 C 降序输出 20 个城市的排名
    6. 敏感性分析  改变 α 取值(如 0.3/0.5/0.7),看排名是否稳定
    

    第 6 步很多人会漏,但它是论文里"模型检验"一节的核心内容——如果 α 微调就导致排名大幅变动,说明结论不可靠,必须在论文里讨论。8 个指标接近 AHP 的判断上限(7±2),若 CR 难以通过,可考虑分层构造判断矩阵(先给指标分 2~3 组,组内比较后再组间比较)。