下面是一篇用 python3 实现TOPSIS法(优劣解距离法)的文章,将作者的案例通过C++实现。
由于精力、能力有限,只编写了前半部分,而对于TOPSIS法的关键,
即权重的确定,还有待进一步探索。
> 原文链接:https://zhuanlan.zhihu.com/p/37738503
> 作者主页:https://www.zhihu.com/people/Suranyi
> main.cpp
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <algorithm>
#include <numeric>
using namespace std;
class item;//item类,存放每个要评价的对象
vector<item> read();//读取出对象、指标构成的矩阵
void orientation(vector<item>& all_items);//正向化
void cost(vector<item>& all_items, int col);//极小型正向化
void bestVal(vector<item>& all_items, int col);//中间型正向化
void section(vector<item>& all_items, int col);//区间型正向化
void show(vector<item>& all_items);//输出矩阵到屏幕
void normalization(vector<item>& all_items);//归一化
void distance(vector<item>& all_items);//计算每个对象与最优方案和最劣方案的距离
void insertCol(vector<item>& all_items);//将每列存放到vector嵌套容器便于求最大最小值
vector<vector<double>> column;//vector嵌套容器
class item {
public:
item() {
m_name = "";
m_nums = 5;
m_target = vector<double>(5);
}
item(string name, int nums) :m_name(name), m_nums(nums) {}
item(const item& it) {
m_name = it.m_name;
m_nums = it.m_nums;
m_target = it.m_target;
}
int getNums() {
return m_nums;
}
string getName() {
return m_name;
}
double getTarget(int index) {
return m_target[index - 1];
}
void reNums(int num) {
m_nums = num;
}
void reName(string name) {
m_name = name;
}
void insertdata(double temp) { //插入指标数据
m_target.push_back(temp);
}
void myprint() {
cout << m_name << "\t";
for (vector<double>::iterator it = m_target.begin(); it != m_target.end(); it++) {
cout.setf(ios::fixed);
cout.precision(6);
cout << (*it) << "\t";
}
}
void changeVal(int index, double val) { //修改指标数据
m_target[index-1] = val;
}
private:
string m_name;
vector<double> m_target; //存放所有指标数据
int m_nums;//指标数量
};
int main() {
vector<item> all_items = read();
orientation(all_items);
show(all_items);
normalization(all_items);
show(all_items);
distance(all_items);
show(all_items);
system("pause");
return 0;
}
//读取源文件,包括表头
vector<item> read() {
cout << "输入行数和列数,列为指标,行为评价对象: ";
int m = 0, n = 0;
cin >> m >> n;
cout << endl;
fstream file;
file.open("data.txt", ios::in);//打开数据文件
if (!file.is_open()) {
cout << "打开失败!";
exit(0);
}
vector<item> all_items;//vector<item> all_items(m-1);
vector<item>::iterator it_all;
//读取文件到每个item类
string buf;
char ch;
getline(file, buf);
for (int i = 0; i < m - 1; i++) {
buf = "";
while ((ch = file.get()) != ' ') {
buf += ch;
}
item temp(buf, n - 1);
for (int j = 0; j < n - 1; j++) {
double data;
file >> data;
temp.insertdata(data);
}
all_items.push_back(temp);
}
//读取每列到column
insertCol(all_items);
file.close();
show(all_items);
cout << endl;
return all_items;
}
//读取每列到column
void insertCol(vector<item>& all_items) {
column.clear();
for (int i = 0; i < all_items[1].getNums(); i++) {
vector<double> v;
for (int j = 0; j < all_items.size(); j++) {
v.push_back(all_items[j].getTarget(i + 1));
}
column.push_back(v);
}
}
//所有指标正向化
void orientation(vector<item>& all_items) {
int index = all_items[1].getNums();
cout << "分别输入每个指标的类型,空格隔开:1.极大型 2.极小型 3.区间型 4.中间型 ";
vector<int> type;//vector<int> type(index)
for (int i = 0; i < index; i++) {
int t = 0;
cin >> t;
type.push_back(t);
}
cout << endl;
for (int i = 0; i < index; i++) {
switch (type[i]) {
case 2: {cost(all_items, i + 1); break; } //极小型
case 3: {section(all_items, i + 1); break; } //区间型
case 4: {bestVal(all_items, i + 1); break; } //中间型
}
}
cout << "指标正向化后的矩阵:" << endl;
}
//极小型指标正向化
void cost(vector<item>& all_items, int col) {
//double maxVal = *max_element(column[col-1].begin(), column[col - 1].end());
for (int i = 0; i < all_items.size(); i++) {
//all_items[i].changeVal(col, maxVal - all_items[i].getTarget(col));
all_items[i].changeVal(col, (double)1 / all_items[i].getTarget(col));
}
insertCol(all_items);
}
//区间型指标正向化
void section(vector<item>& all_items, int col) {
double a = 0, b = 0, c = 0, d = 0;
cout << "输入最佳区间:";
cin >> a >> b;
cout << "输入最大容忍区间:";
cin >> c >> d;
cout << endl;
double maxVal = *max_element(column[col - 1].begin(), column[col - 1].end());
double minVal = *min_element(column[col - 1].begin(), column[col - 1].end());
//double M = max(a - minVal, maxVal - b);
for (int i = 0; i < all_items.size(); i++) {
if ((all_items[i].getTarget(col) >= a) && (all_items[i].getTarget(col) <= b)) {
all_items[i].changeVal(col, 1);
}
else if (all_items[i].getTarget(col) < a) {
all_items[i].changeVal(col, 1 - (a - all_items[i].getTarget(col)) / (a - c)); //a-c、d-b == M
}
else if (all_items[i].getTarget(col) > b) {
all_items[i].changeVal(col, 1 - (all_items[i].getTarget(col) - b) / (d - b));
}
}
insertCol(all_items); //由于正向化对原有数据做出改动,需要重新写入每列到column
}
//中间型指标正向化
void bestVal(vector<item>& all_items, int col) {
cout << "输入中间值:";
double val = 0;
cin >> val;
cout << endl;
double maxVal = *max_element(column[col - 1].begin(), column[col - 1].end());
for (int i = 0; i < all_items.size(); i++) {
all_items[i].changeVal(col, 1 - (abs(maxVal - all_items[i].getTarget(col))) / maxVal);
}
insertCol(all_items);
}
void show(vector<item> & all_items) {
vector<item>::iterator it_all;
for (it_all = all_items.begin(); it_all != all_items.end(); it_all++) {
(*it_all).myprint();
}
cout << endl << endl;
}
//归一化指标
void normalization(vector<item>& all_items) {
int index = all_items[1].getNums();
vector<vector<double>> temp = column;
vector<double> sum;//保存每列的和
//先平方
for (vector<vector<double>>::iterator it = temp.begin(); it != temp.end(); it++) {
for (vector<double>::iterator vit = (*it).begin(); vit != (*it).end(); vit++) {
(*vit) *= (*vit);
}
}
//再求和
for (int i = 0; i < index; i++) {
double s = accumulate(temp[i].begin(), temp[i].end(), (double)0);
sum.push_back(sqrt(s));
}
//归一化后重新写入
for (int i = 0; i < index; i++) {
for (int j = 0; j < all_items.size(); j++) {
all_items[j].changeVal(i + 1, all_items[j].getTarget(i + 1) / sum[i]);
}
}
//对column进行相同操作
int i = 0;
for (vector<vector<double>>::iterator it = column.begin(); it != column.end(); it++) {
for (vector<double>::iterator vit = (*it).begin(); vit != (*it).end(); vit++) {
(*vit) /= sum[i];
}
i++;
}
cout << "归一化初始矩阵:" << endl;
}
//求解距离
void distance(vector<item>& all_items) {
int index = all_items[1].getNums();
vector<double> best; //存放最优方案
vector<double> worst;//存放最劣方案
for (int i = 0; i < index; i++) {
double maxVal = *max_element(column[i].begin(), column[i].end());
double minVal = *min_element(column[i].begin(), column[i].end());
best.push_back(maxVal);
worst.push_back(minVal);
}
cout << "最优方案: ";
for (int i = 0; i < index; i++) {
cout << best[i] << " ";
}
cout << endl << "最劣方案: ";
for (int i = 0; i < index; i++) {
cout << worst[i] << " ";
}
cout << endl << endl;
vector<double> weight;
cout << "输入权重矩阵: ";
for (int i = 0; i < index; i++) {
double w = 0;
cin >> w;
weight.push_back(w);
}
//计算得分
cout << endl;
vector<double> result;
for (int i = 0; i < all_items.size(); i++) {
double sumBest = 0, sumWorst = 0;
for (int j = 0; j < index; j++) {
sumBest += weight[j] * pow((all_items[i].getTarget(j + 1) - best[j]), 2);
sumWorst += weight[j] * pow((all_items[i].getTarget(j + 1) - worst[j]), 2);
}
sumBest = sqrt(sumBest);
sumWorst = sqrt(sumWorst);
result.push_back(sumWorst / (sumBest + sumWorst));
all_items[i].insertdata(result[i]);
}
cout << "最终结果及得分:" << endl;
}
数据
运行结果
原作者运行结果