【学习笔记】Matlab和python双语言的学习(TOPSIS法)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

通过模型算法,熟练对Matlab和python的应用。
学习视频链接:
https://www.bilibili.com/video/BV1EK41187QF?p=4&vd_source=67471d3a1b4f517b7a7964093e62f7e6

一、TOPSIS法

1.模型原理

TOPSIS法是一种理想目标相似性的顺序选优技术,在多目标决策分析中是一种非常有效的方法。它通过归一化后(去量纲化)的数据规范化矩阵,找出多个目标中最优目标和最劣目标(分别用理想解和反理想解表示),分别计算各评价目标与理想解和反理想解的距离,获得各目标与理想解的贴近度,按理想解贴近度的大小排序,以此作为评价目标优劣的依据。贴近度取值在0~1之间,该值愈接近1,表示相应的评价目标越接近最优水平;反之,该值愈接近0,表示评价目标越接近最劣水平。(我的理解是,在 n 维空间中,各评价目标与理想解的距离;距离越近,越优。)
TOPSIS法的优点在于其计算过程简单明了,能够较好地处理多个评价指标,且考虑了各指标的权重及其对决策的影响。该方法被广泛应用于各种决策分析场景,如供应商选择、项目评估和绩效考核等。

2.基本步骤

(1)将原始矩阵正向化
将原始矩阵正向化,就是要将所有的指标类型统一转化为极大型指标。
(2)正向矩阵标准化
标准化的方法有很多种,其主要目的就是去除量纲的影响,保证不同评价指标在同一数量级,且数据大小排序不变。
(3)计算得分并归一化

(1)原始矩阵正向化

将所有的指标类型统一转化为极大型指标(极大型指标就是,越接近理想值,值越大)。
在这里插入图片描述
各类型指标的转化公式:
在这里插入图片描述
视频中江北老师举了找对象的例子:
在这里插入图片描述
对颜值、脾气、身高、体重四个指标进行考察。其中颜值是极大型指标,脾气(争吵次数)是极小型指标;身高是中间型指标,对相亲对象身高要求在165cm;体重是区间型指标,对相亲对象体重要求在90-100(斤)之间。

(2)正向矩阵标准化

正向矩阵标准化的目的是消除不同指标量纲的影响。
在这里插入图片描述
注:在标准化后一般要考虑权重,在本实例中认为各个指标的权重相同。

(3)计算得分并归一化

得到标准化矩阵Z后,定义最大值和最小值。
在这里插入图片描述
最大值为 Z 的每一列最大值组成的行向量。
在这里插入图片描述
最小值为 Z 的每一列最小值组成的行向量。
定义第 i (i = 1,2,…,n)个评价对象与最大值的距离:
在这里插入图片描述
定义第 i (i = 1,2,…,n)个评价对象与最小值的距离:
在这里插入图片描述
接下来我们计算得出第 i (i = 1,2,…,n)个评价对象未归一化的得分:
在这里插入图片描述
显然可得:Si 在 0 到 1 之间,且 Si 越大 Di+ 越小,即越接近最大值。

二、代码实现----Matlab

在本实例中,对不同指标有不同的处理公式,所以在代码中加入了函数,对不同指标调用不同函数进行处理。
Matlab中,对函数的定义:

% function[输出变量] = 函数名称(输入变量)
% 函数的中间部分都是函数体
% 函数的最后要用end结尾
% 输出变量和输入变量可以有多个,用逗号隔开
% function[a,b,c] = test(d,e,f)
%     a = d + e;
%     b = e + f;
%     c = f + d;
% end

1.主程序

clear;clc
% 1.判断是否需要正向化

% A = [9, 10, 175, 120; 8, 7, 164, 80; 6, 3, 157, 90]

X = input('指标矩阵A=');     %%输入判断矩阵
[n,m] = size(X);
disp(['共有' num2str(n) '个评价对象,' num2str(m) '个评价指标'])

Judge = input(['这' num2str(m) '个评价指标是否需要经过正向化处理,需要请输入1,不需要请输入0:']) ;

