PHP实现可扩展均等分队算法:为带技能的玩家数组划分实力对等两队
可扩展均等分队的实现方案
核心设计逻辑
整个算法不绑定固定的20人输入规模,完全基于通用规则实现,后续不管追加多少玩家,只要满足总参赛人数为偶数的基本前提,算法都能正常输出结果,核心思路是通过蛇形分配+偏差微调,把两队的技能总差压到最低,这套逻辑可以直接平移到任意编程语言。
具体实现步骤(PHP适配)
- 统一玩家数据结构:不管是初始录入的20名玩家,还是后续新增的玩家,都统一为带唯一ID、技能评级的固定结构,追加玩家时仅需往玩家池中append新结构的元素,不需要修改任何算法判断逻辑。
- 前置校验:判断当前有效参赛总人数是否为偶数,不符合规则时直接抛出业务异常;如果需要支持奇数人数场景,可以预留规则接口(比如指定最低技能值玩家为替补,凑齐偶数人参赛),校验逻辑不依赖固定人数阈值。
- 全局排序:把所有有效参赛玩家按技能评级从高到低降序排列,不管玩家池规模多大,排序动作都能直接执行,新加入的玩家会自动参与到排序流程中。
- 蛇形贪心分配:按照排序后的顺序做交叉分配:第1名进A队,第2、3名进B队,第4、5名进A队,第6、7名进B队,循环往复直到所有玩家分配完毕。这种分配方式比直接把排序数组对半切的偏差小很多,能天然避免强队全是高分玩家的问题,且分配规则完全通用,不会因为玩家数量变化失效。
- 分差微调:计算当前两队的总技能值差,如果差超过预设的可接受阈值,就遍历两队中技能最接近的玩家对,通过交换位置把分差压到阈值内,微调逻辑同样适配任意规模的玩家池。
PHP核心代码
// 可扩展玩家池,后续新增玩家直接往数组中追加即可 $playerPool = [ ['id' => 1, 'skill' => 7], ['id' => 2, 'skill' => 9], // 初始20名玩家的录入位置 ]; /** * 均等分队核心方法 * @param array $playerPool 全量有效玩家池 * @param int $maxSkillDiff 允许的两队最大技能分差 * @return array * @throws Exception */ function splitTeams(array $playerPool, int $maxSkillDiff = 2): array { $totalCount = count($playerPool); // 人数校验,不绑定固定人数阈值 if ($totalCount % 2 !== 0) { throw new Exception('参赛总人数必须为偶数才能均分队伍'); } $perTeamSize = $totalCount / 2; // 按技能降序全局排序,自动兼容后续追加的玩家 usort($playerPool, fn($a, $b) => $b['skill'] <=> $a['skill']); // 蛇形分配逻辑 $teamA = $teamB = []; $currentAssignTeam = 'A'; foreach ($playerPool as $index => $player) { // 某队人数凑够阈值就切换到另一队 if (count(${'team' . $currentAssignTeam}) >= $perTeamSize) { $currentAssignTeam = $currentAssignTeam === 'A' ? 'B' : 'A'; } ${'team' . $currentAssignTeam}[] = $player; // 每分配2个玩家就切换下一轮的分配目标队,实现蛇形交叉 if ($index % 2 === 1) { $currentAssignTeam = $currentAssignTeam === 'A' ? 'B' : 'A'; } } // 分差微调逻辑 $sumA = array_sum(array_column($teamA, 'skill')); $sumB = array_sum(array_column($teamB, 'skill')); while (abs($sumA - $sumB) > $maxSkillDiff) { $validSwap = null; // 找能最大幅度缩小分差的玩家交换对 foreach ($teamA as $aKey => $aPlayer) { foreach ($teamB as $bKey => $bPlayer) { $afterSwapDiff = abs( ($sumA - $aPlayer['skill'] + $bPlayer['skill']) - ($sumB - $bPlayer['skill'] + $aPlayer['skill']) ); if ($afterSwapDiff < abs($sumA - $sumB)) { $validSwap = [ 'aKey' => $aKey, 'bKey' => $bKey, 'diff' => $afterSwapDiff ]; } } } if (!$validSwap) break; // 找不到可缩小分差的交换就终止 // 执行玩家交换 [$swapA, $swapB] = [$teamA[$validSwap['aKey']], $teamB[$validSwap['bKey']]]; $teamA[$validSwap['aKey']] = $swapB; $teamB[$validSwap['bKey']] = $swapA; // 重新计算两队总技能 $sumA = $sumA - $swapA['skill'] + $swapB['skill']; $sumB = $sumB - $swapB['skill'] + $swapA['skill']; } return [ 'teamA' => ['members' => $teamA, 'totalSkill' => $sumA], 'teamB' => ['members' => $teamB, 'totalSkill' => $sumB], ]; }
可扩展性说明
- 新增玩家无侵入:只需要把新玩家的信息按统一格式追加到玩家池,重新调用分队方法即可,哪怕总人数从20扩展到几百都不需要修改核心逻辑。
- 语言无关:核心的排序、蛇形分配、分差微调逻辑没有绑定PHP专属语法,平移到其他语言时只需要替换对应的数组操作、排序方法即可。
- 参数可配置:允许的最大分差、队伍人数规则都可以作为方法参数传入,不需要硬编码固定值,适配不同场景的分队要求。
内容的提问来源于stack exchange,提问作者help_me
相关产品推荐
相关产品推荐

