Files
pengxiao 219232de74 refactor: 重组项目目录结构
以讲义内容为骨架迁移到标准目录格式:
- officefile/ 主内容(12章 + 附录 + CC4SI补充)
- dofile/ 代码示例(11个Python脚本)
- data/ 图片资源
- output/ 生成输出(忽略)
- Archive/ 归档旧目录(忽略)
- .claude/skills/ 保留markdown-to-docx工具链
- .pandoc/ 保留CSL和本地化配置

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 14:00:56 +08:00

1121 lines
35 KiB
Python

"""
多准则决策示例 (Multi-Criteria Decision Making Example)
======================================================
本示例展示空间智能系统中的多准则决策方法。
MCDA/MCDM 用于处理多个冲突准则下的决策问题。
核心概念:
1. 准则体系 - 构建评价准则层次结构
2. 权重确定 - AHP、熵权法等
3. 决策矩阵 - 标准化与规范化
4. 综合评价 - WSM、WPM、TOPSIS等
5. 灵敏度分析 - 权重变化对结果的影响
应用场景:
- 选址决策
- 项目评估
- 资源配置
- 风险评估
作者: CC4SI 项目组
"""
import math
import json
from typing import List, Dict, Tuple, Optional, Any, Callable
from dataclasses import dataclass, field
from enum import Enum
import random
# ============================================================================
# 准则类型与方向
# ============================================================================
class CriterionType(Enum):
"""准则类型"""
BENEFIT = "benefit" # 效益型 (越大越好)
COST = "cost" # 成本型 (越小越好)
NON_MONOTONIC = "non_monotonic" # 非单调 (有最优值)
@dataclass
class Criterion:
"""
决策准则
定义评价的维度。
"""
name: str
criterion_type: CriterionType
weight: float = 1.0
scale: Tuple[float, float] = (0, 1) # 取值范围
optimal_value: Optional[float] = None # 最优值 (用于非单调型)
def __repr__(self) -> str:
return f"Criterion({self.name}, {self.criterion_type.value}, w={self.weight:.2f})"
# ============================================================================
# 决策方案
# ============================================================================
@dataclass
class Alternative:
"""
决策方案
表示一个待评估的备选方案。
"""
id: str
name: str
values: Dict[str, float] # 准则名称到值的映射
metadata: Dict[str, Any] = field(default_factory=dict)
def get_value(self, criterion_name: str) -> Optional[float]:
"""获取准则值"""
return self.values.get(criterion_name)
def set_value(self, criterion_name: str, value: float) -> None:
"""设置准则值"""
self.values[criterion_name] = value
def __repr__(self) -> str:
return f"Alternative({self.name}, values={len(self.values)})"
# ============================================================================
# 标准化方法
# ============================================================================
class NormalizationMethod(Enum):
"""标准化方法"""
MIN_MAX = "min_max" # Min-Max标准化
VECTOR = "vector" # 向量标准化
Z_SCORE = "z_score" # Z-Score标准化
SUM = "sum" # 总和标准化
class Normalizer:
"""数据标准化器"""
@staticmethod
def min_max(values: List[float],
target_range: Tuple[float, float] = (0, 1),
criterion_type: CriterionType = CriterionType.BENEFIT) -> List[float]:
"""
Min-Max标准化
Args:
values: 原始值列表
target_range: 目标范围
criterion_type: 准则类型
Returns:
标准化后的值列表
"""
min_val = min(values)
max_val = max(values)
if max_val == min_val:
return [target_range[0] for _ in values]
t_min, t_max = target_range
result = []
for v in values:
if criterion_type == CriterionType.BENEFIT:
# 效益型: 越大越好
normalized = (v - min_val) / (max_val - min_val)
else: # COST
# 成本型: 越小越好
normalized = (max_val - v) / (max_val - min_val)
result.append(t_min + normalized * (t_max - t_min))
return result
@staticmethod
def vector(values: List[float],
criterion_type: CriterionType = CriterionType.BENEFIT) -> List[float]:
"""
向量标准化
Args:
values: 原始值列表
criterion_type: 准则类型
Returns:
标准化后的值列表
"""
sum_squares = sum(v * v for v in values)
if sum_squares == 0:
return [0.0 for _ in values]
norm = math.sqrt(sum_squares)
result = [v / norm for v in values]
if criterion_type == CriterionType.COST:
# 成本型: 取倒数
result = [1.0 / (v + 1e-10) if v > 0 else 1.0 for v in result]
# 重新归一化
total = sum(result)
result = [v / total for v in result]
return result
@staticmethod
def z_score(values: List[float],
criterion_type: CriterionType = CriterionType.BENEFIT) -> List[float]:
"""
Z-Score标准化
Args:
values: 原始值列表
criterion_type: 准则类型
Returns:
标准化后的值列表
"""
import statistics
if len(values) < 2:
return [0.0 for _ in values]
mean = statistics.mean(values)
stdev = statistics.stdev(values)
if stdev == 0:
return [0.0 for _ in values]
result = [(v - mean) / stdev for v in values]
# 转换到正值范围
min_result = min(result)
if min_result < 0:
result = [v - min_result for v in result]
# 归一化到0-1
max_result = max(result)
if max_result > 0:
result = [v / max_result for v in result]
if criterion_type == CriterionType.COST:
result = [1.0 - v for v in result]
return result
# ============================================================================
# AHP层次分析法
# ============================================================================
class AHP:
"""
层次分析法 (Analytic Hierarchy Process)
用于确定准则权重的方法。
"""
# Saaty标度
SAATY_SCALE = {
1: "同等重要",
2: "稍微重要",
3: "明显重要",
4: "非常重要",
5: "极端重要"
}
def __init__(self, criteria: List[str]):
"""
初始化AHP
Args:
criteria: 准则名称列表
"""
self.criteria = criteria
self.n = len(criteria)
self.comparison_matrix: List[List[float]] = []
def build_comparison_matrix(self, comparisons: Dict[Tuple[str, str], float]) -> None:
"""
构建比较矩阵
Args:
comparisons: 准则对比较值字典
((criterion_i, criterion_j), value)
value > 1 表示 i 比 j 重要
value < 1 表示 j 比 i 重要
"""
# 初始化单位矩阵
self.comparison_matrix = [[1.0 for _ in range(self.n)] for _ in range(self.n)]
# 填充比较矩阵
for (c1, c2), value in comparisons.items():
if c1 in self.criteria and c2 in self.criteria:
i = self.criteria.index(c1)
j = self.criteria.index(c2)
self.comparison_matrix[i][j] = value
self.comparison_matrix[j][i] = 1.0 / value
def calculate_weights(self) -> Tuple[List[float], float, float]:
"""
计算权重
Returns:
(权重列表, 一致性比率, 最大特征值)
"""
if not self.comparison_matrix:
return [1.0 / self.n] * self.n, 0.0, self.n
# 特征向量法 (幂法)
weights = self._eigenvector_method()
lambda_max = self._calculate_lambda_max(weights)
ci = (lambda_max - self.n) / (self.n - 1) if self.n > 1 else 0
ri = self._random_consistency_index(self.n)
cr = ci / ri if ri > 0 else 0
return weights, cr, lambda_max
def _eigenvector_method(self, max_iterations: int = 100,
tolerance: float = 1e-6) -> List[float]:
"""使用幂法计算特征向量"""
# 初始化权重向量
weights = [1.0 / self.n] * self.n
for _ in range(max_iterations):
# 矩阵向量乘法
new_weights = []
for i in range(self.n):
new_weights.append(
sum(self.comparison_matrix[i][j] * weights[j] for j in range(self.n))
)
# 归一化
total = sum(new_weights)
new_weights = [w / total for w in new_weights]
# 检查收敛
if max(abs(new_weights[i] - weights[i]) for i in range(self.n)) < tolerance:
break
weights = new_weights
return weights
def _calculate_lambda_max(self, weights: List[float]) -> float:
"""计算最大特征值"""
lambda_sum = 0.0
for i in range(self.n):
weighted_sum = sum(self.comparison_matrix[i][j] * weights[j] for j in range(self.n))
lambda_sum += weighted_sum / weights[i] if weights[i] > 0 else 0
return lambda_sum / self.n
def _random_consistency_index(self, n: int) -> float:
"""随机一致性指标RI"""
ri_table = {
1: 0.0, 2: 0.0, 3: 0.58, 4: 0.90, 5: 1.12,
6: 1.24, 7: 1.32, 8: 1.41, 9: 1.45, 10: 1.49
}
return ri_table.get(n, 1.49)
# ============================================================================
# 熵权法
# ============================================================================
class EntropyWeightMethod:
"""
熵权法
基于数据离散度的客观权重确定方法。
"""
@staticmethod
def calculate_weights(decision_matrix: List[List[float]],
criterion_types: List[CriterionType]) -> List[float]:
"""
计算熵权
Args:
decision_matrix: 决策矩阵 (方案 x 准则)
criterion_types: 各准则的类型
Returns:
权重列表
"""
n_alternatives = len(decision_matrix)
n_criteria = len(decision_matrix[0]) if decision_matrix else 0
if n_alternatives == 0 or n_criteria == 0:
return []
# 标准化
normalized_matrix = []
for j in range(n_criteria):
column = [decision_matrix[i][j] for i in range(n_alternatives)]
normalized = EntropyWeightMethod._normalize_column(
column, criterion_types[j]
)
normalized_matrix.append(normalized)
# 计算熵值
entropy_values = []
for j in range(n_criteria):
column = normalized_matrix[j]
# 转换为概率
total = sum(column)
if total == 0:
entropy_values.append(0)
continue
probabilities = [v / total for v in column]
# 计算熵
entropy = 0.0
k = 1 / math.log(n_alternatives) if n_alternatives > 1 else 0
for p in probabilities:
if p > 0:
entropy -= k * p * math.log(p)
entropy_values.append(entropy)
# 计算权重
diversity = [1 - e for e in entropy_values]
total_diversity = sum(diversity)
if total_diversity == 0:
return [1.0 / n_criteria] * n_criteria
weights = [d / total_diversity for d in diversity]
return weights
@staticmethod
def _normalize_column(column: List[float],
criterion_type: CriterionType) -> List[float]:
"""标准化列"""
min_val = min(column)
max_val = max(column)
if max_val == min_val:
return [1.0 for _ in column]
result = []
for v in column:
if criterion_type == CriterionType.BENEFIT:
normalized = (v - min_val) / (max_val - min_val)
else: # COST
normalized = (max_val - v) / (max_val - min_val)
result.append(normalized)
return result
# ============================================================================
# MCDA方法实现
# ============================================================================
class WeightedSumModel:
"""
加权求和模型 (WSM)
最简单的多准则决策方法。
"""
def __init__(self, criteria: List[Criterion]):
self.criteria = criteria
self.criterion_map = {c.name: c for c in criteria}
def evaluate(self, alternatives: List[Alternative]) -> List[Tuple[Alternative, float]]:
"""
评估方案
Args:
alternatives: 备选方案列表
Returns:
(方案, 得分) 列表,按得分降序排列
"""
results = []
for alt in alternatives:
score = 0.0
valid = True
for criterion in self.criteria:
value = alt.get_value(criterion.name)
if value is None:
valid = False
break
# 标准化
normalized = self._normalize_value(value, criterion)
score += criterion.weight * normalized
if valid:
results.append((alt, score))
results.sort(key=lambda x: x[1], reverse=True)
return results
def _normalize_value(self, value: float, criterion: Criterion) -> float:
"""标准化单个值"""
min_val, max_val = criterion.scale
if criterion.criterion_type == CriterionType.BENEFIT:
if max_val == min_val:
return 0.5
return (value - min_val) / (max_val - min_val)
else: # COST
if max_val == min_val:
return 0.5
return (max_val - value) / (max_val - min_val)
class TOPSIS:
"""
TOPSIS (逼近理想解排序法)
考虑方案与理想解的距离。
"""
def __init__(self, criteria: List[Criterion]):
self.criteria = criteria
self.criterion_map = {c.name: c for c in criteria}
def evaluate(self, alternatives: List[Alternative]) -> List[Tuple[Alternative, float]]:
"""
评估方案
Args:
alternatives: 备选方案列表
Returns:
(方案, 相对贴近度) 列表,按贴近度降序排列
"""
# 构建决策矩阵
matrix, criterion_names = self._build_matrix(alternatives)
if not matrix or not criterion_names:
return []
n_alternatives = len(matrix)
n_criteria = len(matrix[0])
# 向量标准化
normalized_matrix = self._normalize_matrix(matrix)
# 构建加权标准化矩阵
weights = [self.criterion_map[c].weight for c in criterion_names]
weighted_matrix = [
[normalized_matrix[i][j] * weights[j] for j in range(n_criteria)]
for i in range(n_alternatives)
]
# 确定理想解和负理想解
ideal_positive, ideal_negative = self._determine_ideals(
weighted_matrix, criterion_names
)
# 计算距离
distances_positive = self._calculate_distances(weighted_matrix, ideal_positive)
distances_negative = self._calculate_distances(weighted_matrix, ideal_negative)
# 计算相对贴近度
results = []
for i, alt in enumerate(alternatives):
d_pos = distances_positive[i]
d_neg = distances_negative[i]
if d_pos + d_neg == 0:
closeness = 0
else:
closeness = d_neg / (d_pos + d_neg)
results.append((alt, closeness))
results.sort(key=lambda x: x[1], reverse=True)
return results
def _build_matrix(self, alternatives: List[Alternative]) -> Tuple[List[List[float]], List[str]]:
"""构建决策矩阵"""
if not self.criteria:
return [], []
criterion_names = [c.name for c in self.criteria]
matrix = []
for alt in alternatives:
row = []
valid = True
for name in criterion_names:
value = alt.get_value(name)
if value is None:
valid = False
break
row.append(value)
if valid:
matrix.append(row)
return matrix, criterion_names
def _normalize_matrix(self, matrix: List[List[float]]) -> List[List[float]]:
"""向量标准化"""
if not matrix:
return []
n_criteria = len(matrix[0])
result = []
for j in range(n_criteria):
column = [matrix[i][j] for i in range(len(matrix))]
sum_squares = sum(v * v for v in column)
norm = math.sqrt(sum_squares) if sum_squares > 0 else 1
for i in range(len(matrix)):
if j == 0:
result.append([])
result[i].append(matrix[i][j] / norm)
return result
def _determine_ideals(self, matrix: List[List[float]],
criterion_names: List[str]) -> Tuple[List[float], List[float]]:
"""确定理想解和负理想解"""
n_criteria = len(matrix[0])
ideal_positive = []
ideal_negative = []
for j in range(n_criteria):
column = [matrix[i][j] for i in range(len(matrix))]
criterion = self.criterion_map[criterion_names[j]]
if criterion.criterion_type == CriterionType.BENEFIT:
ideal_positive.append(max(column))
ideal_negative.append(min(column))
else: # COST
ideal_positive.append(min(column))
ideal_negative.append(max(column))
return ideal_positive, ideal_negative
def _calculate_distances(self, matrix: List[List[float]],
ideal: List[float]) -> List[float]:
"""计算到理想解的距离"""
distances = []
for i in range(len(matrix)):
dist = math.sqrt(
sum((matrix[i][j] - ideal[j]) ** 2 for j in range(len(ideal)))
)
distances.append(dist)
return distances
class VIKOR:
"""
VIKOR (VIseKriterijumska Optimizacija I Kompromisno Resenje)
折衷排序方法,适用于准则间存在冲突的情况。
"""
def __init__(self, criteria: List[Criterion], v: float = 0.5):
"""
初始化VIKOR
Args:
criteria: 准则列表
v: 决策机制系数 (0-1)
v > 0.5: 按群体效益最大化
v < 0.5: 按个别遗憾最小化
v = 0.5: 折衷解
"""
self.criteria = criteria
self.criterion_map = {c.name: c for c in criteria}
self.v = v
def evaluate(self, alternatives: List[Alternative]) -> List[Tuple[Alternative, float]]:
"""评估方案"""
# 构建决策矩阵
matrix, criterion_names = self._build_matrix(alternatives)
if not matrix or not criterion_names:
return []
n_alternatives = len(matrix)
n_criteria = len(matrix[0])
# 标准化
normalized_matrix = self._normalize_matrix(matrix, criterion_names)
# 确定最优最劣值
f_best, f_worst = self._determine_best_worst(normalized_matrix, criterion_names)
# 计算S和R
weights = [self.criterion_map[c].weight for c in criterion_names]
S_values = [] # 群体效益
R_values = [] # 个别遗憾
for i in range(n_alternatives):
S = 0.0
R = 0.0
for j in range(n_criteria):
weight = weights[j]
value = normalized_matrix[i][j]
# 距离最优值的归一化距离
if f_best[j] == f_worst[j]:
distance = 0
else:
distance = (f_best[j] - value) / (f_best[j] - f_worst[j])
S += weight * distance
R = max(R, weight * distance)
S_values.append(S)
R_values.append(R)
# 计算Q值
S_min, S_max = min(S_values), max(S_values)
R_min, R_max = min(R_values), max(R_values)
Q_values = []
for i in range(n_alternatives):
if S_max == S_min:
s_term = 0
else:
s_term = (S_values[i] - S_min) / (S_max - S_min)
if R_max == R_min:
r_term = 0
else:
r_term = (R_values[i] - R_min) / (R_max - R_min)
Q = self.v * s_term + (1 - self.v) * r_term
Q_values.append(Q)
# 返回按Q值排序的结果
results = [(alternatives[i], Q_values[i]) for i in range(n_alternatives)]
results.sort(key=lambda x: x[1])
return results
def _build_matrix(self, alternatives: List[Alternative]) -> Tuple[List[List[float]], List[str]]:
"""构建决策矩阵"""
criterion_names = [c.name for c in self.criteria]
matrix = []
for alt in alternatives:
row = [alt.get_value(name) for name in criterion_names]
if None not in row:
matrix.append(row)
return matrix, criterion_names
def _normalize_matrix(self, matrix: List[List[float]],
criterion_names: List[str]) -> List[List[float]]:
"""标准化决策矩阵"""
result = []
n_criteria = len(matrix[0])
for j in range(n_criteria):
column = [matrix[i][j] for i in range(len(matrix))]
criterion = self.criterion_map[criterion_names[j]]
min_val = min(column)
max_val = max(column)
for i in range(len(matrix)):
if j == 0:
result.append([])
if max_val == min_val:
result[i].append(1.0)
elif criterion.criterion_type == CriterionType.BENEFIT:
result[i].append((matrix[i][j] - min_val) / (max_val - min_val))
else:
result[i].append((max_val - matrix[i][j]) / (max_val - min_val))
return result
def _determine_best_worst(self, matrix: List[List[float]],
criterion_names: List[str]) -> Tuple[List[float], List[float]]:
"""确定最优值和最劣值"""
n_criteria = len(matrix[0])
f_best = []
f_worst = []
for j in range(n_criteria):
column = [matrix[i][j] for i in range(len(matrix))]
f_best.append(max(column))
f_worst.append(min(column))
return f_best, f_worst
# ============================================================================
# 灵敏度分析
# ============================================================================
class SensitivityAnalyzer:
"""
灵敏度分析器
分析权重变化对决策结果的影响。
"""
@staticmethod
def weight_sensitivity(alternatives: List[Alternative],
criteria: List[Criterion],
method: str = "TOPSIS",
perturbation: float = 0.1) -> Dict[str, Any]:
"""
权重灵敏度分析
Args:
alternatives: 备选方案
criteria: 准则列表
method: 评价方法
perturbation: 扰动幅度
Returns:
灵敏度分析结果
"""
# 原始权重
original_weights = [c.weight for c in criteria]
n_criteria = len(criteria)
# 原始排名
if method == "TOPSIS":
evaluator = TOPSIS(criteria)
else:
evaluator = WeightedSumModel(criteria)
original_results = evaluator.evaluate(alternatives)
original_ranking = [alt.id for alt, _ in original_results]
# 分析每个准则的权重变化
sensitivity_data = {}
for i, criterion in enumerate(criteria):
# 增加权重
weights_plus = original_weights.copy()
weights_plus[i] += perturbation
# 归一化
total = sum(weights_plus)
weights_plus = [w / total for w in weights_plus]
# 减少权重
weights_minus = original_weights.copy()
weights_minus[i] = max(0, weights_minus[i] - perturbation)
total = sum(weights_minus)
weights_minus = [w / total for w in weights_minus]
# 评估
criteria_plus = [CriteriaWrapper(c, w) for c, w in zip(criteria, weights_plus)]
criteria_minus = [CriteriaWrapper(c, w) for c, w in zip(criteria, weights_minus)]
if method == "TOPSIS":
evaluator_plus = TOPSIS(criteria_plus)
evaluator_minus = TOPSIS(criteria_minus)
else:
evaluator_plus = WeightedSumModel(criteria_plus)
evaluator_minus = WeightedSumModel(criteria_minus)
results_plus = evaluator_plus.evaluate(alternatives)
results_minus = evaluator_minus.evaluate(alternatives)
ranking_plus = [alt.id for alt, _ in results_plus]
ranking_minus = [alt.id for alt, _ in results_minus]
# 计算排名变化
rank_changes_plus = sum(
1 for a, b in zip(original_ranking, ranking_plus) if a != b
)
rank_changes_minus = sum(
1 for a, b in zip(original_ranking, ranking_minus) if a != b
)
sensitivity_data[criterion.name] = {
"weight_change": perturbation,
"rank_changes_increase": rank_changes_plus,
"rank_changes_decrease": rank_changes_minus,
"sensitive": rank_changes_plus > 0 or rank_changes_minus > 0
}
return {
"original_ranking": original_ranking,
"sensitivity_data": sensitivity_data
}
class CriteriaWrapper:
"""准则包装器,用于临时修改权重"""
def __init__(self, original: Criterion, weight: float):
self.name = original.name
self.criterion_type = original.criterion_type
self.weight = weight
self.scale = original.scale
self.optimal_value = original.optimal_value
# ============================================================================
# 主程序
# ============================================================================
def main():
"""主程序 - 演示多准则决策的使用"""
print("="*70)
print("多准则决策示例演示")
print("="*70)
# ========================================================================
# 1. 定义问题
# ========================================================================
print("\n[部分 1] 商场选址决策问题")
print("-" * 50)
# 定义准则
criteria = [
Criterion("人流量", CriterionType.BENEFIT, scale=(1000, 50000)),
Criterion("租金成本", CriterionType.COST, scale=(50, 200)),
Criterion("交通便利", CriterionType.BENEFIT, scale=(1, 10)),
Criterion("竞争强度", CriterionType.COST, scale=(0, 10)),
Criterion("发展潜力", CriterionType.BENEFIT, scale=(1, 10))
]
print("\n评价准则:")
for i, c in enumerate(criteria, 1):
type_cn = "效益型" if c.criterion_type == CriterionType.BENEFIT else "成本型"
print(f" {i}. {c.name:8s} ({type_cn}): {c.scale}")
# 定义备选方案
alternatives = [
Alternative("A1", "西湖商圈", {
"人流量": 45000,
"租金成本": 180,
"交通便利": 9,
"竞争强度": 8,
"发展潜力": 6
}),
Alternative("A2", "滨江新城", {
"人流量": 25000,
"租金成本": 120,
"交通便利": 7,
"竞争强度": 4,
"发展潜力": 9
}),
Alternative("A3", "萧山城区", {
"人流量": 18000,
"租金成本": 80,
"交通便利": 5,
"竞争强度": 3,
"发展潜力": 7
}),
Alternative("A4", "城西商圈", {
"人流量": 32000,
"租金成本": 150,
"交通便利": 8,
"竞争强度": 6,
"发展潜力": 8
}),
Alternative("A5", "下沙副中心", {
"人流量": 28000,
"租金成本": 100,
"交通便利": 6,
"竞争强度": 5,
"发展潜力": 7
})
]
print("\n备选方案:")
for alt in alternatives:
print(f" {alt.id}: {alt.name}")
for c in criteria:
print(f" {c.name}: {alt.get_value(c.name)}")
# ========================================================================
# 2. AHP确定权重
# ========================================================================
print("\n\n[部分 2] AHP层次分析法确定权重")
print("-" * 50)
ahp = AHP([c.name for c in criteria])
# 构建比较矩阵 (专家判断)
comparisons = {
("人流量", "租金成本"): 2, # 人流量稍微比租金重要
("人流量", "交通便利"): 3, # 人流量明显比交通重要
("人流量", "竞争强度"): 4, # 人流量比竞争重要
("人流量", "发展潜力"): 2, # 人流量稍微比发展潜力重要
("租金成本", "交通便利"): 2, # 租金稍微比交通重要
("租金成本", "竞争强度"): 2, # 租金稍微比竞争重要
("租金成本", "发展潜力"): 3, # 租金明显比发展潜力重要
("交通便利", "竞争强度"): 2, # 交通稍微比竞争重要
("交通便利", "发展潜力"): 2, # 交通稍微比发展潜力重要
("竞争强度", "发展潜力"): 2, # 竞争稍微比发展潜力重要
}
ahp.build_comparison_matrix(comparisons)
weights, cr, lambda_max = ahp.calculate_weights()
print(f"\nAHP权重计算结果:")
print(f" 最大特征值: {lambda_max:.4f}")
print(f" 一致性比率 CR: {cr:.4f}", end="")
if cr < 0.1:
print(" (通过一致性检验)")
else:
print(" (未通过一致性检验)")
print(f"\n准则权重:")
for i, (name, weight) in enumerate(zip([c.name for c in criteria], weights)):
criteria[i].weight = weight
print(f" {name:8s}: {weight:.4f}")
# ========================================================================
# 3. 熵权法确定权重
# ========================================================================
print("\n\n[部分 3] 熵权法确定客观权重")
print("-" * 50)
# 构建决策矩阵
decision_matrix = [
[alt.get_value(c.name) for c in criteria]
for alt in alternatives
]
entropy_weights = EntropyWeightMethod.calculate_weights(
decision_matrix,
[c.criterion_type for c in criteria]
)
print(f"\n熵权法计算结果:")
for name, weight in zip([c.name for c in criteria], entropy_weights):
print(f" {name:8s}: {weight:.4f}")
# 组合权重 (AHP 0.6 + 熵权 0.4)
print(f"\n组合权重 (AHP 60% + 熵权 40%):")
for i, c in enumerate(criteria):
combined_weight = 0.6 * c.weight + 0.4 * entropy_weights[i]
c.weight = combined_weight
print(f" {c.name:8s}: {combined_weight:.4f}")
# ========================================================================
# 4. TOPSIS评价
# ========================================================================
print("\n\n[部分 4] TOPSIS评价结果")
print("-" * 50)
topsis = TOPSIS(criteria)
topsis_results = topsis.evaluate(alternatives)
print(f"\nTOPSIS排名:")
print(f"{'排名':<6} {'方案ID':<8} {'名称':<12} {'贴近度':<10}")
print("-" * 40)
for i, (alt, score) in enumerate(topsis_results, 1):
print(f"{i:<6} {alt.id:<8} {alt.name:<12} {score:<10.4f}")
# ========================================================================
# 5. WSM评价
# ========================================================================
print("\n\n[部分 5] 加权求和模型(WSM)评价结果")
print("-" * 50)
wsm = WeightedSumModel(criteria)
wsm_results = wsm.evaluate(alternatives)
print(f"\nWSM排名:")
print(f"{'排名':<6} {'方案ID':<8} {'名称':<12} {'得分':<10}")
print("-" * 40)
for i, (alt, score) in enumerate(wsm_results, 1):
print(f"{i:<6} {alt.id:<8} {alt.name:<12} {score:<10.4f}")
# ========================================================================
# 6. VIKOR评价
# ========================================================================
print("\n\n[部分 6] VIKOR评价结果")
print("-" * 50)
vikor = VIKOR(criteria, v=0.5)
vikor_results = vikor.evaluate(alternatives)
print(f"\nVIKOR排名 (Q值越小越好):")
print(f"{'排名':<6} {'方案ID':<8} {'名称':<12} {'Q值':<10}")
print("-" * 40)
for i, (alt, score) in enumerate(vikor_results, 1):
print(f"{i:<6} {alt.id:<8} {alt.name:<12} {score:<10.4f}")
# ========================================================================
# 7. 方法比较
# ========================================================================
print("\n\n[部分 7] 不同方法排名比较")
print("-" * 50)
print(f"\n{'方案':<12} {'TOPSIS':<8} {'WSM':<8} {'VIKOR':<8}")
print("-" * 40)
for alt in alternatives:
topsis_rank = next(i for i, (a, _) in enumerate(topsis_results, 1) if a.id == alt.id)
wsm_rank = next(i for i, (a, _) in enumerate(wsm_results, 1) if a.id == alt.id)
vikor_rank = next(i for i, (a, _) in enumerate(vikor_results, 1) if a.id == alt.id)
print(f"{alt.name:<12} {topsis_rank:<8} {wsm_rank:<8} {vikor_rank:<8}")
# ========================================================================
# 8. 灵敏度分析
# ========================================================================
print("\n\n[部分 8] 权重灵敏度分析")
print("-" * 50)
sensitivity = SensitivityAnalyzer.weight_sensitivity(
alternatives, criteria, method="TOPSIS", perturbation=0.2
)
print(f"\n权重变化 ±20% 对排名的影响:")
print(f"{'准则':<10} {'排名变化':<12} {'敏感':<6}")
print("-" * 30)
for name, data in sensitivity["sensitivity_data"].items():
max_changes = max(data["rank_changes_increase"], data["rank_changes_decrease"])
sensitive = "" if data["sensitive"] else ""
print(f"{name:<10} {max_changes:<12} {sensitive:<6}")
# ========================================================================
# 9. 决策建议
# ========================================================================
print("\n\n[部分 9] 决策建议")
print("-" * 50)
best_topsis = topsis_results[0][0]
best_wsm = wsm_results[0][0]
best_vikor = vikor_results[0][0]
print(f"\n各方法推荐的最佳方案:")
print(f" TOPSIS: {best_topsis.name}")
print(f" WSM: {best_wsm.name}")
print(f" VIKOR: {best_vikor.name}")
# 综合推荐
vote_counts = {}
for alt in [best_topsis, best_wsm, best_vikor]:
vote_counts[alt.id] = vote_counts.get(alt.id, 0) + 1
recommended = max(vote_counts.items(), key=lambda x: x[1])[0]
recommended_alt = next(alt for alt in alternatives if alt.id == recommended)
print(f"\n综合推荐: {recommended_alt.name}")
print(f" 理由: 该方案在多种评价方法中表现最佳")
print("\n" + "="*70)
print("演示完成!")
print("="*70)
if __name__ == "__main__":
main()