if Judge == 1
    Position = input('请输入需要进行正向化处理的指标所在的列.例如第2、3、6三列需要处理,那么你需要输入[2,3,6]:');  %[2,3,4]
    disp('请输入需要处理的这些列的指标类型(1:极小型, 2:中间型, 3:区间型)');
    Type = input('例如:第2列是极小型, 第3列是区间型, 第6列是中间型,就输入[1, 3, 2]:');  %[1,2,3]
    %注意,Position和Type是两个同纬度的行向量
    for i = 1 : size(Position,2)
        %循环的次数
        X(:,Position(i)) = Positivization(X(:,Position(i)),Type(i),Position(i));
    % Positivization()是自己定义的函数,作用是进行正向化
    % 第一个参数是要正向化处理的那一列向量X(:,Position(i))
    % 第二个参数是对应这一列的指标类型
    % 第三个参数是告诉函数我们正在处理的是原始矩阵的哪一列
    % 该函数有一个返回值,它返回正向化之后的指标,我们可以将其直接赋值给原始处理的那一列向量
    end
    disp('正向化后的矩阵X = ')
    disp(X)
else 
    
end
% 矩阵标准化
for i = 1 : m
    X(:,i) = X(:,i)./(sum(X(:,i).^2).^(1/2));      %Z = X ./ repmat(sum(X.*X).^0.5, n, 1);
end
disp('标准化之后的矩阵:')
disp(X)
% 计算得分
% 求每一列的最大值
maxc = max(X);
minc = min(X);
DI_max = zeros(n,1);  
DI_min = zeros(n,1);
for i = 1 : n
    DI_max(i) = (sum((maxc - X(i,:)).^2)).^(1/2);     %DI_max = sum([(Z - repmat(max(Z), n, 1)) .^2 ], 2) .^0.5;
    DI_min(i) = (sum((minc - X(i,:)).^2)).^(1/2);
end
% 求出的分
SI = DI_min ./ (DI_max + DI_min);
% 归一化
Stand_S = SI ./ (sum(SI)) *100;
disp('最终得分为:')
disp(Stand_S)
[Stand_S,index] = sort(Stand_S,'descend');
disp('最终排名为:')
disp(index)

Matlab中sort()函数的应用。

% sort(A)若A是向量不管是行还是列向量,默认都是对A进行升序排列,sort(A,'descend')是降序排列
% sort(A)若A是矩阵,默认对A的各列进行升序排列
% sort(A,dim)
% dim=1时,等效sort(A)
% dim=2时,表示对A中各行元素升序排列
% A = [2,1,3,8]
% Matlab中给一个一维向量排序是使用sort函数:sort(A),排列是按升序进行的,代排列的向量是A
% 若欲保留排列前的索引,则可用[sA,index] = sort(sA,'descend'),排序后,sA是排序好的向量,index是向量sA中对A的索引

2.正向化处理函数

function [posit_x] = Positivization(x,type,i)
% 输入变量有三个;
% x: 需要正向化处理的指标对应的原始列向量
% type: 指标类型(1:极小型, 2:中间型, 3:区间型)
% i: 正在处理的是原始矩阵的哪一列
% 输出变量posit_x表示:正向化后的列向量
    if type == 1  %极小型
        disp(['第' num2str(i) '列是极小型,正在正向化'])
        posit_x = Min2Max(x);
        disp(['第' num2str(i) '列极小型正向化处理完成'])
        disp('~~~~~~~~~~~~~~分界线~~~~~~~~~~~~~~~')
    elseif type == 2  %中间型
        disp(['第' num2str(i) '列是中间型'])
        best = input('请输入最佳的那一个值:');
        posit_x = Mid2Max(x,best);
        disp(['第' num2str(i) '列中间型正向化处理完成'])
        disp('~~~~~~~~~~~~~~分界线~~~~~~~~~~~~~~~')
    elseif type == 3  %区间型
        disp(['第' num2str(i) '列是区间型'])
        a = input('请输入区间的下界:');
        b = input('请输入区间的上界:');
        posit_x = Inter2Max(x,a,b);
        disp(['第' num2str(i) '列区间型正向化处理完成'])
        disp('~~~~~~~~~~~~~~分界线~~~~~~~~~~~~~~~')
    end
end

3.极小型正向化函数

function [posit_x] = Min2Max(x)
    posit_x = max(x) - x ;
end

4.中间型正向化函数

function [posit_x] = Mid2Max(x,best)
    M = max(abs(x - best));
    posit_x = 1 - abs(x - best)/M ; 
end

5.区间型正向化函数

