如何基于两两身高比较数据计算所有人员的总体身高排序
解决方案
方案1:有向图拓扑排序(最适合无矛盾比较的全序推导)
这是成对比较排序的标准实现,逻辑简单、效率高,还能自动检测比较结果中的矛盾(比如出现A>B、B>C、C>A的循环关系)。
实现逻辑
- 把每个人员作为图的节点
- 对每一条比较记录生成有向边:
- 若
height == 1,添加边person1 → person2,代表person1身高高于person2 - 若
height == 0,添加边person2 → person1,代表person2身高高于person1
- 若
- 对生成的有向无环图执行拓扑排序,输出的顺序就是身高从高到低的总排序
R代码示例
# 加载依赖包 library(igraph) # 你的示例数据 df <- data.frame(person1 = c('A', 'C', 'B', 'D'), person2 = c('B', 'D', 'C', 'A'), height = c(0, 0, 0, 1)) # 生成边列表 edges <- lapply(1:nrow(df), function(i) { if (df$height[i] == 1) { c(df$person1[i], df$person2[i]) } else { c(df$person2[i], df$person1[i]) } }) edges <- unlist(edges) # 构建有向图 g <- graph(edges = edges, directed = TRUE) # 检查是否有环(有环代表比较数据存在矛盾) if (is_dag(g)) { # 执行拓扑排序 rank_order <- topological.sort(g) final_rank <- V(g)$name[rank_order] cat(paste(final_rank, collapse = ", "), "\n") } else { stop("比较数据存在矛盾的循环关系,请检查原始数据") }
运行上述代码会直接输出你需要的结果:D, C, B, A
注意:如果数据中存在无任何比较关联的人员,拓扑排序会返回多个合法的排序结果,你可以根据业务需求选择保留并列,或者改用Elo评分得到唯一的近似排序。
方案2:Elo评分法(适合存在无法比较的个体、数据量极大的场景)
如果你的数据中存在部分人员没有任何直接/间接的比较关系,无法得到严格全序,或者数据量极大,可以用Elo评分的方式得到近似排序:
- 初始所有人员的Elo得分设为相同值(比如1000)
- 迭代遍历所有比较记录,按照胜负关系更新双方的得分(获胜方加分,失败方减分,双方分差越小加减分幅度越大)
- 迭代到所有人员得分波动小于阈值后停止,按最终得分从高到低排序即可
简单R实现参考
# 初始化得分 all_people <- unique(c(df$person1, df$person2)) elo_score <- rep(1000, length(all_people)) names(elo_score) <- all_people # Elo更新参数 k <- 32 # 调整系数,数值越大得分波动越大 max_iter <- 1000 threshold <- 1e-5 for (iter in 1:max_iter) { old_score <- elo_score for (i in 1:nrow(df)) { p1 <- df$person1[i] p2 <- df$person2[i] # 实际胜者标记 actual_win <- ifelse(df$height[i] == 1, 1, 0) # 计算预期胜率 e_win <- 1 / (1 + 10^((elo_score[p2] - elo_score[p1])/400)) # 更新双方得分 elo_score[p1] <- elo_score[p1] + k * (actual_win - e_win) elo_score[p2] <- elo_score[p2] + k * ((1-actual_win) - (1-e_win)) } # 得分稳定则停止迭代 if (max(abs(elo_score - old_score)) < threshold) break } # 输出排序结果 final_rank <- names(sort(elo_score, decreasing = TRUE)) cat(paste(final_rank, collapse = ", "), "\n")
内容的提问来源于stack exchange,提问作者Hakki
相关产品推荐
相关产品推荐

