打分算法是房地产专家评估系统中的核心评估算法之一,用于对建筑工程安全风险进行量化评估。该算法基于层级体系结构,通过问题扣分和权重计算,最终得出综合评分。
flowchart TD
A[开始评估] --> B[初始化问题映射表]
B --> C[构建评分树]
C --> D[统计错误记录扣分]
D --> E[标记参与评分节点]
E --> F[裁剪无用节点]
F --> G[逐层计算得分]
G --> H[美化显示数据]
H --> I[返回评估结果]
subgraph "初始化阶段"
B1[获取专家权重配置]
B2[构建层级树结构]
B3[初始化ScoreValue节点]
end
subgraph "统计阶段"
D1[遍历错误记录]
D2[计算问题扣分]
D3[累加到对应节点]
D4[处理轮空项]
D5[向上累加错误统计]
end
subgraph "计算阶段"
G1[标记叶子节点]
G2[计算子节点得分]
G3[权重归一化]
G4[计算父节点得分]
G5[递归向上计算]
end
B --> B1
B1 --> B2
B2 --> B3
D --> D1
D1 --> D2
D2 --> D3
D3 --> D4
D4 --> D5
G --> G1
G1 --> G2
G2 --> G3
G3 --> G4
G4 --> G5
var problemMap = initProblemMap(scoreExpert, expertService);
var scoreResult = initSysResult(scoreExpert, expertService);
count(problemMap, scoreResult, records, byPassPaths);
步骤详解:
1. 遍历错误记录:对每个错误记录进行处理
2. 问题验证:检查问题是否在模型中且未被计算过
3. 扣分计算:根据问题的NumberFactor计算应扣除的分数
4. 节点更新:将扣分累加到对应层级的节点上
5. 错误统计:记录错误等级和数量
关键逻辑:java // 计算应扣除的分数 var numberFactor = problem.getNumberFactors().get(errorRecord.getNumberFactorIndex()); var scoreValue = node.getData(); scoreValue.setDeduct(scoreValue.getDeduct() + numberFactor.getDeductPoint());
// 轮空项,如果设置了,则需要参与计算
if(byPassPaths != null){
byPassPaths.forEach(path -> {
var node = scoreResult.get(path);
if(node == null) return;
var scoreValue = node.getData();
scoreValue.setInclude(true);
node.setData(scoreValue);
});
}
TreeHelper.travel(scoreResult.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];
}
}
});
mark(scoreResult);
标记逻辑:
```java
TreeHelper.travel(scoreResult.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);
});
```
scoreResult = prune(scoreResult, leafDepth);
裁剪逻辑:java var root = TreeHelper.pruneTree(scoreResult.root(), (n) -> { return n.getData().isInclude() || n.getDepth() == depth; });
compute(scoreResult, leafDepth);
计算逻辑分为两个层次:
if(node.getDepth() >= (leafDepth - 2)) {
// 最下的两层的计算逻辑不一样,只计算得分,不计算权重
float total = 0;
for(var child : node.getChildren().values()) {
if (!child.getData().isInclude()) continue;
// 计算总扣分
var sv = child.getData();
totalDeduct += Math.min(sv.getTotal(), sv.getDeduct() + sv.getChildrenDeduct());
total += sv.getTotal();
}
var value = node.getData();
if(node.getDepth() != leafDepth)
value.setTotal(total);
value.setChildrenDeduct(totalDeduct);
value.setTotalDeduct(value.getDeduct() + value.getChildrenDeduct());
value.setScore(Math.max(value.getTotal() - value.getTotalDeduct(), 0));
value.setNormalizedWeight(1.0f);
}
特点:
- 直接计算得分,不进行权重归一化
- 扣分不能超过总分
- 最终得分 = 总分 - 总扣分(最小为0)
else {
var total = 0.0f;
// 计算总权重
for (var child : node.getChildren().values()) {
if (!child.getData().isInclude()) continue;
totalWeight += Math.max(child.getData().getWeight(), 1e-3f); // 防止权重为0
}
// 归一化权重,计算总扣分
for (var child : node.getChildren().values()) {
if (!child.getData().isInclude()) continue;
var sv = child.getData();
sv.setNormalizedWeight(Math.max(sv.getWeight(), 1e-3f) / totalWeight);
// 最大扣分不能超过总分
totalDeduct += Math.min(sv.getTotal(), sv.getDeduct() + sv.getChildrenDeduct()) / sv.getTotal() * sv.getNormalizedWeight();
total += sv.getTotal();
}
var value = node.getData();
value.setChildrenDeduct(totalDeduct * value.getTotal());
value.setTotalDeduct(value.getDeduct() + value.getChildrenDeduct());
value.setScore(Math.max(value.getTotal() - value.getTotalDeduct(), 0));
value.setNormalizedWeight(1.0f);
}
特点:
- 进行权重归一化处理
- 按权重比例计算扣分
- 考虑子节点的得分情况
prettyDisplay(scoreResult);
美化逻辑:java TreeHelper.travel(result.root(), (node) -> { var fuzzyValue = node.getData(); // totalErrorCounts = errorCounts + childErrorCounts fuzzyValue.setTotalErrorCounts(Stream.of(0,1,2,3) .map(i -> fuzzyValue.getErrorCounts()[i] + fuzzyValue.getChildErrorCounts()[i]) .mapToInt(Integer::intValue) .toArray()); });