基于ELO的团队匹配算法

Team matchmaking algorithm based on ELO

本文关键字:匹配算法 团队 ELO 基于      更新时间:2023-10-16

我正在寻找一种非常简单的方法,从未指定(但已知)数量的玩家中组建 2 支球队。因此,它实际上不是标准的匹配,因为它只从特定比赛的整个注册玩家池中创建一场比赛。我确实只有一个变量,它是每个玩家的 ELO 分数,这意味着它是计算基础的唯一可用选项。

我想到的只是简单地检查每个可能的球员组合(每支球队 6 名),球队平均 ELO 之间的最小差异是创建的最终名单。我已经测试了这个选项,它为 17 名注册玩家提供了超过 18 英里的计算(通常玩家数量不应超过 24 人),所以它是可行的,但它绝对是最未优化的方式做到这一点。

所以我决定在这里问一个问题,也许你可以以某种方式帮助我。任何想法我可以使用哪些简单的算法,或者在所有可能的组合的直接比较中优化某些东西的方法。

如果你想提供任何代码示例,我可以阅读任何代码语言(几乎),所以这并不重要。

更新:

  • 输入:包含player_ideloplayer的列表[] ,
  • 输出:两个列表:包含玩家对象的team1[]team2[]
  • 目标:两支球队的平均Elo应尽可能接近。

我们可以把它写成一个数学优化问题:

假设我们有球员i=1..24,球队j=1,2。引入决策变量:

x(i,j) = 1 if player i is assigned to team j
0 otherwise

然后我们可以写:

Min |avg(2)-avg(1)|
subject to
sum(j, x(i,j)) <= 1    for all i  (a player can be assigned only once)
sum(i, x(i,j)) = 6     for all j  (a team needs 6 players)
avg(j) = sum(i, rating(i)*x(i,j)) / 6   (calculate the average)
avg(j) >= 0         

我们可以线性化目标中的绝对值:

Min z
subject to
sum(j, x(i,j)) <= 1    for all i
sum(i, x(i,j)) = 6     for all j
avg(j) = sum(i, rating(i)*x(i,j)) / 6
-z <= avg(2)-avg(1) <= z
z >= 0, avg(j) >= 0

现在,这是一个线性混合整数规划问题。MIP 求解器随时可用。

使用一些随机数据,我得到:

----     43 PARAMETER r  ELO rating
player1  1275,    player2  1531,    player3  1585,    player4   668,    player5  1107,    player6  1011
player7  1242,    player8  1774,    player9  1096,    player10 1400,    player11 1036,    player12 1538
player13 1135,    player14 1206,    player15 2153,    player16 1112,    player17  880,    player18  850
player19 1528,    player20 1875,    player21  939,    player22 1684,    player23 1807,    player24 1110

----     43 VARIABLE x.L  assignment
team1       team2
player1        1.000
player2                    1.000
player4        1.000
player5                    1.000
player6                    1.000
player7        1.000
player8        1.000
player9        1.000
player10                   1.000
player11                   1.000
player17       1.000
player18                   1.000

----     43 VARIABLE avg.L  average rating of team
team1 1155.833,    team2 1155.833

----     43 PARAMETER report  solution report
team1       team2
player1     1275.000
player2                 1531.000
player4      668.000
player5                 1107.000
player6                 1011.000
player7     1242.000
player8     1774.000
player9     1096.000
player10                1400.000
player11                1036.000
player17     880.000
player18                 850.000
sum         6935.000    6935.000
avg         1155.833    1155.833

令人惊讶的是,对于这个数据集,我们可以找到一个完美的匹配。

鉴于您的方法无论如何都是近似值,花费太多精力来产生完美的答案是一个失败的原因。 相反,选择一个合理的差异并从那里开始。

