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