層次分析法,簡稱AHP,層次分析法是 多目標決策問題 的一個解決方案。
它把有關的元素分解成目標、準則、方案等層次,在此基礎之上進行定性和定量分析的決策方法。
該方法是美國運籌學家匹茨堡大學教授薩蒂於20世紀70年代初提出的。
人們分析問題時,經常面對一個由相互關聯、相互制約的衆多因素構成的複雜系統。層次分析法則爲研究這類複雜的系統,提供了一種新的、簡潔的、實用的決策方法。
原理
假設你有m個候選方案,有n個準則。
(例如,有m=3個候選幹部,n=5個評價指標,分別是品德、才能、資歷、年齡、羣衆關係)
比較矩陣
比較矩陣是指,你預先對評價指標有個重要度比較。
例如,選拔幹部有5個條件,這個比較矩陣就是5X5的,如下:
例如,指的是品德與年齡重要性之比是5
比較矩陣需要滿足以下性質
1. 比較矩陣的尺度
比較矩陣上元素的值是拍腦袋定的,遵循以下規則:
尺度 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
重要性 | 相同 | 稍強 | 強 | 明顯強 | 絕對強 |
2. 正互反矩陣
對角線對稱的兩個數互爲倒數,即
這是合乎實際情況的,例如,品德對比年齡重要程度是5,那麼年齡對比品德重要程度是1/5
3. 一致性
一致性指的是,
這也是合乎實際情況的,例如,品德對比才能重要程度是2,才能對比資歷的重要程度是4,那麼品德對比資歷的重要程度就是2X4=8
實際上,比較矩陣並不必須嚴格滿足一致性,上面案例中的那個矩陣就不滿足。
但是需要近似滿足一致性。也就是說,需要通過一致性檢驗。
一致性檢驗
定義n階正互反矩陣的最大特徵根爲,那麼以下成立
- 如果,那麼這個n階互反矩陣就是一致矩陣。
定義,CI越小,越接近一致矩陣。
然後還有個RI指標表:
n | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
RI | 0 | 0 | 0.58 | 0.9 | 1.12 | 1.24 | 1.32 | 1.41 | 1.45 |
如果,則判斷有較強的一致性。
重要性
如果一個矩陣滿足上面的條件,那麼它最大特徵值對應的特徵向量就可以認爲是每個維度的重要性權重。
算法流程
那麼整套算法實際上是用了兩次重要性權重。
準則層,從準則的重要性矩陣(nxn矩陣)中,抽取重要性權重。它的現實意義是 每個準則的重要程度。
也就是說,輸入的是nxn一個矩陣,值是每個準則兩兩之間重要度,輸出的是這n個準則各自的權重。
方案層,對每個準則,m個方案都有個mxm矩陣(總共是n個mxm矩陣)。也就是說,對每個準則,都可以算出m個方案的重要性權重。
然後n個重要性權重組合起來,與準則層的重要性權重相乘。就得到了每個方案的重要性權重。
代碼
import numpy as np
import pandas as pd
import warnings
class AHP:
def __init__(self, criteria, b):
self.RI = (0, 0, 0.58, 0.9, 1.12, 1.24, 1.32, 1.41, 1.45, 1.49)
self.criteria = criteria
self.b = b
self.num_criteria = criteria.shape[0]
self.num_project = b[0].shape[0]
def cal_weights(self, input_matrix):
input_matrix = np.array(input_matrix)
n, n1 = input_matrix.shape
assert n == n1, '不是一個方陣'
for i in range(n):
for j in range(n):
if np.abs(input_matrix[i, j] * input_matrix[j, i] - 1) > 1e-7:
raise ValueError('不是反互對稱矩陣')
eigenvalues, eigenvectors = np.linalg.eig(input_matrix)
max_idx = np.argmax(eigenvalues)
max_eigen = eigenvalues[max_idx].real
eigen = eigenvectors[:, max_idx].real
eigen = eigen / eigen.sum()
if n > 9:
CR = None
warnings.warn('無法判斷一致性')
else:
CI = (max_eigen - n) / (n - 1)
CR = CI / self.RI[n]
return max_eigen, CR, eigen
def run(self):
max_eigen, CR, criteria_eigen = self.cal_weights(self.criteria)
print('準則層:最大特徵值{:<5f},CR={:<5f},檢驗{}通過'.format(max_eigen, CR, '' if CR < 0.1 else '不'))
print('準則層權重={}\n'.format(criteria_eigen))
max_eigen_list, CR_list, eigen_list = [], [], []
for i in self.b:
max_eigen, CR, eigen = self.cal_weights(i)
max_eigen_list.append(max_eigen)
CR_list.append(CR)
eigen_list.append(eigen)
pd_print = pd.DataFrame(eigen_list,
index=['準則' + str(i) for i in range(self.num_criteria)],
columns=['方案' + str(i) for i in range(self.num_project)],
)
pd_print.loc[:, '最大特徵值'] = max_eigen_list
pd_print.loc[:, 'CR'] = CR_list
pd_print.loc[:, '一致性檢驗'] = pd_print.loc[:, 'CR'] < 0.1
print('方案層')
print(pd_print)
# 目標層
obj = np.dot(criteria_eigen.reshape(1, -1), np.array(eigen_list))
print('\n目標層', obj)
print('最優選擇是方案{}'.format(np.argmax(obj)))
return obj
if __name__ == '__main__':
# 準則重要性矩陣
criteria = np.array([[1, 2, 7, 5, 5],
[1 / 2, 1, 4, 3, 3],
[1 / 7, 1 / 4, 1, 1 / 2, 1 / 3],
[1 / 5, 1 / 3, 2, 1, 1],
[1 / 5, 1 / 3, 3, 1, 1]])
# 對每個準則,方案優劣排序
b1 = np.array([[1, 1 / 3, 1 / 8], [3, 1, 1 / 3], [8, 3, 1]])
b2 = np.array([[1, 2, 5], [1 / 2, 1, 2], [1 / 5, 1 / 2, 1]])
b3 = np.array([[1, 1, 3], [1, 1, 3], [1 / 3, 1 / 3, 1]])
b4 = np.array([[1, 3, 4], [1 / 3, 1, 1], [1 / 4, 1, 1]])
b5 = np.array([[1, 4, 1 / 2], [1 / 4, 1, 1 / 4], [2, 4, 1]])
b = [b1, b2, b3, b4, b5]
a = AHP(criteria, b).run()
輸出:
準則層:最大特徵值5.072084,CR=0.014533,檢驗通過 準則層權重=[0.47583538 0.26360349 0.0538146 0.09806829 0.10867824] 方案層 方案0 方案1 方案2 最大特徵值 CR 一致性檢驗 準則0 0.081935 0.236341 0.681725 3.001542 8.564584e-04 True 準則1 0.595379 0.276350 0.128271 3.005535 3.075062e-03 True 準則2 0.428571 0.428571 0.142857 3.000000 -4.934325e-16 True 準則3 0.633708 0.191921 0.174371 3.009203 5.112618e-03 True 準則4 0.344545 0.108525 0.546931 3.053622 2.978976e-02 True 目標層 [[0.318586 0.23898522 0.44242878]] 最優選擇是方案2