基于ELO

时间:2018-11-02 13:22:47

标签: python c++ algorithm math combinations

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

我想到的只是简单地经历一个球员的所有可能组合(每队6名),而各队平均ELO之间的最小差异就是最终形成的名册。我已经测试了该选项,它为18个注册玩家提供了超过1700万次的计算(通常,玩家人数不应超过24个),因此它可行,但绝对是未经过优化的方式。

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

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

UPD。基本上,输入将是包含玩家“ id”和“ elo”的玩家对象的list [],输出应为2个包含玩家对象的团队列表team1 []和team2 []。两队的平均ELO应该尽可能接近。

3 个答案:

答案 0 :(得分:5)

我们可以将其写为数学优化问题:

假设我们有玩家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

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

答案 1 :(得分:1)

鉴于您的方法无论如何都是近似的,因此付出过多的努力才能得出完美的答案是一个失败的原因。而是选择一个合理的差异并从那里去。

我建议您按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组组成的所有8组的列表。解决。称为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。这样的速度足够快,即使您有一百个人被扔,它仍然会很快给出答案。

结果将是两支均等的球队,这样,当他们通过实力配对时,实力较弱的球队至少有合理的机会令人信服。

答案 2 :(得分:1)

这是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 的总和求和,并强制所有团队和的最小值和最大值接近。