模糊算法说明

1. 算法概述

模糊算法是房地产专家评估系统中的另一个核心评估算法,基于模糊数学理论对建筑工程安全风险进行模糊评估。与打分算法不同,模糊算法通过隶属度矩阵和比较矩阵,结合外部模糊计算API,实现更加智能和灵活的风险评估。

2. 算法流程图

flowchart TD
    A[开始模糊评估] --> B[初始化问题映射表]
    B --> C[构建模糊评估树]
    C --> D[统计错误记录]
    D --> E[标记参与评估节点]
    E --> F[裁剪无用节点]
    F --> G[更新隶属度和比较矩阵]
    G --> H[调用模糊计算API]
    H --> I[美化显示数据]
    I --> J[返回评估结果]
    
    subgraph "初始化阶段"
        B1[获取专家配置]
        B2[构建FuzzyValue树结构]
        B3[初始化错误统计]
    end
    
    subgraph "统计阶段"
        D1[遍历错误记录]
        D2[累加错误数量]
        D3[处理轮空项]
        D4[向上累加错误统计]
        D5[计算总错误数量]
    end
    
    subgraph "矩阵更新阶段"
        G1[叶子节点:获取隶属度矩阵]
        G2[父节点:获取比较矩阵]
        G3[设置矩阵数据]
    end
    
    subgraph "API计算阶段"
        H1[构建API请求]
        H2[调用外部模糊API]
        H3[解析API响应]
        H4[更新节点数据]
    end
    
    B --> B1
    B1 --> B2
    B2 --> B3
    
    D --> D1
    D1 --> D2
    D2 --> D3
    D3 --> D4
    D4 --> D5
    
    G --> G1
    G1 --> G2
    G2 --> G3
    
    H --> H1
    H1 --> H2
    H2 --> H3
    H3 --> H4

3. 详细算法步骤

3.1 初始化阶段

3.1.1 初始化问题映射表

var problemMap = initProblemMap(expert, expertService);
  • 目的:建立问题ID到布尔值的映射,用于标记问题是否已被计算
  • 实现:从专家的模型中选择的问题ID列表,初始值设为false
  • 作用:确保同一问题只被计算一次,避免重复统计

3.1.2 构建模糊评估树

var result = initSysResult(expert, expertService);

构建逻辑:
java private FuzzyResult initSysResult(Expert expert, ExpertService expertService) { var tree = expertService.getHierarchyTree(expert.getId()); var root = TreeHelper.<HierarchyData, FuzzyValue>convertTree(tree.root(), (node, newNode) -> FuzzyValue.builder() .build() ); return new FuzzyResult(root); }

  • 目的:根据专家的层级配置构建模糊评估树结构
  • 实现:将层级数据转换为FuzzyValue节点
  • 结构:树形结构,每个节点包含错误统计、隶属度、比较矩阵等信息

3.2 统计阶段

3.2.1 统计错误记录

count(problemMap, result, records, byPassPaths);

统计逻辑:
```java
private void count(TreeMap<Integer, Boolean> problemMap, FuzzyResult result, List records, List byPassPaths) {
// 遍历错误记录,将隐患数量累加到对应的节点上
if(records != null) {
records.forEach(errorRecord -> {
var problem = errorRecord.getProblem();
var ps = problemMap.get(problem.getId());

        // 如果问题不在模型中,不计算
        // 如果问题已经计算过,也不计算(同一问题的记录只计算一次)
        if (ps == null || ps)
            return;

        problemMap.put(problem.getId(), true);

        // 计算隐患数量
        var node = result.get(problem.getHierarchyPath());
        node.getData().addError(errorRecord.getLevel());
        node.getData().setInclude(true);
    });
}

}
```

步骤详解:
1. 遍历错误记录:对每个错误记录进行处理
2. 问题验证:检查问题是否在模型中且未被计算过
3. 错误累加:根据错误等级累加到对应节点
4. 节点标记:标记节点参与评估

3.2.2 处理轮空项

// 轮空项,如果设置了,则需要参与计算
if(byPassPaths != null){
    byPassPaths.forEach(path -> {
        var node = result.get(path);
        if(node == null) return;
        var scoreValue = node.getData();
        scoreValue.setInclude(true);
        node.setData(scoreValue);
    });
}
  • 目的:处理评估中需要跳过但参与计算的项
  • 实现:将轮空路径对应的节点标记为参与评估
  • 作用:确保轮空项不影响整体评估结构

