6

【AHP】层次分析法原理与Python实现

 2 years ago
source link: https://www.guofei.site/2020/01/05/ahp.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

【AHP】层次分析法原理与Python实现

2020年01月05日

Author: Guofei

文章归类: 5-9-应用数学 ,文章编号: 7404


版权声明:本文作者是郭飞。转载随意,但需要标明原文链接,并通知本人
原文链接:https://www.guofei.site/2020/01/05/ahp.html

Edit

层次分析法,简称AHP,层次分析法是 多目标决策问题 的一个解决方案。
它把有关的元素分解成目标、准则、方案等层次,在此基础之上进行定性和定量分析的决策方法。
该方法是美国运筹学家匹茨堡大学教授萨蒂于20世纪70年代初提出的。
人们分析问题时,经常面对一个由相互关联、相互制约的众多因素构成的复杂系统。层次分析法则为研究这类复杂的系统,提供了一种新的、简洁的、实用的决策方法。

原理

假设你有m个候选方案,有n个准则。
(例如,有m=3个候选干部,n=5个评价指标,分别是品德、才能、资历、年龄、群众关系)

比较矩阵

比较矩阵是指,你预先对评价指标有个重要度比较。
例如,选拔干部有5个条件,这个比较矩阵就是5X5的,如下:
⎛⎝⎜⎜⎜⎜⎜⎜11/21/71/51/5211/41/31/374123531/211531/311⎞⎠⎟⎟⎟⎟⎟⎟(127551/214331/71/411/21/31/51/32111/51/3311)

例如,a14=5a14=5指的是品德与年龄重要性之比是5
比较矩阵需要满足以下性质

1. 比较矩阵的尺度

比较矩阵上元素的值是拍脑袋定的,遵循以下规则:

尺度123456789重要性相同 稍强 强 明显强 绝对强

2. 正互反矩阵

对角线对称的两个数互为倒数,即aji=1aijaji=1aij
这是合乎实际情况的,例如,品德对比年龄重要程度是5,那么年龄对比品德重要程度是1/5

3. 一致性

一致性指的是,aij=aikakjaij=aikakj
这也是合乎实际情况的,例如,品德对比才能重要程度是2,才能对比资历的重要程度是4,那么品德对比资历的重要程度就是2X4=8

实际上,比较矩阵并不必须严格满足一致性,上面案例中的那个矩阵就不满足。
但是需要近似满足一致性。也就是说,需要通过一致性检验。

一致性检验 定义n阶正互反矩阵的最大特征根为λmaxλmax,那么以下成立

  • λmax≥nλmax≥n
  • 如果λmax=nλmax=n,那么这个n阶互反矩阵就是一致矩阵。

定义CI=λmax−nn−1CI=λmax−nn−1,CI越小,越接近一致矩阵。

然后还有个RI指标表:

n123456789RI000.580.91.121.241.321.411.45

如果CR=CIRI<0.1CR=CIRI<0.1,则判断有较强的一致性。

重要性

如果一个矩阵满足上面的条件,那么它最大特征值对应的特征向量就可以认为是每个维度的重要性权重。

算法流程

那么整套算法实际上是用了两次重要性权重。

准则层,从准则的重要性矩阵(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

您的支持将鼓励我继续创作!

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK