目录
1.TOPSIS方法简介
前文所讲的层次分析法和熵权法主要用来确定每个指标在每个方案中所占权重,在得到权重后与每个指标对应的值求积再求和就能得到每个方案的综合评分,因此也可以用来进行评价。但是其也有各自的局限性,例如层次分析法主观性太强并且方案层不能太多,否则很难通过一致性检验,而熵权法就只适用于计算权重,其所得评价结果也具有一定局限性。
而TOPSIS是一种综合评价方法,可以弥补层次分析法的一些缺点:
(1)优劣解距离法可以充分利用原始数据信息,且其结果能充分反应各评价方案与最优方案的接近程度。
(2)对样本容量没有严格限制,数据计算简单易行,无需数据检验。(topsis法适用于两个以上)
2.算法流程
3.算法步骤
3.1 原始矩阵正向化(即统一转化为极大指标)
3.1.1 极小型指标转化为极大型指标
极小型指标指其数值越小所代表的效果越好,例如排名,数值越小越好。其转换方法有以下两种:
3.1.2 中间型指标转化为极大型指标
中间型指标指其数值在某个中间值时为其最佳值,例如PH值,PH=7即为其中间值(最佳值)。其转换方法如下:
参数M(到最佳值距离的最大值):
式中表示最佳值,例如PH最佳值为7。
则
3.1.3 区间型指标转化为极大型指标
区间型指标指其数值落在某一个区间内时均为最佳值,例如人的体温区间为[36,37.2]。假设其最佳区间为[a,b],则其转换方法如下:
3.2 正向化矩阵标准化
假设已正向化的矩阵有n个评价对象,m个指标。
3.3 计算得分并归一化
每列最大值:
每列最小值:
第i个评价对象(方案)与最大值距离:
第i个评价对象(方案)与最小值距离:
式中为每个指标的权重,默认权重都相同,在实际计算中可以根据实际情况对每个指标赋予不同的权重,其主要方法有层次分析法和熵权法,具体操作步骤在前文有详细解说。
则第i个评价对象的得分(未归一化):
4.案例分析
4.1 题目简介
已知判断某一水域水质情况的好坏时可以通过x1-x9共9种指标来评判,现有A-K共11条河流的指标测量情况,请以此判断各河流水质的优良情况。其中x1,x3,x6,x7为极大型指标,x2,x5为中间型指标,其中间值分别为45、52,x4,x8,x9为区间型指标,其最佳区间依次为[30,65],[40,50],[30,70]。
河流 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 |
A | 88 | 63 | 17 | 50 | 53 | 49 | 64 | 24 | 32 |
B | 75 | 1 | 2 | 43 | 14 | 53 | 31 | 18 | 42 |
C | 79 | 48 | 73 | 62 | 43 | 48 | 72 | 76 | 98 |
D | 73 | 95 | 9 | 34 | 20 | 46 | 54 | 4 | 59 |
E | 36 | 70 | 53 | 5 | 100 | 6 | 74 | 25 | 43 |
F | 40 | 46 | 92 | 60 | 68 | 19 | 2 | 76 | 8 |
G | 20 | 78 | 35 | 98 | 83 | 70 | 35 | 58 | 1 |
H | 67 | 29 | 38 | 3 | 32 | 27 | 3 | 47 | 75 |
I | 38 | 30 | 28 | 72 | 46 | 20 | 50 | 42 | 74 |
J | 95 | 25 | 20 | 42 | 13 | 54 | 56 | 54 | 82 |
K | 40 | 32 | 27 | 14 | 22 | 59 | 6 | 81 | 53 |
4.2原始矩阵正向化
4.2.1 极小型指标转化为极大型指标
河流 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 |
A | 7 | 63 | 75 | 50 | 53 | 21 | 10 | 24 | 32 |
B | 20 | 1 | 90 | 43 | 14 | 17 | 43 | 18 | 42 |
C | 16 | 48 | 19 | 62 | 43 | 22 | 2 | 76 | 98 |
D | 22 | 95 | 83 | 34 | 20 | 24 | 20 | 4 | 59 |
E | 59 | 70 | 39 | 5 | 100 | 64 | 0 | 25 | 43 |
F | 55 | 46 | 0 | 60 | 68 | 51 | 72 | 76 | 8 |
G | 75 | 78 | 57 | 98 | 83 | 0 | 39 | 58 | 1 |
H | 28 | 29 | 54 | 3 | 32 | 43 | 71 | 47 | 75 |
I | 57 | 30 | 64 | 72 | 46 | 50 | 24 | 42 | 74 |
J | 0 | 25 | 72 | 42 | 13 | 16 | 18 | 54 | 82 |
K | 55 | 32 | 65 | 14 | 22 | 11 | 68 | 81 | 53 |
4.2.2 中间型指标转化为极大型指标
河流 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 |
A | 7 | 0.64 | 75 | 50 | 0.979167 | 21 | 10 | 24 | 32 |
B | 20 | 0.12 | 90 | 43 | 0.208333 | 17 | 43 | 18 | 42 |
C | 16 | 0.94 | 19 | 62 | 0.8125 | 22 | 2 | 76 | 98 |
D | 22 | 0 | 83 | 34 | 0.333333 | 24 | 20 | 4 | 59 |
E | 59 | 0.5 | 39 | 5 | 0 | 64 | 0 | 25 | 43 |
F | 55 | 0.98 | 0 | 60 | 0.666667 | 51 | 72 | 76 | 8 |
G | 75 | 0.34 | 57 | 98 | 0.354167 | 0 | 39 | 58 | 1 |
H | 28 | 0.68 | 54 | 3 | 0.583333 | 43 | 71 | 47 | 75 |
I | 57 | 0.7 | 64 | 72 | 0.875 | 50 | 24 | 42 | 74 |
J | 0 | 0.6 | 72 | 42 | 0.1875 | 16 | 18 | 54 | 82 |
K | 55 | 0.74 | 65 | 14 | 0.375 | 11 | 68 | 81 | 53 |
4.2.3 区间型指标转化为极大型指标
河流 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 |
A | 7 | 0.64 | 75 | 1 | 0.979167 | 21 | 10 | 0.555556 | 1 |
B | 20 | 0.12 | 90 | 1 | 0.208333 | 17 | 43 | 0.388889 | 1 |
C | 16 | 0.94 | 19 | 1 | 0.8125 | 22 | 2 | 0.277778 | 0.034483 |
D | 22 | 0 | 83 | 1 | 0.333333 | 24 | 20 | 0 | 1 |
E | 59 | 0.5 | 39 | 0.242424 | 0 | 64 | 0 | 0.583333 | 1 |
F | 55 | 0.98 | 0 | 1 | 0.666667 | 51 | 72 | 0.277778 | 0.241379 |
G | 75 | 0.34 | 57 | 0 | 0.354167 | 0 | 39 | 0.777778 | 0 |
H | 28 | 0.68 | 54 | 0.181818 | 0.583333 | 43 | 71 | 1 | 0.827586 |
I | 57 | 0.7 | 64 | 0.787879 | 0.875 | 50 | 24 | 1 | 0.862069 |
J | 0 | 0.6 | 72 | 1 | 0.1875 | 16 | 18 | 0.888889 | 0.586207 |
K | 55 | 0.74 | 65 | 0.515152 | 0.375 | 11 | 68 | 0.138889 | 1 |
4.3 正向化矩阵标准化
河流 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 |
A | 0.049036 | 0.301914 | 0.365241 | 0.378561 | 0.514284 | 0.182692 | 0.071387 | 0.265794 | 0.382607 |
B | 0.140104 | 0.056609 | 0.43829 | 0.378561 | 0.109422 | 0.147893 | 0.306963 | 0.186056 | 0.382607 |
C | 0.112083 | 0.443436 | 0.092528 | 0.378561 | 0.426746 | 0.191391 | 0.014277 | 0.132897 | 0.013193 |
D | 0.154114 | 0 | 0.404201 | 0.378561 | 0.175075 | 0.20879 | 0.142773 | 0 | 0.382607 |
E | 0.413306 | 0.23587 | 0.189926 | 0.091772 | 0 | 0.556774 | 0 | 0.279084 | 0.382607 |
F | 0.385285 | 0.462305 | 0 | 0.378561 | 0.35015 | 0.44368 | 0.513984 | 0.132897 | 0.092353 |
G | 0.525388 | 0.160392 | 0.277584 | 0 | 0.186017 | 0 | 0.278408 | 0.372112 | 0 |
H | 0.196145 | 0.320783 | 0.262974 | 0.068829 | 0.306382 | 0.374083 | 0.506846 | 0.478429 | 0.31664 |
I | 0.399295 | 0.330218 | 0.311673 | 0.29826 | 0.459573 | 0.43498 | 0.171328 | 0.478429 | 0.329834 |
J | 0 | 0.283044 | 0.350632 | 0.378561 | 0.09848 | 0.139194 | 0.128496 | 0.42527 | 0.224287 |
K | 0.385285 | 0.349088 | 0.316543 | 0.195016 | 0.19696 | 0.095696 | 0.48543 | 0.066448 | 0.382607 |
4.4 计算得分
由熵权法可得9个指标权重如下(详情见文章):
指标 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 |
权重 | 0.09737 | 0.081928 | 0.127874 | 0.121424 | 0.17078 | 0.082974 | 0.133028 | 0.092766 | 0.091855 |
输入权重可以得到11条河流水质综合评分如下
河流 | A | B | C | D | E | F | G | H | I | J | K |
得分 | 0.562987 | 0.528023 | 0.504048 | 0.490226 | 0.482329 | 0.503079 | 0.497164 | 0.511509 | 0.528856 | 0.521446 | 0.524432 |
由此可见水质优劣排序依次为:A>I>B>K>J>H>C>F>G>D>E
而直接使用熵权法得到的水质情况为:C>G>E>A>F>J>I>D>K>H>B
对比可见还是有很大差别,这是因为熵权法最后只是得到了各指标的权重,直接用权重求出的得分就很笼统,没有对数据进行细化处理。
5.代码示例
5.1 代码展示
package TOPSIS;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
import jxl.Cell;
import jxl.Sheet;
import jxl.Workbook;
import jxl.read.biff.BiffException;
import jxl.write.WriteException;
public class topsis {
private String filepath;//文件路径
private double[][] orig = new double[1000][1000];//原始矩阵
private double[][] pos = new double[1000][1000];//正向化后的矩阵
private double[][] stad = new double[1000][1000];//标准化后的矩阵
private double[][] uniform = new double[1000][1000];//归一化后的矩阵
int rows,cols;//存储Excel的行和列数
Scanner input = new Scanner(System.in);
//读取数据
public void read(String filepath) throws IOException, BiffException,WriteException {
//创建输入流
InputStream stream = new FileInputStream(filepath);
//获取Excel文件对象
Workbook rwb = Workbook.getWorkbook(stream);
//获取文件的指定工作表 默认的第一个
Sheet sheet = rwb.getSheet("Sheet1");
rows = sheet.getRows();
cols = sheet.getColumns();
//row为行
for(int i=0;i<sheet.getRows();i++) {
for(int j=0;j<sheet.getColumns();j++) {
//orig = new double[sheet.getRows()][sheet.getColumns()];
String[] str = new String[sheet.getColumns()];
Cell cell = null;
cell = sheet.getCell(j,i);
str[j] = cell.getContents();
orig[i][j] = Double.valueOf(str[j]);
pos[i][j] = Double.valueOf(str[j]);
stad[i][j] = Double.valueOf(str[j]);
uniform[i][j] = Double.valueOf(str[j]);
}
}
//输出原始矩阵,行为评价对象,列为评价指标
System.out.println("原始矩阵为:");
for(int i=0;i<rows;i++) {
for(int j=0;j<cols;j++) {
System.out.print(orig[i][j]+" ");
}
System.out.println();
}
}
//原始矩阵正向化
//极小型指标——>极大型指标
public void positive1() {
System.out.println("有几列数据(指标)要将极小型转化为极大型:");
int num = input.nextInt();
System.out.println("输入要将极小型转化为极大型的列数:");//以空格分分开
int[] col = new int[num];//存储要转化的列
for(int i=0;i<num;i++) {
col[i] = input.nextInt();
//System.out.println(col[i]);
}
//挑选每列最大值
double[] colmaxs = new double[num];//存储每列的最大值
for(int j=0;j<num;j++) {
double colmax = 0;
int x = col[j];
for(int i=0;i<rows;i++) {
if(orig[i][x] >= colmax) {
colmax = orig[i][x];
//System.out.println("orig:"+orig[i][x]);
}
}
colmaxs[j] = colmax;
//System.out.println(colmaxs[j]);
//每列最大值减去当前值:max-x
for(int i=0;i<rows;i++) {
pos[i][x] = colmaxs[j] - pos[i][x];
}
}
System.out.println("(极小型指标——>极大型指标)pos1矩阵为:");
for(int i=0;i<rows;i++) {
for(int j=0;j<cols;j++) {
System.out.print(pos[i][j]+" ");
}
System.out.println();
}
}
//中间型指标——>极大型指标
public void positive2() {
System.out.println("有几列数据(指标)要将中间型转化为极大型:");
int num = input.nextInt();
System.out.println("输入要将中间型转化为极大型的列数:");//以空格分分开
int[] col = new int[num];//存储要转化的列
for(int i=0;i<num;i++) {
col[i] = input.nextInt();
//System.out.println(col[i]);
}
System.out.println("依次输入对应列的最佳值:");
double[] best = new double[num];
for(int i=0;i<num;i++) {
System.out.println("第"+col[i]+"列的最佳值:");
best[i] = input.nextDouble();
//System.out.println(best[i]);
}
//计算每个元素到最佳距离的值以及最大值、正向化矩阵
for(int j=0;j<num;j++) {
int x = col[j];
//System.out.println(colmaxs[j]);
double[][] dis = new double[rows][num];
double[] MAX = new double[num];//到最佳距离的最大值
double max = 0;
for(int i=0;i<rows;i++) {
double a = pos[i][x] - best[j];
//System.out.println(a);
dis[i][j] = Math.abs(a);
//System.out.println(dis[i]);
if(dis[i][j] >= max) {
max = dis[i][j];
MAX[j] = max;
}
}
System.out.println("MAX:"+MAX[j]);
//System.out.println("dis矩阵为:");
for(int i=0;i<rows;i++) {
pos[i][x] = 1-(dis[i][j]/MAX[j]);
//System.out.println(dis[i][j]);
}
}
System.out.println("(中间型指标——>极大型指标)pos2矩阵为:");
for(int i=0;i<rows;i++) {
for(int j=0;j<cols;j++) {
System.out.print(pos[i][j]+" ");
}
System.out.println();
}
}
//区间型指标——>极大型指标
public void positive3() {
System.out.println("有几列数据(指标)要将区间型转化为极大型:");
int num = input.nextInt();
System.out.println("输入要将区间型转化为极大型的列数:");//以空格分分开
int[] col = new int[num];//存储要转化的列
for(int i=0;i<num;i++) {
col[i] = input.nextInt();
//System.out.println(col[i]);
}
System.out.println("输入每列对应的区间[a,b]:");
double[][] sec = new double[num][2];//存储区间
for(int i=0;i<num;i++) {
int a=col[i];
System.out.println("输入第"+a+"列的区间:");
for(int j=0;j<2;j++) {
sec[i][j] = input.nextDouble();
}
}
double[] colmaxs = new double[num];//存储每列的最大值
double[] colmins = new double[num];//存储每列的最小值
double[] MAX = new double[num];//到a,b距离的最大值
for(int j=0;j<num;j++) {
double colmax = 0;
int x = col[j];
for(int i=0;i<rows;i++) {
if(orig[i][x] >= colmax) {
colmax = orig[i][x];
//System.out.println("orig:"+orig[i][x]);
}
}
colmaxs[j] = colmax;
double colmin = 0;
for(int i=0;i<rows;i++) {
if(i==0) {
colmin = orig[i][x];
}
else if(orig[i][x] <= colmin) {
colmin = orig[i][x];
//System.out.println("orig:"+orig[i][x]);
}
}
colmins[j] = colmin;
MAX[j] = Math.max(sec[j][0] - colmins[j], colmaxs[j] - sec[j][1]);
for(int i=0;i<rows;i++) {
if(pos[i][x] < sec[j][0]) {
pos[i][x] = 1-((sec[j][0] - pos[i][x])/MAX[j]);
}
else if(pos[i][x] > sec[j][1]) {
pos[i][x] = 1-((pos[i][x] - sec[j][1])/MAX[j]);
}
else {
pos[i][x] = 1;
}
}
}
/*
for(int j=0;j<num;j++) {
int x = col[j];
System.out.println("第"+x+"列最大值:");
System.out.println(colmaxs[j]);
System.out.println("第"+x+"列最小值:");
System.out.println(colmins[j]);
System.out.println("第"+x+"列最大距离:");
System.out.println(MAX[j]);
}
*/
System.out.println("(区间型指标——>极大型指标)pos3矩阵为:");
for(int i=0;i<rows;i++) {
for(int j=0;j<cols;j++) {
System.out.print(pos[i][j]+" ");
}
System.out.println();
}
}
//正向化举证标准化
public void standar() {
double[] sum =new double[cols];
for(int j=0;j<cols;j++) {
for(int i=0;i<rows;i++) {
sum[j] += pos[i][j]*pos[i][j];
}
}
for(int j=0;j<cols;j++) {
for(int i=0;i<rows;i++) {
stad[i][j] = pos[i][j]/Math.sqrt(sum[j]);
}
}
System.out.println("(标准化)stad矩阵为:");
for(int i=0;i<rows;i++) {
for(int j=0;j<cols;j++) {
System.out.print(stad[i][j]+" ");
}
System.out.println();
}
}
//计算得分并归一化
public void uniform() {
double[] Zmax = new double[cols];//存储每列的最大值
double[] Zmin = new double[cols];//存储每列的最小值
for(int j=0;j<cols;j++) {
double colmax = 0;
for(int i=0;i<rows;i++) {
if(stad[i][j] >= colmax) {
colmax = stad[i][j];
//System.out.println("orig:"+orig[i][x]);
}
}
Zmax[j] = colmax;//每列的最大值
double colmin = 0;
for(int i=0;i<rows;i++) {
if(i==0) {
colmin = stad[i][j];
}
else if(stad[i][j] <= colmin) {
colmin = stad[i][j];
//System.out.println("orig:"+orig[i][x]);
}
}
Zmin[j] = colmin;//每列的最小值
}
double Dmax = 0.0;
double Dmin = 0.0;
double summax =0.0;
double summin =0.0;
double[] wi = new double[cols];//权重向量
System.out.println("依次输入每个指标(列)的权重:");
for(int i =0;i<cols;i++) {
wi[i] = input.nextDouble();
}
for(int i=0;i<rows;i++) {
for(int j=0;j<cols;j++) {
summax += wi[j]*Math.pow(Zmax[j] - stad[i][j], 2.0);
summin += wi[j]*Math.pow(Zmin[j] - stad[i][j], 2.0);
}
Dmax = Math.sqrt(summax);
Dmin = Math.sqrt(summin);
uniform[i][0] = Dmin/(Dmax+Dmin);
}
System.out.println("uniform矩阵(最终得分):");
for(int i=0;i<rows;i++) {
System.out.println(uniform[i][0]);
}
}
public String getFilepath() {
return filepath;
}
public void setFilepath(String filepath) {
this.filepath = filepath;
}
}
package TOPSIS;
import java.io.IOException;
import jxl.read.biff.BiffException;
import jxl.write.WriteException;
public class topsismain {
public static void main(String args[]) throws BiffException, IOException, WriteException {
topsis top = new topsis();
top.read("topsis.xls");
top.positive1();
top.positive2();
top.positive3();
top.standar();
top.uniform();
}
}