3.2.3 向上累加错误统计

// 累加到上一级
TreeHelper.travel(result.root(), (node) -> {
    var nodeData = node.getData();
    for(var child : node.getChildren().values()) {
        var childData = child.getData();
        for(var i = 0; i < 4; i++) {
            nodeData.getChildErrorCounts()[i] += childData.getErrorCounts()[i] + childData.getChildErrorCounts()[i];
        }
    }
});
  • 目的:将子节点的错误统计累加到父节点
  • 实现:遍历树结构,累加各级错误数量
  • 作用:为后续计算提供完整的错误统计信息

3.2.4 计算总错误数量

// 更新totalErrorCounts
TreeHelper.travel(result.root(), (node) -> {
    var nodeData = node.getData();
    var t = new int[4];
    for(var i = 0; i < 4; i++) {
        t[i] = nodeData.getErrorCounts()[i] + nodeData.getChildErrorCounts()[i];
    }
    nodeData.setTotalErrorCounts(t);
});
  • 目的:计算每个节点的总错误数量
  • 实现:将节点自身错误和子节点错误累加
  • 作用:为隶属度矩阵查询提供数据

3.3 标记阶段

3.3.1 标记参与评估节点

mark(result);

标记逻辑:
```java
private void mark(FuzzyResult result) {
TreeHelper.travel(result.root(), (node) -> {
// 只去更新非叶子节点的include属性
if(node.getChildren().isEmpty())
return;

    // 如果有子节点参与评分,当前节点也参与评分
    boolean include = false;
    for (var child : node.getChildren().values()) {
        if(child.getData().isInclude())
            include = true;
    }
    node.getData().setInclude(include);
});

}
```

  • 目的:标记哪些节点需要参与最终评估计算
  • 规则:如果子节点参与评估,父节点也必须参与评估
  • 作用:为后续裁剪和计算提供依据

3.4 裁剪阶段

3.4.1 裁剪无用节点

result = prune(result);

裁剪逻辑:
java private FuzzyResult prune(FuzzyResult result) { var root = TreeHelper.pruneTree(result.root(), (node) -> { return node.getData().isInclude() && node.getDepth() <= 3; }); return new FuzzyResult(root); }

  • 目的:移除不参与评估的节点,只保留前3层
  • 规则:保留参与评估且深度不超过3的节点
  • 作用:减少计算量,提高算法效率

3.5 矩阵更新阶段

3.5.1 更新隶属度和比较矩阵

update(result, expert, expertService);

