Составить список из другого списка - PullRequest
0 голосов
/ 14 ноября 2018

У меня есть список игроков.У каждого игрока есть рыночная стоимость.Мне нужно создать второй список, который перебирает список игроков и создает команду.Хитрость заключается в том, что в новой команде должно быть не менее 15 игроков и максимальная рыночная стоимость 100 млн. +/- 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 голосов
/ 15 ноября 2018

Просто чтобы показать мое решение, если кому-то это нужно.

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);
    }
}
0 голосов
/ 14 ноября 2018

Это на самом деле не вопрос C #, а вопрос об алгоритме, так что для него может быть лучшее место. AIUI, вы хотите выбрать 15 номеров из списка, так что общая сумма составляет 99-101.

Вероятно, существует много решений, все одинаково действительны.

Я думаю, вы могли бы сделать это так:

  1. Составьте список из 14 самых дешевых предметов.
  2. Выберите самое высокое значение, при условии, что оставшееся пространство больше, чем сумма 14 самых дешевых.
  3. Повторите вышеизложенное, пропуская игроков, которые не подходят.
  4. Заполните оставшиеся места игроками из «самого дешевого» списка.

Это, вероятно, даст вам команду с лучшими и худшими игроками и одного игрока среднего ранга, который просто подходит.

Если вы хотите провести дополнительные исследования, это звучит как вариант проблемы с монетой .

...