从另一个列表建立列表

时间:2018-11-14 11:59:22

标签: c# algorithm genetic-algorithm best-fit

我有一个球员名单。每个玩家都有一个市场价值。我需要建立第二个列表,该列表遍历玩家列表并建立一个团队。棘手的是,新团队至少应有15名球员,最大市值应为100 Mio +/- 1%。

有人知道如何优雅地做到这一点吗?

    private Result<List<Player>> CreateRandomTeam(List<Player> players, int startTeamValue)
    {

        // start formation  4-4-2
        // Threshold tw 20 mio defender 40 mio Midfielder 40 Mio Striker 50 Mio
        var playerKeeperList = players.FindAll(p => p.PlayerPosition == PlayerPosition.Keeper);
        var playerDefenderList = players.FindAll(p => p.PlayerPosition == PlayerPosition.Defender);
        var playerMidfieldList = players.FindAll(p => p.PlayerPosition == PlayerPosition.Midfield);
        var playerStrikerList = players.FindAll(p => p.PlayerPosition == PlayerPosition.Striker);

        List<Player> keeperPlayers = AddRandomPlayers(playerKeeperList, 2, 0, 20000000);
        List<Player> defenderPlayers = AddRandomPlayers(playerDefenderList, 4, 0, 40000000);
        List<Player> midfieldPlayers = AddRandomPlayers(playerMidfieldList, 4, 0, 40000000);
        List<Player> strikerPlayers = AddRandomPlayers(playerStrikerList, 2, 0, 50000000);


        List<Player> team = new List<Player>();
        team.AddRange(keeperPlayers);
        team.AddRange(defenderPlayers);
        team.AddRange(midfieldPlayers);
        team.AddRange(strikerPlayers);

        var currentTeamValue = team.Sum(s => s.MarketValue);
        var budgetLeft = startTeamValue - currentTeamValue;

        players.RemoveAll(p => team.Contains(p));

        var player1 = AddRandomPlayers(players, 2, 0, budgetLeft);
        team.AddRange(player1);
        players.RemoveAll(p => player1.Contains(p));
        currentTeamValue = team.Sum(t => t.MarketValue);
        budgetLeft = startTeamValue - currentTeamValue;

        var player2 = players.Aggregate((x, y) => Math.Abs(x.MarketValue - budgetLeft) < Math.Abs(y.MarketValue - budgetLeft) ? x : y);

        team.Add(player2);
        players.Remove(player2);

        return Result<List<Player>>.Ok(team);
    }

    private static List<Player> AddRandomPlayers(List<Player> players, int playerCount, double minMarketValue, double threshold)
    {
        // TODO: AYI Implement Random TeamName assign logic
        Random rnd = new Random();
        var team = new List<Player>();
        double assignedTeamValue = 0;

        while (team.Count < playerCount)
        {
            var index = rnd.Next(players.Count);
            var player = players[index];
            if ((assignedTeamValue + player.MarketValue) <= threshold)
            {
                team.Add(player);
                players.RemoveAt(index);
                assignedTeamValue += player.MarketValue;
            }
        }

        return team;
    }`

2 个答案:

答案 0 :(得分:0)

这实际上不是C#问题,而是算法问题,因此可能会有更好的解决方法。 AIUI,您想从列表中选择15个数字,这样总数就达到了99-101。

可能有很多解决方案,都同样有效。

我认为您可以这样做:

  1. 列出最便宜的14种商品。
  2. 只要剩余空间大于14个最便宜的空间的总和,请选择最高的值。
  3. 重复以上步骤,跳过所有不适合的球员。
  4. 用“最便宜”列表中的玩家填充其余位置。

这可能会给您一个包含最佳和最差球员的团队,以及一个恰到好处的中级球员。

如果您想做更多研究,这听起来像是coin change problem的一种变体。

答案 1 :(得分:0)

只是为了显示我的解决方案(如果有人需要)。

var selection = new EliteSelection();
        var crossover = new OnePointCrossover(0);
        var mutation = new UniformMutation(true);
        var fitness = new TeamFitness(players, startTeamValue);
        var chromosome = new TeamChromosome(15, players.Count);
        var population = new Population(players.Count, players.Count, chromosome);

        var ga = new GeneticAlgorithm(population, fitness, selection, crossover, mutation)
        {
            Termination = new GenerationNumberTermination(100)
        };

        ga.Start();

        var bestChromosome = ga.BestChromosome as TeamChromosome;
        var team = new List<Player>();

        if (bestChromosome != null)
        {
            for (int i = 0; i < bestChromosome.Length; i++)
            {
                team.Add(players[(int) bestChromosome.GetGene(i).Value]);
            }

            // Remove assigned player to avoid duplicate assignment
            players.RemoveAll(p => team.Contains(p));

            return Result<List<Player>>.Ok(team);
        }

        return Result<List<Player>>.Error("Chromosome was null!");

有一种适应方法,可以处理逻辑以获得最佳结果。

class TeamFitness : IFitness
{
    private readonly List<Player> _players;
    private readonly int _startTeamValue;
    private List<Player> _selected;

    public TeamFitness(List<Player> players, int startTeamValue)
    {
        _players = players;
        _startTeamValue = startTeamValue;
    }
    public double Evaluate(IChromosome chromosome)
    {
        double f1 = 9;
        _selected = new List<Player>();

        var indexes = new List<int>();
        foreach (var gene in chromosome.GetGenes())
        {
            indexes.Add((int)gene.Value);
            _selected.Add(_players[(int)gene.Value]);
        }

        if (indexes.Distinct().Count() < chromosome.Length)
        {
            return int.MinValue;
        }


        var sumMarketValue = _selected.Sum(s => s.MarketValue);
        var targetValue = _startTeamValue;
        if (sumMarketValue < targetValue)
        {
            f1 = targetValue - sumMarketValue;
        }else if (sumMarketValue > targetValue)
        {
            f1 = sumMarketValue - targetValue;
        }
        else
        {
            f1 = 0;
        }

        var keeperCount = _selected.Count(s => s.PlayerPosition == PlayerPosition.Keeper);
        var strikerCount = _selected.Count(s => s.PlayerPosition == PlayerPosition.Striker);
        var defCount = _selected.Count(s => s.PlayerPosition == PlayerPosition.Defender);
        var middleCount = _selected.Count(s => s.PlayerPosition == PlayerPosition.Midfield);
        var factor = 0;
        var penaltyMoney = 10000000;
        if (keeperCount > 2)
        {
            factor += (keeperCount - 2) * penaltyMoney;
        }

        if (keeperCount == 0)
        {
            factor += penaltyMoney;
        }

        if (strikerCount < 2)
        {
            factor += (2 - strikerCount) * penaltyMoney;
        }

        if (middleCount < 4)
        {
            factor += (4 - middleCount) * penaltyMoney;
        }

        if (defCount < 4)
        {
            factor += (4 - defCount) * penaltyMoney;
        }


        return 1.0 - (f1 + factor);
    }
}