function [posit_x] = Inter2Max(x,a,b)
    M = max((a - min(x)),(max(x) - b));
    posit_x = zeros(size(x,1),1);  %创建0矩阵,size(x,1)行1列;ones(3)->创建3*3的单位矩阵
    for i = 1 : size(x,1)
        if x(i) < a
            posit_x(i) = 1 - (a - x(i))/M
        elseif x(i) > b
            posit_x(i) = 1 - (x(i) - b)/M
        else 
            posit_x(i) = 1
        end
    end
end

在矩阵运算中,可以使用 repmat()函数,减少循环语句,提高内存的使用效率和代码运行速度。

二、代码实现----python

1.主函数

import numpy as np

n = int(input("有几个评价对象:"))   # 3
m = int(input("有几个评价指标:"))   # 4

kind = input("请输入评价指标的类型").split(" ")   # 1 2 3 :1是极小型, 2是区间型, 3是中间型

A = np.zeros(shape=(n,m))

for i in range(n):
    A[i] = input("请输入指标矩阵:").split(" ")
    A[i] = list(map(float, A[i]))    # 将矩阵A转化成float型,并转化成列表

Judge = input("这" + str(m) + "个评价指标是否需要经过正向化处理,需要请输入1,不需要请输入0:")   

if Judge == '1':
    position = input('请输入需要进行正向化处理的指标所在的列.例如第2、3、6三列需要处理,那么你需要输入[2 3 6]:').split(' ')   #[1 2 3]
    Position = list(map(int,position))
    for i in range(len(Position)):
    #循环的次数
        A[:,Position[i]] = Positivization(A[:,Position[i]],kind[i],Position[i])
    # Positivization()是自己定义的函数,作用是进行正向化
    # 第一个参数是要正向化处理的那一列向量X(:,Position(i))
    # 第二个参数是对应这一列的指标类型
    # 第三个参数是告诉函数我们正在处理的是原始矩阵的哪一列
    # 该函数有一个返回值,它返回正向化之后的指标,我们可以将其直接赋值给原始处理的那一列向量
    print(A)

python中使用input()读取键盘输入时,得到的数据类型是字符串,可以使用以下代码,将字符串转化为矩阵:

import numpy as np

A = np.zeros(shape=(n,m))
# n, m 均在上文有定义
for i in range(n):
    A[i] = input("请输入指标矩阵:").split(" ")
    A[i] = list(map(float, A[i]))    # 将矩阵A转化成float型,并转化成列表

2.正向化函数

def Positivization(x,kind,position):
    if kind == '1':
        print("第", int(position) + 1, "列向量是极小型,需要进行正向化")
        print("正在进行正向化处理")
        posit_x = Min2Max(x)
        print("正向化处理完成")
        print("~~~~~~~~~~分割线~~~~~~~~~")
    elif kind == '2':
        print("第", int(position) + 1, "列向量是中间型,需要进行正向化")
        best = int(input("请输入最优值:"))
        print("正在进行正向化处理")
        posit_x = Mid2Max(x, best)
        print("正向化处理完成")
        # print(A[:,2])
        print("~~~~~~~~~~分割线~~~~~~~~~")
    elif kind == '3':
        print("第", int(position) + 1, "列向量是区间型,需要进行正向化")
        a = int(input("请输入区间下限:"))
        b = int(input("请输入区间上限:"))
        print("正在进行正向化处理")
        posit_x = Inter2Max(x, a, b)
        print("正向化处理完成")
        print("~~~~~~~~~~分割线~~~~~~~~~")
    return posit_x

3.极小型正向化函数

def Min2Max(x):
    posit_x = max(x) - x
    return posit_x

4.中间型正向化函数

def Mid2Max(x, best):
    M = max(abs(x - best))
    posit_x = 1 - abs(x - best)/M
    return posit_x

5.区间型正向化函数

def Inter2Max(x, a, b):
    M = max((a - min(x)), (max(x) - b))
    posit_x = np.zeros(n)  #创建0向量(1*n)
    for i in range(n):
        if x[i] < a:
            posit_x[i] = 1 - (a - x[i]) / M
        elif x[i] > b:
            posit_x[i] = 1 - (x[i] - b ) / M
        else:
            posit_x[i] = 1
    return posit_x

总结

本文介绍了TOPSIS法的使用,并分别使用Matlab和python进行代码编写。

  • 20
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值