Python实现“层次分析法”及“自调节层次分析法”

姓名:张婧;学号:20021210706;学院:电子工程学院

转载于 :https://blog.csdn.net/Lwwwwwwwl/article/details/115838449

【嵌牛导读】假设我们遇到如下问题:

①对于M个方案,每个方案有N个属性,在已知各个方案每个属性值&&任意两个属性的重要程度的前提下,如何选择最优的方案?

②对于一个层级结构,在已知各底层指标相互之间的重要程度下,如何确定各底层指标对最高级指标的权值?

… …

此时,便可用层次分析法将我们的主观想法——“谁比谁重要”转换为客观度量——“权值”

【嵌牛鼻子】层次分析法  自调节层次分析法

【嵌牛提问】你知道怎么用Python实现“层次分析法”及“自调节层次分析法”吗?

【嵌牛正文】

层次分析法

层次分析法的基本思想是将复杂问题分为若干层次和若干因素,在同一层次的各要素之间简单地进行比较判断和计算,并评估每层评价指标对上一层评价指标的重要程度,确定因素权重,从而为选择最优方案提出依据。步骤如下:

(1)根据自己体系中的关联及隶属关系构建有层次的结构模型,一般分为三层,分别为最高层、中间层和最低层。

(2)构造判断矩阵

假设该层有n个评价指标u1, u2, …, un,设cij为ui相对于uj的重要程度,根据公式列出的1-9标度法,判断两两评价指标之间的重要性。

根据比较得出判断矩阵:

C=(cij)n*n其属性为cij>0, cji=1/cij,cii=1

(3)层次单排序:从下往上,对于每一层的每个判断矩阵,计算权向量和一致性检验。

计算矩阵C的最大特征根λmax及对应的特征向量(P1,P2,…, Pn)

一致性指标定义为: C I = λ max ⁡ − n n − 1 CI = \frac{{{\lambda _{\max }} - n}}{{n - 1}} CI=n−1λmax−n

CI(Consistency Ratio)称为一致性比例。CI=0时,具有完全一致性;CI接近于0,具有满意的一致性;CI越大,不一致性越严重。

一致性比率定义为: C R = C I R I < 0.1 {\rm{CR}} = \frac{{{\rm{CI}}}}{{{\rm{RI}}}} < 0.1 CR=RICI<0.1

其中RI称为随机性指标,参照表如下:

只有当CR<0.1,则认为该判断矩阵通过了一致性检验,即该矩阵自相矛盾产生的误差可忽略。将矩阵C最大特征根对应的特征向量元素作归一化处理,即可得到对应的权重集(C1,C2,…,Cn)。

(4)层次总排序

从上往下,依次计算每一层各指标对最上层指标的权值,以及每一层的综合一致性比率CR。

自调节层次分析法——赵中奇

由于层次分析法选用1-9标度构建判断矩阵,而大部分时候我们自己也不能很好度量重要性的程度,故赵中奇提出用-1,0,1三标度来构建判断矩阵。同时,自动调整判断矩阵,消除前后时刻主观比较重要性时的矛盾现象,即让矩阵变为一致性矩阵(CR=0)。构建并调整判断矩阵以及算权值向量的步骤如下:

(1)初始化m=1

a、确定比较矩阵C=(cij)n*n的第m行元素

b、划分指标集合Dm={j|j=m+1,…,n}为

Hm={j|cmj=-1,j∈Dm}、Mm={j|cmj=0,j∈Dm}与Lm={j|cmj=1,j∈Dm}

并构造集合为,其中×表示集合的笛卡尔积

c、若DLm、DMm、DHm全为空集,转d,否则令:

d、若m=n-1,转第二步,否则令m=m+1,转回a

(2)求比较矩阵C

(3)求B=(bij)n*n,其中

(4)求A=(aij)n*n的特征向量,作为各评价指标的相对权重值,其中:


实例分析

由于网上找到的代码大多只能算三层的体系,而且没有赵中奇论文中的自调节层次分析法代码。因此,自己写了一个可以计算超过3层的层次分析法和自调节层次分析法代码!

构建如下4层体系

层次分析法得到的权值

判断矩阵就不列出来了了,可以在代码里找到,得到第四层对A的权值条形图如下:

自调节层次分析法得到的权值

自调节层次分析法对高阶判断矩阵更有优势,而算低阶判断矩阵时的结果和层次分析法差不多。

代码

代码包括了层次分析法与自调节层次分析法的实例,运行的时候注释掉其中一个就行!


"""

Created on Tue Jan 26 10:12:30 2021

自适应层数的层次分析法求权值

@author: lw

"""

import numpy as np

import itertools

import matplotlib.pyplot as plt

#自适应层数的层次分析法