我建议你按ELO对玩家列表进行排序,然后将它们配对。 如果这些人被包括在内,他们将在对方的球队中。 如果人数为奇数,请省略与其他人最远的人。 按差异对配对进行排序,并以相同的方式配对。 这给了你相当平均匹配的 4 组,对阵中间 2 的球队将是最好和最差的。 这4组通常应该相对接近偶数。 将其评分为较好的组减去较差的组。 (根据实际分数,任何一半都可以更好。

现在搜索 3 组,每组 4 个,使 A 尽可能接近 B 和 C 的总和。 来自A的较好的组将与来自B和C的较差的组一起。

对于 24 人,这将是一个几乎即时的计算,并将给出合理的结果。

我开始使用的重复差分方法是子集和问题的著名启发式方法。


鉴于这种启发式方法的速度有多快,我认为值得扩大对优秀团队的搜索范围,如下所示。

  1. 对玩家进行排序。 将每个玩家与上下的人配对。 对于n玩家,这将是n-1对。 给每对打一个 ELO 差异的分数,或者更好击败更坏的可能性有多大。 (我会选择哪个取决于两者的玩法。
  2. 对配对进行排序。 将每对与不相交的上方和下方最接近的一对配对。 对于n-1对,这通常会导致n-2组 4 个。
  3. 创建 4 个组的排序列表。 称之为list4. 请注意,此列表的大小为n + O(1)
  4. 构建一个包含所有 8 组的列表,该组可以从 2 组 4 个不相交组成。 排序。 称之为list8. 此列表有多大的公式很复杂,但n^2/2 + O(n),需要时间O(n^2 log(n))排序。
  5. 对于list4的每个元素,list8中找到中最近的元素,这些元素位于其上方/下方,并且与它没有共同的玩家。 对于O(n)元素,这是O(log(n))预期工作。

结果是你摆脱了偶数/奇数逻辑。 是的,您添加了一些额外的努力,但最大的努力是O(n^2 log(n))排序list8. 这足够快,即使你有一百个人,你仍然会得到非常快的答案。

结果将是两支势均力敌的球队,这样当他们按实力配对时,较弱的球队至少有合理的机会发布令人信服的冷门。

以下是MiniZinc中的解决方案:

% Selecting Chess Players
include "globals.mzn";
int: noOfTeams = 2;
int: noOfPlayers = 24;
int: playersPerTeam = 6;
set of int: Players = 1..noOfPlayers;
set of int: Teams = 1..noOfTeams;
array[Players] of int: elo = 
[1275, 1531, 1585,  668, 1107, 1011,
1242, 1774, 1096, 1400, 1036, 1538,
1135, 1206, 2153, 1112,  880,  850,
1528, 1875,  939, 1684, 1807, 1110];
array[Players] of var 0..noOfTeams: team;
array[Teams] of var int: eloSums;
%  same number of players per team
constraint forall(t in Teams) (
playersPerTeam == sum([team[p] == t | p in Players])
);
%  sum up the ELO numbers per team
constraint forall(t in Teams) (
eloSums[t] == sum([if team[p] == t then elo[p] else 0 endif | p in Players])
);
%  enforce sorted sums to break symmetries
%  and avoid minimum/maximum predicates          
constraint forall(t1 in Teams, t2 in Teams where t1 < t2) (
eloSums[t1] <= eloSums[t2]
);
solve minimize eloSums[noOfTeams] - eloSums[1];
output ["n             "] ++ ["team" ++ show(t) ++ "  " | t in Teams] ++
["n"] ++
[ if fix(team[p]) != 0 then
if t == 1 then 
"nplayer" ++ show_int(-2,p) ++ " " 
else 
"" 
endif ++
if fix(team[p]) == t then
show_int(8, elo[p])
else
"       "
endif
else 
"" 
endif 
| p in Players, t in Teams ] ++
["nsum     "] ++
[show_int(8, eloSums[t]) | t in Teams ] ++
["navg        "] ++
[show_float(8,2,eloSums[t]/playersPerTeam) | t in Teams ];

主要决策变量是数组team。它将每个玩家分配到其中一个团队(0 = 没有团队)。为了找到接近的ELO平均值,我汇总了ELO总和,并强制执行所有团队总和的最小值和最大值接近。