""" 多准则决策示例 (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()