class AHP():

    '''

    注意:python中list与array运算不一样,严格按照格式输入!

    本层次分析法每个判断矩阵不得超过9阶,各判断矩阵必须是正互反矩阵

    FA_mx:下一层对上一层的判断矩阵集(包含多个三维数组,默认从目标层向方案层依次输入判断矩阵。同层的判断矩阵按顺序排列,且上层指标不共用下层指标)

    string:默认为'norm'(经典的层次分析法,需输入9标度判断矩阵),若为'auto'(自调节层次分析法,需输入3标度判断矩阵)

    '''


    #初始化函数

    def __init__(self,FA_mx,string='norm'):

        self.RI=np.array([0,0,0.58,0.9,1.12,1.24,1.32,1.41,1.45,1.49])  #平均随机一致性指标

        if string=='norm':

            self.FA_mx=FA_mx          #所有层级的判断矩阵

        elif string=='auto':

            self.FA_mx=[]

            for i in range(len(FA_mx)):

                  temp=[]

                  for j in range(len(FA_mx[i])):

                      temp.append(self.preprocess(FA_mx[i][j]))

                  self.FA_mx.append(temp)    #自调节层次分析法预处理后的所有层级的判断矩阵

        self.layer_num=len(FA_mx)  #层级数目

        self.w=[]                  #所有层级的权值向量

        self.CR=[]                #所有层级的单排序一致性比例

        self.CI=[]                #所有层级下每个矩阵的一致性指标

        self.RI_all=[]              #所有层级下每个矩阵的平均随机一致性指标

        self.CR_all=[]            #所有层级的总排序一致性比例

        self.w_all=[]              #所有层级指标对目标的权值



    #输入单个矩阵算权值并一致性检验(特征根法精确求解)

    def count_w(self,mx):

        n=mx.shape[0]

        eig_value, eigen_vectors=np.linalg.eig(mx)

        maxeig=np.max(eig_value)        #最大特征值

        maxindex=np.argmax(eig_value)    #最大特征值对应的特征向量

        eig_w=eigen_vectors[:,maxindex]/sum(eigen_vectors[:,maxindex])        #权值向量

        CI=(maxeig-n)/(n-1)

        RI=self.RI[n-1]

        if(n<=2 and CI==0):

                CR=0.0

        else:

            CR=CI/RI

        if(CR<0.1):

            return CI,RI,CR,list(eig_w.T)

        else:

            print('该%d阶矩阵一致性检验不通过,CR为%.3f'%(n,CR))

            return -1.0,-1.0,-1.0,-1.0


    #计算单层的所有权值与CR

    def onelayer_up(self,onelayer_mx,index):

        num=len(onelayer_mx)          #该层矩阵个数

        CI_temp=[]

        RI_temp=[]

        CR_temp=[]

        w_temp=[]

        for i in range(num):

            CI,RI,CR,eig_w=self.count_w(onelayer_mx[i])

            if(CR>0.1):

                print('第%d层的第%d个矩阵未通过一致性检验'%(index,i+1))

                return

            CI_temp.append(CI)

            RI_temp.append(RI)

            CR_temp.append(CR)

            w_temp.append(eig_w)

        self.CI.append(CI_temp)

        self.RI_all.append(RI_temp)

        self.CR.append(CR_temp)

        self.w.append(w_temp)


    #计算单层的总排序及该层总的一致性比例

    def alllayer_down(self):

        self.CR_all.append(self.CR[self.layer_num-1])

        self.w_all.append(self.w[self.layer_num-1])

        for i in range(self.layer_num-2,-1,-1):

            if(i==self.layer_num-2):

                temp=sum(self.w[self.layer_num-1],[])        #列表降维,扁平化处理,取上一层的权值向量

            CR_temp=[]

            w_temp=[]

            CR=sum(np.array(self.CI[i])*np.array(temp))/sum(np.array(self.RI_all[i])*np.array(temp))

            if(CR>0.1):

                print('第%d层的总排序未通过一致性检验'%(self.layer_num-i))

                return

            for j in range(len(self.w[i])):

                shu=temp[j]

                w_temp.append(list(shu*np.array(self.w[i][j])))

            temp=sum(w_temp,[])        #列表降维,扁平化处理,取上一层的总排序权值向量

            CR_temp.append(CR)

            self.CR_all.append(CR_temp)

            self.w_all.append(w_temp)

        return




    #计算所有层的权值与CR,层次总排序

    def run(self):

        for i in range(self.layer_num,0,-1):

            self.onelayer_up(self.FA_mx[i-1],i)

        self.alllayer_down()

        return



    #自调节层次分析法的矩阵预处理过程

    def preprocess(self,mx):

        temp=np.array(mx)

        n=temp.shape[0]

        for i in range(n-1):

            H=[j for j,x in enumerate(temp[i]) if j>i and x==-1]

            M=[j for j,x in enumerate(temp[i]) if j>i and x==0]

            L=[j for j,x in enumerate(temp[i]) if j>i and x==1]

            DL=sum([[i for i in itertools.product(H,M)],[i for i in itertools.product(H,L)],[i for i in itertools.product(M,L)]],[])

            DM=[i for i in itertools.product(M,M)]

            DH=sum([[i for i in itertools.product(L,H)],[i for i in itertools.product(M,H)],[i for i in itertools.product(L,M)]],[])

            if DL:

                for j in DL:

                  if(j[0]<j[1] and i<j[0]):

                      temp[int(j[0])][int(j[1])]=1

            if DM:

                for j in DM:

                  if(j[0]<j[1] and i<j[0]):

                      temp[int(j[0])][int(j[1])]=0

            if DH:

                for j in DH:

                  if(j[0]<j[1] and i<j[0]):

                      temp[int(j[0])][int(j[1])]=-1

        for i in range(n):

            for j in range(i+1,n):

                temp[j][i]=-temp[i][j]

        A=[]

        for i in range(n):

            atemp=[]

            for j in range(n):

                a0=0

                for k in range(n):

                    a0+=temp[i][k]+temp[k][j]

                atemp.append(np.exp(a0/n))

            A.append(atemp)

        return np.array(A) 




