如何改进我的团队创建逻辑?

时间:2017-12-04 18:43:19

标签: c# algorithm

我有一天要创建一个程序,当给出带有列的.csv文件时:[Name] | [Elo Rating],将输出另一个平衡团队的.csv文件。用户可以选择每个团队想要的玩家数量。

这可能是我最喜欢的编程项目,但考虑到匆忙的时间框架以及我对如何最好地实现这样一项任务的知识有限,这是非常低效的。它类似于question,但我宁愿不将过程简化为仅根据评级顺序将玩家置于团队中(更快,但不如我现有的那样准确)。您能否就如何在不牺牲准确性的情况下加快运行速度提出建议?

我接受的逻辑是:

  1. 找到所有可能的团队组合。
  2. 查找所有团队组合的平均评分&找到最接近该平均水平的球队评分
  3. 从第2步中选择具有平均评分的第一个团队。
  4. 删除所有拥有这些玩家的球队。
  5. 重复#2,#3和#4,直到完成所有团队。
  6. 第1步由this answer完成并且运作良好。

    第2步可能更像是一个数学问题。我不确定玩家评分的平均值是否等于所有团队的平均评分。我正在使用this答案的略微修改版本,因为我使用字典来保存名称&评分。如果只是平均玩家的评分同样准确,我就不需要这本字典。

    float average = teamScoreDict.Average(s => s.Value);    
    
    var closestScoreToAvg = teamScoreDict.Values.Aggregate((x, y) =>
    Math.Abs(x - average) < Math.Abs(y - average) ? x : y);   
    

    第3步

    static Team FindTeam(float targetScore)
        {
            var selectedTeam = new Team();
    
            //grabbing the first team with the proper score and adding it to final teams (not perfect since there could be multiple teams with the same score)
            for (int i = 0; i < teams.Count; i++)
            {
                if (teams[i].TeamRating == targetScore)
                {
                    selectedTeam = teams[i];
    
                    //add these players to the list of players who have already been selected
                    foreach (var player in teams[i].Player)
                        if (!usedPlayers.Contains(player.Key))
                            usedPlayers.Add(player.Key);
    
                    //remove the score and team then break
                    teamScoreDict.Remove(teams[i].Id);
                    teams.Remove(teams[i]);
                    break;
                }
            }
    
            return selectedTeam;
        }
    

    我认为步骤4是这项任务中最慢的部分。我认为每次迭代都会删除团队会使后续团队搜索更快,但是删除过程很慢。

    static void RemoveTeamsWithUsedPlayers()
        {
            for (int i = teams.Count - 1; i >= 0; i--)
            {
                if (teams[i].Player.Any(kvp => usedPlayers.Contains(kvp.Key)))
                {
                    teamScoreDict.Remove(teams[i].Id);
                    teams.Remove(teams[i]);
                }
            }
        }
    

    结果非常出色(我最后一次将40名球员分成5人队,他们的得分为1300-2200,给了我8支球队,最高和最低球队的总得分只有19分[8498 vs 8517] ])。

    我的问题是,对于规模较大的团队来说,这是非常缓慢的。有3名男子球队的12名球员是即时的。 4人队中的32名球员需要几秒钟。 5人团队中的40名球员需要花费很多分钟,因为可能的K组合会大幅增长,而且我会在他们身上多次循环。

    我希望这个问题不是太宽泛,谢谢你的任何建议。

    编辑:我继续使用秒表类来按照建议获取每个步骤的时间。每队3名(220 k组合)的12名球员。

    1. 00:00:00.0014493
    2. 00:00:00.0083637
    3. 00:00:00.0015608
    4. 00:00:00.0015930
    5. 我还忘记了另一个步骤。在步骤1和步骤2之间,我采用所有可能的团队组合的IEnumerable并将其放入我所做的课程中,并计算团队总分数。此步骤需要00:00:00.0042700

      foreach (var team in teamList)
              {
                  Team teamClass = new Team();
                  teamScore = 0.0F;
      
                  foreach (var player in team)
                  {
                      if (participants.TryGetValue(player, out tempInt))
                      {
                          teamScore += tempInt;
                          teamClass.Player.Add(player, tempInt);
                      }
                  }
      
                  teamClass.TeamRating = teamScore;
                  teamClass.Id = count;
                  teamScoreDict.Add(count, teamScore);
                  teams.Add(teamClass);
                  count++;
              }
      

      编辑2(改进的逻辑):我确定我仍然可以改进这一点,但根据我标记的答案和评论我能够大大提高速度仍保持准确性。拥有12名球员的3人队大约需要同一时间,但是拥有32名球员的4人队从4.5229176s到0.4067160s。以下是我做的调整:

      1. 我只使用球员的平均值。它与所有团队组合的平均值相同
      2. 我删除评分最高的玩家,然后找到比我平常少一个玩家的组合。
      3. 当我将所有这些队伍放入我将要使用的班级时,我同时检查(最接近球队评级+最高球员评分)至(每队平均*球员)
      4. 然后我将最高的玩家添加回该团队并从列表中删除所有玩家
      5. 重复步骤2&amp; 3直到每个人都用光了

1 个答案:

答案 0 :(得分:1)

对于平均值的平均值问题,答案是您不需要词典。

至于算法,最简单的加速是说评级最高的人必须在某些团队中。因此,只与那个人建立团队,采取最好的。然后再与其他人一起建立团队。并重复一遍。结果将与您当前所做的不同,因为评分最高的人并不总是在最接近平均评分的团队中,但结果并不准确。这将使您当前的O(n^m)算法用于构建所有可能的团队列表为O(n^(m-1))算法。

但是对于真正的加速,您需要做的是遵循https://en.wikipedia.org/wiki/Subset_sum_problem#Pseudo-polynomial_time_dynamic_programming_solution中的策略并使用动态编程来消除对分数将会相同的团队的看法。也就是说,到目前为止,您按部分团队中的玩家数量构建数据结构,按总评分,添加最后一个玩家。例如,你第二次发现3名玩家的总得分为3500,你知道它所导致的团队将与你已经做过的事情重复得分,所以你把它扔掉了。

在团队生成过程中丢弃所有重复项将减少从O(n^m)O(n*m*k)找到下一个团队,其中k是最低和最高评级之间的范围。因此,执行n/m次需要时间O(n^2*k)。对于5人以下的团队而言,这应该是可以接受的。

所以你建立了这个数据结构,然后为整个团队排序,并采取最好的。现在向后走数据结构,您可以找到所选的团队。然后扔掉它再做一次。