更新逻辑:
java private void update(FuzzyResult fuzzyResult, Expert expert, ExpertService expertService){ TreeHelper.travel(fuzzyResult.root(), (node) -> { var path = node.getPath(); var fuzzyValue = node.getData(); if(node.getChildren().isEmpty()) { // 从专家中获取隶属度矩阵 // 叶子节点,可以直接从专家中获取 // 非叶子节点,需要由下级节点计算得到 fuzzyValue.setMembershipList(expert.getMembershipMatrix().getData(fuzzyValue.getTotalErrorCounts())); fuzzyValue.setCompareMatrix(new CompareMatrix(new String[]{"空"})); }else{ // 从专家中获取判断矩阵 var indexCols = node.getChildren().values().stream().map(TreeNode::getName).toArray(String[]::new); var compareMatrix = expertService.getFuzzyMatrix(expert.getId(), path, indexCols); fuzzyValue.setCompareMatrix(compareMatrix); } }); }

矩阵类型:
1. 叶子节点:
- 获取隶属度矩阵:根据总错误数量查询
- 设置空比较矩阵:1x1矩阵

  1. 父节点:
  • 获取比较矩阵:根据子节点名称和路径查询
  • 不设置隶属度矩阵:由子节点计算得出

3.6 API计算阶段

3.6.1 调用模糊计算API

TreeHelper.travel(result.root(), this::computeNode);

计算逻辑:
```java
private void computeNode(TreeNode node) {
var fuzzyValue = node.getData();
var compareMatrix = fuzzyValue.getCompareMatrix();

float[][] membershipData;
if(node.getChildren().isEmpty()) {
    membershipData = new float[][]{fuzzyValue.getMembershipList()};
}else {
    membershipData = node.getChildren().values().stream()
            .map(child -> child.getData().getResp().getLargeList())
            .toArray(float[][]::new);
}

// 调用fuzzyAPI,计算检查得分
var request = FuzzyApiRequest.builder()
        .n(compareMatrix.getN())
        .membership_data(membershipData)
        .index(compareMatrix.getIndexCols())
        .compare_matrix_data(compareMatrix.getValues())
        .num_experts(1)
        .build();

var response = fuzzyApi.call(request);
fuzzyValue.setResp(response);

}
```

API请求构建:
1. 叶子节点:使用自身的隶属度矩阵
2. 父节点:使用子节点的计算结果(LargeList)
3. 请求参数:
- n:矩阵维度
- membership_data:隶属度数据
- index:索引列
- compare_matrix_data:比较矩阵数据
- num_experts:专家数量

3.7 美化阶段

3.7.1 美化显示数据

prettyDisplay(result);

美化逻辑:
```java
private void prettyDisplay(FuzzyResult result) {
TreeHelper.travel(result.root(), (node) -> {
var fuzzyValue = node.getData();
fuzzyValue.setMembershipList(fuzzyValue.getResp().getLargeList());
fuzzyValue.setRiskLevel(FuzzyRiskLevel.values()[5 - fuzzyValue.getResp().getRiskInterval()]);
fuzzyValue.setScore(fuzzyValue.getResp().getRiskPoint());

    // 更新权重显示
    int i = 0;
    var children = node.getChildren().values().stream().toList();
    for(var child : children) {
        child.getData().setWeight(fuzzyValue.getResp().getWeight()[i]);
        i++;
    }
});

}
```

美化内容:
1. 隶属度列表:从API响应中获取
2. 风险等级:根据风险区间计算
3. 风险得分:从API响应中获取
4. 权重更新:更新子节点的权重显示

4. 算法特点

4.1 核心特性

  1. 模糊数学基础:基于模糊数学理论进行风险评估
  2. 外部API调用:通过外部模糊计算API进行复杂计算
  3. 矩阵运算:使用隶属度矩阵和比较矩阵进行运算
  4. 层级计算:基于树形结构逐层计算风险等级
  5. 智能评估:能够处理不确定性和模糊性

4.2 计算规则

  1. 叶子节点:直接使用隶属度矩阵进行计算
  2. 父节点:基于子节点结果和比较矩阵计算
  3. 矩阵查询:根据错误数量和路径查询对应矩阵
  4. API调用:统一通过外部API进行模糊计算

4.3 优化策略

  1. 节点裁剪:只保留前3层参与计算
  2. 问题映射:避免重复计算同一问题
  3. 树形遍历:高效的树结构操作
  4. API缓存:外部API调用结果缓存

5. 算法复杂度

  • 时间复杂度:O(n + m + k),其中n是节点数量,m是错误记录数量,k是API调用次数
  • 空间复杂度:O(n),主要是树形结构和矩阵数据的存储
  • 外部依赖:依赖外部模糊计算API的性能和可用性

6. 应用场景

  1. 综合体系评估:基于系统模型的模糊风险评估
  2. 设备体系评估:基于设备模型的模糊风险评估
  3. 混合评估:同时进行综合和设备模糊评估
  4. 智能评估:处理不确定性和模糊性的复杂场景

7. 与打分算法的区别

特性 打分算法 模糊算法
计算方式 直接扣分计算 模糊数学计算
外部依赖 无 依赖外部API
矩阵类型 权重矩阵 隶属度矩阵+比较矩阵
结果类型 具体分数 风险等级+风险得分
计算复杂度 较低 较高
适用场景 确定性评估 不确定性评估

8. 注意事项

  1. API依赖:确保外部模糊计算API的可用性
  2. 矩阵配置:隶属度矩阵和比较矩阵需要正确配置
  3. 错误统计:错误数量统计需要准确
  4. 节点裁剪:裁剪规则需要合理设置
  5. 异常处理:API调用失败需要适当的异常处理

9. 总结

模糊算法通过模糊数学理论和外部API调用,实现了更加智能和灵活的风险评估。相比打分算法,模糊算法能够更好地处理不确定性和模糊性,提供风险等级和风险得分的综合评估结果。该算法适用于需要处理复杂、不确定评估场景的建筑工程安全风险评估。