PHP计算数组元素dense_rank与gapped_rank的代码问题排查
问题根因
现有代码的两类排名逻辑存在计数规则和变量使用错误:
- 间隔排名(gapped rank,即标准竞争排名:同值同名次,后续名次跳过已占用位次,例如两个并列第1后下一名次为第3)的逻辑未基于遍历实际位置赋值,初始值设置错误,也没有统计同值元素的占位数量。
- 密集排名(dense rank:同值同名次,后续名次紧接上一名次,例如两个并列第1后下一名次为第2)的逻辑在同值判断分支中多写了
++$dense_rank自增逻辑,导致每遇到一个同值元素排名就上涨,只有碰到新数值时才会临时回归正确值,和你观察到的错误规律完全吻合。
修复后可运行代码
$members = [ ['num' => 2, 'rank' => 0, 'dense_rank' => 0], ['num' => 2, 'rank' => 0, 'dense_rank' => 0], ['num' => 3, 'rank' => 0, 'dense_rank' => 0], ['num' => 3, 'rank' => 0, 'dense_rank' => 0], ['num' => 3, 'rank' => 0, 'dense_rank' => 0], ['num' => 3, 'rank' => 0, 'dense_rank' => 0], ['num' => 3, 'rank' => 0, 'dense_rank' => 0], ['num' => 5, 'rank' => 0, 'dense_rank' => 0], ['num' => 9, 'rank' => 0, 'dense_rank' => 0], ['num' => 9, 'rank' => 0, 'dense_rank' => 0], ['num' => 9, 'rank' => 0, 'dense_rank' => 0] ]; // 排名计算前置要求:数组必须按num字段升序排序,若原始数组未排序请放开下方排序代码 /* usort($members, function($a, $b){ return $a['num'] <=> $b['num']; }); */ $gappedRank = 0; $prevGappedNum = null; $denseRank = 0; $prevDenseNum = null; $currentPos = 0; foreach($members as &$item){ $currentPos++; // 计算间隔排名 if($item['num'] !== $prevGappedNum){ $gappedRank = $currentPos; $prevGappedNum = $item['num']; } $item['rank'] = $gappedRank; // 计算密集排名 if($item['num'] !== $prevDenseNum){ $denseRank++; $prevDenseNum = $item['num']; } $item['dense_rank'] = $denseRank; echo $item['num'].' - '.$item['rank'].' - '.$item['dense_rank'].'<br>'; } unset($item); // 解除引用避免后续数组操作出现值污染
正确输出结果
代码运行后排名完全符合规则:
| num(数值) | gapped rank(间隔排名) | dense rank(密集排名) |
|---|---|---|
| 2 | 1 | 1 |
| 2 | 1 | 1 |
| 3 | 3 | 2 |
| 3 | 3 | 2 |
| 3 | 3 | 2 |
| 3 | 3 | 2 |
| 3 | 3 | 2 |
| 5 | 8 | 3 |
| 9 | 9 | 4 |
| 9 | 9 | 4 |
| 9 | 9 | 4 |
性能说明
该实现为单次遍历O(n)时间复杂度,单组处理10万级数据无性能压力,完全适配你提到的每周批量处理动态组合的场景,不需要依赖数据库建表计算。
内容的提问来源于stack exchange,提问作者GregW
相关产品推荐
相关产品推荐