#%%测试函数

if __name__=='__main__' :

    '''

    # 层次分析法的经典9标度矩阵

    goal=[]            #第一层的全部判断矩阵

    goal.append(np.array([[1, 3], 

                [1/3 ,1]]))

    criteria1 = np.array([[1, 3],

                          [1/3,1]])

    criteria2=np.array([[1, 1,3],

                        [1,1,3],

                        [1/3,1/3,1]])

    c_all=[criteria1,criteria2]  #第二层的全部判断矩阵

    sample1 = np.array([[1, 1], [1, 1]])

    sample2 = np.array([[1,1,1/3], [1,1,1/3],[3,3,1]])

    sample3 = np.array([[1, 1/3], [3, 1]])

    sample4 = np.array([[1,3,1], [1 / 3, 1, 1/3], [1,3, 1]])

    sample5=np.array([[1,3],[1/3 ,1]])

    sample_all=[sample1,sample2,sample3,sample4,sample5]  #第三层的全部判断矩阵

    FA_mx=[goal,c_all,sample_all]

    A1=AHP(FA_mx)    #经典层次分析法

    A1.run()

    a=A1.CR          #层次单排序的一致性比例(从下往上)

    b=A1.w            #层次单排序的权值(从下往上)

    c=A1.CR_all      #层次总排序的一致性比例(从上往下)

    d=A1.w_all        #层次总排序的权值(从上往下)

    e=sum(d[len(d)-1],[])      #底层指标对目标层的权值

    #可视化

    plt.rcParams['font.sans-serif'] = ['SimHei']

    plt.rcParams['axes.unicode_minus'] = False

    name=['D1','D2','D3','D4','D5','D6','D7','D8','D9','D10','D11','D12']

    plt.figure()

    plt.bar(name,e)

    for i,j in enumerate(e):

        plt.text(i,j+0.005,'%.4f'%(np.abs(j)),ha='center',va='top')

    plt.title('底层指标对A的权值')

    plt.show()

    '''


    #自调节层次分析法的3标度矩阵(求在线体系的权值)

    goal=[]            #第一层的全部判断矩阵

    goal.append(np.array([[0, 1], 

                [-1,0]]))

    criteria1 = np.array([[0, 1],

                          [-1,0]])

    criteria2=np.array([[0, 0,1],

                        [0,0,1],

                        [-1,-1,0]])

    c_all=[criteria1,criteria2]  #第二层的全部判断矩阵

    sample1 = np.array([[0, 0], [0, 0]])

    sample2 = np.array([[0,0,-1], [0,0,-1],[1,1,0]])

    sample3 = np.array([[0, -1], [1, 0]])

    sample4 = np.array([[0,1,0], [-1, 0,-1], [0,1,0]])

    sample5=np.array([[0,1],[-1 ,0]])

    sample_all=[sample1,sample2,sample3,sample4,sample5]  #第三层的全部判断矩阵

    FA_mx=[goal,c_all,sample_all]

    A1=AHP(FA_mx,'auto')    #经典层次分析法

    A1.run()

    a=A1.CR          #层次单排序的一致性比例(从下往上)

    b=A1.w            #层次单排序的权值(从下往上)

    c=A1.CR_all      #层次总排序的一致性比例(从上往下)

    d=A1.w_all        #层次总排序的权值(从上往下)

    e=sum(d[len(d)-1],[])      #底层指标对目标层的权值

    #可视化

    plt.rcParams['font.sans-serif'] = ['SimHei']

    plt.rcParams['axes.unicode_minus'] = False

    name=['D1','D2','D3','D4','D5','D6','D7','D8','D9','D10','D11','D12']

    plt.figure()

    plt.bar(name,e)

    for i,j in enumerate(e):

        plt.text(i,j+0.005,'%.4f'%(np.abs(j)),ha='center',va='top')

    plt.title('底层指标对A的权值')

    plt.show()

————————————————

版权声明:本文为CSDN博主「易~lw」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/Lwwwwwwwl/article/details/115838449

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容