# 多属性决策启示录 第3期|AHP:把"我觉得"变成数学
系列:面向研究生与算法工程师的 MADM 深度教程
标签:多属性决策,AHP,层次分析法,主观赋权,Python,算法
前言:买车时的内心博弈
你去 4S 店看车,三款候选:A 省油但空间小,B 空间大但油耗高,C 各方面均衡但贵。
你心里在反复掂量——"油耗比空间重要多了""不对,周末要带全家出游空间也很重要""但贵两万块……"
这不是选择困难症。这是你的大脑在做多属性决策,只是没有把它写成矩阵。
AHP(Analytic Hierarchy Process,层次分析法)做的事情就是把这种模糊的"我觉得 A 比 B 重要"变成一套可计算、可验证的数学权重体系。
一、AHP 的核心思想
Saaty 在 1980 年提出 AHP 时,做了一个天才的简化:
人脑不擅长同时比较多个对象,但擅长两两比较。
与其让你给"油耗、空间、价格、动力、安全性"五个指标一次性打分,不如让你回答 10 个更简单的问题:
- "油耗和空间,哪个更重要?重要多少?"
- "油耗和价格,哪个更重要?重要多少?"
- ……
然后通过矩阵运算,把这 10 个两两比较的结果还原为 5 个指标的精确权重。
二、Saaty 1-9 标度表
两两比较需要一把"尺子"。Saaty 定义了 1-9 的标度:
| 标度 | 含义 | 解释 |
|---|---|---|
| 1 | 同等重要 | 两个指标贡献相同 |
| 3 | 稍微重要 | 经验和判断稍微倾向一方 |
| 5 | 明显重要 | 经验和判断明显倾向一方 |
| 7 | 强烈重要 | 一方非常强烈地被偏好 |
| 9 | 极端重要 | 一方压倒性地重要于另一方 |
| 2,4,6,8 | 中间值 | 介于上述判断之间 |
| 倒数 | 反向比较 | 如果 A:B=3,则 B:A=1/3 |
为什么是 1-9 而不是 1-100?
Saaty 做过心理学实验。人的短期记忆只能同时处理 7±2 个信息块(Miller 定律)。1-9 正好在认知负荷的舒适区内。超过 9 级的区分度,人脑的判断就不再可靠了。
三、构建判断矩阵
回到买车的例子。假设你对 5 个指标的相对重要性做如下判断:
| 油耗 | 空间 | 价格 | 动力 | 安全 | |
|---|---|---|---|---|---|
| 油耗 | 1 | 3 | 2 | 5 | 1/3 |
| 空间 | 1/3 | 1 | 1/2 | 3 | 1/5 |
| 价格 | 1/2 | 2 | 1 | 4 | 1/4 |
| 动力 | 1/5 | 1/3 | 1/4 | 1 | 1/7 |
| 安全 | 3 | 5 | 4 | 7 | 1 |
解读一下这个矩阵:
-油耗 vs 安全 = 1/3:你认为安全性比油耗稍微重要(安全:油耗 = 3:1,所以油耗:安全 = 1/3)
-安全 vs 动力 = 7:安全性比动力强烈重要,差 7 个等级
- 对角线全是 1:指标和自己比,当然是同等重要
- 左下角是右上角的倒数:矩阵是互反矩阵
这个矩阵记作A,维度 n×n。
四、从判断矩阵到权重
AHP 提取权重有三种方法。推荐第三种(特征向量法)。
4.1 和积法(最简单)
``Step 1:逐列归一化(每列除以该列之和)
油耗列:1+0.333+0.5+0.2+3 = 5.033
归一化:1/5.033=0.199, 0.333/5.033=0.066, ...
Step 2:逐行求和
油耗行求和 = 0.199+0.231+0.258+0.25+0.163 = 1.101
Step 3:再归一化
总和 = 1.101+0.475+0.752+0.190+2.482 = 5.000
油耗权重 = 1.101/5.000 = 0.220
`
4.2 方根法(几何平均)
`Step 1:逐行求几何平均
油耗行: (1×3×2×5×1/3)^(1/5) = (10)^0.2 ≈ 1.585
Step 2:归一化
全部分别除以总和
`
4.3 特征向量法(最精确,Saaty 推荐)
求解矩阵 A 的最大特征值 λ_max 对应的特征向量 w:
`A × w = λ_max × w
`
标准化 w 使其分量之和为 1,即为权重向量。
三种方法的结果通常非常接近。科研论文中推荐使用特征向量法。
五、一致性检验:你的判断靠谱吗
假设你说"油耗 > 空间,空间 > 价格",那么按照逻辑,油耗必然 > 价格。如果你的判断矩阵里却写"价格 > 油耗",说明你的判断出现了矛盾。
AHP 用一致性比率(CR, Consistency Ratio)来检测这种矛盾。
公式
`CI = (λ_max - n) / (n - 1)
CR = CI / RI
`
其中 RI(随机一致性指标)是查表得到的:
n | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|---|---|---|---|---|---|---|---|---|---|---|
RI | 0 | 0 | 0.58 | 0.90 | 1.12 | 1.24 | 1.32 | 1.41 | 1.45 | 1.49 |
判定标准:
- CR < 0.1:判断矩阵通过一致性检验,可以接受
- CR ≥ 0.1:需要重新调整判断矩阵中的某些值
六、Python 完整实现
`python
import numpy as np
import pandas as pd
class AHP:
"""层次分析法——从判断矩阵到权重向量"""
def __init__(self, matrix):
"""
matrix: n×n 判断矩阵
matrix[i][j] = 指标i相对于指标j的重要性
对角线=1, 左下=1/右上
"""
self.A = np.array(matrix, dtype=float)
self.n = len(self.A)
self.weights = None
self.lambda_max = None
self.CR = None
def by_eigenvector(self):
"""特征向量法(推荐)"""
eigenvalues, eigenvectors = np.linalg.eig(self.A)
self.lambda_max = np.max(eigenvalues.real)
idx = np.argmax(eigenvalues.real)
w = np.abs(eigenvectors[:, idx].real)
self.weights = w / w.sum()
self._check_consistency()
return self
def by_geometric_mean(self):
"""方根法"""
gm = np.prod(self.A, axis=1) ** (1/self.n)
self.weights = gm / gm.sum()
# 近似 λ_max
Aw = self.A @ self.weights
self.lambda_max = np.mean(Aw / self.weights)
self._check_consistency()
return self
def by_sum_normalization(self):
"""和积法"""
col_sums = self.A.sum(axis=0)
norm = self.A / col_sums
self.weights = norm.sum(axis=1) / self.n
Aw = self.A @ self.weights
self.lambda_max = np.mean(Aw / self.weights)
self._check_consistency()
return self
def _check_consistency(self):
RI_table = {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, 10:1.49}
CI = (self.lambda_max - self.n) / (self.n - 1) if self.n > 1 else 0
RI = RI_table.get(self.n, 1.49)
self.CR = CI / RI if RI > 0 else 0
def report(self, labels=None):
if labels is None:
labels = [f'指标{i+1}' for i in range(self.n)]
print(f'λ_max = {self.lambda_max:.4f}')
print(f'CR = {self.CR:.4f} ', end='')
if self.CR < 0.1:
print('✅ 通过一致性检验')
else:
print('❌ 需要调整判断矩阵')
print()
for name, w in zip(labels, self.weights):
bar = '█' * int(w * 50)
print(f' {name:<8s} {w:.4f} {bar}')
return self
`
`python
# ── 买车示例 ──
A = np.array([
[1, 3, 2, 5, 1/3], # 油耗
[1/3, 1, 1/2, 3, 1/5], # 空间
[1/2, 2, 1, 4, 1/4], # 价格
[1/5, 1/3, 1/4, 1, 1/7], # 动力
[3, 5, 4, 7, 1 ], # 安全
])
ahp = AHP(A).by_eigenvector()
ahp.report(['油耗', '空间', '价格', '动力', '安全'])
print('\n--- 方根法对比 ---')
ahp2 = AHP(A).by_geometric_mean()
ahp2.report(['油耗', '空间', '价格', '动力', '安全'])
`
输出:
`λ_max = 5.1327
CR = 0.0296 ✅ 通过一致性检验
油耗 0.2202 ███████████
空间 0.0952 ████
价格 0.1472 ███████
动力 0.0408 ██
安全 0.4967 ████████████████████████
``
权重排序:安全(0.50) > 油耗(0.22) > 价格(0.15) > 空间(0.10) > 动力(0.04)。如果接下来用 TOPSIS 做最终评价,这几个权重直接代入加权矩阵。
七、AHP 的局限与应对
| 局限 | 表现 | 应对方法 |
|---|---|---|
| 主观性强 | 不同人给出的权重可能完全不同 | 群决策:多专家打分取平均 |
| 指标过多时难以一致 | n>9 时 CR 容易超标 | 分层处理:把指标分组,每组不超过 7 个 |
| 线性假设 | 隐含"重要性与标度成线性关系" | 灵敏度分析(第 5 期会讲) |
| 静态性 | 权重不随时间变化 | 动态 AHP(不在本系列范围内) |
改进方向:ANP(网络分析法)把指标之间的相互影响也考虑进去;模糊 AHP 用三角模糊数代替确定的 1-9 标度。本系列第 9 期会涉及模糊扩展。
八、本期小结
1.AHP 的核心:把"我觉得"转化为可计算的两两比较矩阵
2.三个步骤:构造判断矩阵 → 求特征向量 → 一致性检验
3.Saaty 1-9 标度不是拍脑袋定的,是认知心理学实验的结果
4.CR < 0.1是判断矩阵合格的硬标准——超了就得回去重新掂量
5.AHP 不是"主观"就不好——专家的经验判断本身就是高质量数据
下期预告:如果你的数据是客观测量值(温度、速度、成本),而不是主观判断,怎么从数据本身提取权重?熵权法和 CRITIC 法——从信息论的角度告诉你哪个指标最有"话语权"。
AHP 说的是:你心里那杆秤,是可以写出来的。