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

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

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

我接受的逻辑是这样的:

>找到所有可能的团队组合.
>查找所有团队组合的平均评分&找到最接近该平均水平的球队评分
>从第2步中选择具有平均评分的第一个团队.
>删除所有拥有这些球员的球队.
>重复#2,#3和#4,直到完成所有队伍.

第一步是在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组合会大幅增长,而且我会循环很多次.

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

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

> 00:00:00.0014493
> 00:00:00.0083637
> 00:00:00.0015608
> 00:00:00.0015930

我还忘记了另一个步骤.在步骤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.以下是我做的调整:

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

最佳答案 对于平均值的平均值问题,答案是您不需要字典.

至于算法,最简单的加速就是说评级最高的人必须在某些团队中.因此,只与那个人建立团队,采取最好的.然后再与其他人一起建立团队.并重复一遍.结果将与您当前所做的不同,因为评分最高的人并不总是在最接近平均评分的团队中,但结果并不准确.这将使您当前的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人以下的团队来说是可以接受的.

因此,您构建此数据结构,然后对整个团队进行分数排序,并采用最佳分数.现在向后走数据结构,您可以找到所选的团队.扔掉它然后再做一次.

点赞