我有一个球员名单。每个玩家都有一个市场价值。我需要建立第二个列表,该列表遍历玩家列表并建立一个团队。棘手的是,新团队至少应有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;
}`
答案 0 :(得分:0)
这实际上不是C#问题,而是算法问题,因此可能会有更好的解决方法。 AIUI,您想从列表中选择15个数字,这样总数就达到了99-101。
可能有很多解决方案,都同样有效。
我认为您可以这样做:
这可能会给您一个包含最佳和最差球员的团队,以及一个恰到好处的中级球员。
如果您想做更多研究,这听起来像是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);
}
}